Refactor kfile interface.
authorasterix <asterix@38d2e660-2303-0410-9eaa-f027e97ec537>
Wed, 16 Jan 2008 17:16:20 +0000 (17:16 +0000)
committerasterix <asterix@38d2e660-2303-0410-9eaa-f027e97ec537>
Wed, 16 Jan 2008 17:16:20 +0000 (17:16 +0000)
git-svn-id: https://src.develer.com/svnoss/bertos/trunk@1055 38d2e660-2303-0410-9eaa-f027e97ec537

kern/kfile.c
kern/kfile.h

index 9996c1aa0f6cfd74388b8b78c3a361654691227b..ab8016c32cda080421fd2ba2bb03103a0a50758c 100644 (file)
@@ -31,8 +31,7 @@
  * -->
  *
  * \brief Virtual KFile I/O interface.
- * This module implement a standard fd.seek and a kfile
- * test function.
+ * This module implements some generic I/O interfaces for kfile.
  *
  * \version $Id$
  * \author Francesco Sacchi <batt@develer.com>
  *
  */
 
-#include <kern/kfile.h>
-#include <cfg/debug.h>
 
+#include "kfile.h"
+#include <appconfig.h>
+
+#include <cfg/debug.h>
+#include <mware/formatwr.h>
 #include <string.h>
 
-#include <appconfig.h>
+/*
+ * Sanity check for config parameters required by this module.
+ */
+#if !defined(CONFIG_KFILE_GETS) || ((CONFIG_KFILE_GETS != 0) && CONFIG_KFILE_GETS != 1)
+       #error CONFIG_KFILE_GETS must be set to either 0 or 1 in appconfig.h
+#endif
+#if !defined(CONFIG_PRINTF)
+       #error CONFIG_PRINTF missing in appconfig.h
+#endif
+
+
+/**
+ * Generic putc implementation using \a fd->write.
+ */
+int kfile_putc(int _c, struct KFile *fd)
+{
+       unsigned char c = (unsigned char)_c;
+
+       if (kfile_write(fd, &c, sizeof(c)) == sizeof(c))
+               return (int)((unsigned char)_c);
+       else
+               return EOF;
+}
 
 /**
- * Move \a fd file seek position of \a offset bytes
- * from current position.
- * This is a generic implementation of seek function, you should redefine
- * it in your local module.
+ * Generic getc implementation using \a fd->read.
  */
-kfile_off_t kfile_seek(struct _KFile *fd, kfile_off_t offset, KSeekMode whence)
+int kfile_getc(struct KFile *fd)
+{
+       unsigned char c;
+
+       if (kfile_read(fd, &c, sizeof(c)) == sizeof(c))
+               return (int)((unsigned char)c);
+       else
+               return EOF;
+}
+
+#if CONFIG_PRINTF
+/**
+ * Formatted write.
+ */
+int kfile_printf(struct KFile *fd, const char *format, ...)
+{
+       va_list ap;
+       int len;
+
+       va_start(ap, format);
+       len = _formatted_write(format, (void (*)(char, void *))kfile_putc, fd, ap);
+       va_end(ap);
+
+       return len;
+}
+#endif /* CONFIG_PRINTF */
+
+/**
+ * Write a string to kfile \a fd.
+ * \return 0 if OK, EOF in case of error.
+ */
+int kfile_print(struct KFile *fd, const char *s)
+{
+       while (*s)
+       {
+               if (kfile_putc(*s++, fd) == EOF)
+                       return EOF;
+       }
+       return 0;
+}
+
+#if CONFIG_KFILE_GETS
+/**
+ * Read a line long at most as size and put it
+ * in buf.
+ * \return number of chars read or EOF in case
+ *         of error.
+ */
+int kfile_gets(struct KFile *fd, char *buf, int size)
+{
+       return kfile_gets_echo(fd, buf, size, false);
+}
+
+
+/**
+ * Read a line long at most as size and put it
+ * in buf, with optional echo.
+ *
+ * \return number of chars read, or EOF in case
+ *         of error.
+ */
+int kfile_gets_echo(struct KFile *fd, char *buf, int size, bool echo)
+{
+       int i = 0;
+       int c;
+
+       for (;;)
+       {
+               if ((c = kfile_getc(fd)) == EOF)
+               {
+                       buf[i] = '\0';
+                       return -1;
+               }
+
+               /* FIXME */
+               if (c == '\r' || c == '\n' || i >= size-1)
+               {
+                       buf[i] = '\0';
+                       if (echo)
+                               kfile_print(fd, "\r\n");
+                       break;
+               }
+               buf[i++] = c;
+               if (echo)
+                       kfile_putc(c, fd);
+       }
+
+       return i;
+}
+#endif /* !CONFIG_KFILE_GETS */
+
+
+/**
+ * Move \a fd file seek position of \a offset bytes from \a whence.
+ *
+ * This is a generic implementation of seek function, you can redefine
+ * it in your local module is needed.
+ */
+kfile_off_t kfile_genericSeek(struct KFile *fd, kfile_off_t offset, KSeekMode whence)
 {
        uint32_t seek_pos;
 
-       switch(whence)
+       switch (whence)
        {
 
        case KSM_SEEK_SET:
@@ -71,20 +190,18 @@ kfile_off_t kfile_seek(struct _KFile *fd, kfile_off_t offset, KSeekMode whence)
                break;
        default:
                ASSERT(0);
-               return -1;
+               return EOF;
                break;
-
        }
 
        /* Bound check */
        if (seek_pos + offset > fd->size)
        {
                ASSERT(0);
-               return -1;
+               return EOF;
        }
 
        fd->seek_pos = seek_pos + offset;
-       kprintf("Flash seek to [%lu]\n", fd->seek_pos);
 
        return fd->seek_pos;
 }
@@ -92,8 +209,8 @@ kfile_off_t kfile_seek(struct _KFile *fd, kfile_off_t offset, KSeekMode whence)
 #if CONFIG_TEST
 
 /**
- * Program memory read/write subtest.
- * Try to write/read in the same \a f file location \a _size bytes.
+ * KFile read/write subtest.
+ * Try to write/read in the same \a f file location \a size bytes.
  * \return true if all is ok, false otherwise
  * \note Restore file position at exit (if no error)
  * \note Test buffer \a buf must be filled with
@@ -102,24 +219,31 @@ kfile_off_t kfile_seek(struct _KFile *fd, kfile_off_t offset, KSeekMode whence)
  * buf[i] = i & 0xff
  * </pre>
  */
-static bool kfile_rwTest(KFile *f, uint8_t *buf, size_t _size)
+static bool kfile_rwTest(KFile *f, uint8_t *buf, size_t size)
 {
-       int32_t size = _size;
-
-       // Write test buffer
-       if (f->write(f, buf, size) != size)
+       /*
+        * Write test buffer
+        */
+       if (kfile_write(f, buf, size) != size)
                return false;
-       f->seek(f, -size, KSM_SEEK_CUR);
 
-       // Reset test buffer
+       kfile_seek(f, -(kfile_off_t)size, KSM_SEEK_CUR);
+
+       /*
+        * Reset test buffer
+        */
        memset(buf, 0, size);
 
-       // Read flash in test buffer
-       if (f->read(f, buf, size) != size)
+       /*
+        * Read file in test buffer
+        */
+       if (kfile_read(f, buf, size) != size)
                return false;
-       f->seek(f, -size, KSM_SEEK_CUR);
+       kfile_seek(f, -(kfile_off_t)size, KSM_SEEK_CUR);
 
-       // Check test result
+       /*
+        * Check test result
+        */
        for (size_t i = 0; i < size; i++)
                if (buf[i] != (i & 0xff))
                        return false;
@@ -128,24 +252,19 @@ static bool kfile_rwTest(KFile *f, uint8_t *buf, size_t _size)
 }
 
 /**
- * Test for program memory read/write.
- * This function write and read \p test_buf long \p _size
- * on \p fd handler. If you want not overwrite exist data
- * you should pass an \p save_buf where test store exist data,
- * otherwise su must pass NULL.
+ * KFile read/write test.
+ * This function write and read \a test_buf long \a size
+ * on \a fd handler.
+ * \a save_buf can be NULL or a buffer where to save previous file content.
  */
-bool kfile_test(KFile *fd, uint8_t *test_buf, size_t _size , uint8_t *save_buf, size_t save_buf_size)
+bool kfile_test(KFile *fd, uint8_t *test_buf, uint8_t *save_buf, size_t size)
 {
-       int32_t size = _size;
-
        /*
         * Part of test buf size that you would write.
-        * This var is useded in test 3 to check fd.write
-        * when write outside size limit. Normaly we want
-        * perform a write until is space to write, otherwise
-        * we return.
+        * This var is used in test 3 to check kfile_write
+        * when writing beyond filesize limit.
         */
-       int32_t len = size/2;
+       kfile_off_t len = size / 2;
 
 
        /* Fill test buffer */
@@ -153,32 +272,26 @@ bool kfile_test(KFile *fd, uint8_t *test_buf, size_t _size , uint8_t *save_buf,
                test_buf[i] = (i & 0xff);
 
        /*
-        * Open fd handler
-        */
-       fd->open(fd, NULL, 0);
-       kprintf("Opened fd handler..\n");
-
-       /*
-        * If necessary, user could save content,
+        * If necessary, user can save content,
         * for later restore.
         */
-       if (save_buf != NULL)
+       if (save_buf)
        {
-               fd->read(fd, save_buf, save_buf_size);
-               kprintf("Saved content..form [%lu] to [%lu]\n", fd->seek_pos, fd->seek_pos + save_buf_size);
+               kfile_read(fd, save_buf, size);
+               kprintf("Saved content..form [%lu] to [%lu]\n", fd->seek_pos, fd->seek_pos + size);
        }
 
        /* TEST 1 BEGIN. */
        kprintf("Test 1: write from pos 0 to [%lu]\n", size);
 
        /*
-        * Seek to addr 0
+        * Seek to addr 0.
         */
-       if (fd->seek(fd, 0, KSM_SEEK_SET) != 0)
+       if (kfile_seek(fd, 0, KSM_SEEK_SET) != 0)
                goto kfile_test_end;
 
        /*
-        * Test flash read/write to address 0..size
+        * Test read/write to address 0..size
         */
        if (!kfile_rwTest(fd, test_buf, size))
                goto kfile_test_end;
@@ -186,40 +299,40 @@ bool kfile_test(KFile *fd, uint8_t *test_buf, size_t _size , uint8_t *save_buf,
        kprintf("Test 1: ok!\n");
 
        /*
-        * Restore previous read content
+        * Restore previous read content.
         */
-       if (save_buf != NULL)
+       if (save_buf)
        {
-               fd->seek(fd, 0, KSM_SEEK_SET);
+               kfile_seek(fd, 0, KSM_SEEK_SET);
 
-               if (fd->write(fd, save_buf, save_buf_size) != size)
+               if (kfile_write(fd, save_buf, size) != size)
                        goto kfile_test_end;
 
-               kprintf("Restore content..form [%lu] to [%lu]\n", fd->seek_pos, fd->seek_pos + save_buf_size);
+               kprintf("Restore content..form [%lu] to [%lu]\n", fd->seek_pos, fd->seek_pos + size);
        }
        /* TEST 1 END. */
 
        /* TEST 2 BEGIN. */
-       kprintf("Test 2: write from pos [%lu] to [%lu]\n", fd->size/2 , size);
+       kprintf("Test 2: write from pos [%lu] to [%lu]\n", fd->size/2 , fd->size/2 + size);
 
        /*
         * Go to half test size.
         */
-       fd->seek(fd, (fd->size/ 2), KSM_SEEK_SET);
+       kfile_seek(fd, (fd->size / 2), KSM_SEEK_SET);
 
        /*
-        * If necessary, user could save content,
+        * If necessary, user can save content
         * for later restore.
         */
-       if (save_buf != NULL)
+       if (save_buf)
        {
-               fd->read(fd, save_buf, save_buf_size);
-               fd->seek(fd, -size, KSM_SEEK_CUR);
-               kprintf("Saved content..form [%lu] to [%lu]\n", fd->seek_pos, fd->seek_pos + save_buf_size);
+               kfile_read(fd, save_buf, size);
+               kfile_seek(fd, -(kfile_off_t)size, KSM_SEEK_CUR);
+               kprintf("Saved content..form [%lu] to [%lu]\n", fd->seek_pos, fd->seek_pos + size);
        }
 
        /*
-        * Test flash read/write to address FLASHEND/2 ... FLASHEND/2 + size
+        * Test read/write to address filesize/2 ... filesize/2 + size
         */
        if (!kfile_rwTest(fd, test_buf, size))
                goto kfile_test_end;
@@ -227,57 +340,56 @@ bool kfile_test(KFile *fd, uint8_t *test_buf, size_t _size , uint8_t *save_buf,
        kprintf("Test 2: ok!\n");
 
        /*
-        * Restore previous read content
+        * Restore previous content.
         */
-       if (save_buf != NULL)
+       if (save_buf)
        {
-               fd->seek(fd, -size, KSM_SEEK_CUR);
+               kfile_seek(fd, -(kfile_off_t)size, KSM_SEEK_CUR);
 
-               if (fd->write(fd, save_buf, save_buf_size) != size)
+               if (kfile_write(fd, save_buf, size) != size)
                        goto kfile_test_end;
 
-               kprintf("Restore content..form [%lu] to [%lu]\n", fd->seek_pos, fd->seek_pos + save_buf_size);
+               kprintf("Restore content..form [%lu] to [%lu]\n", fd->seek_pos, fd->seek_pos + size);
        }
 
        /* TEST 2 END. */
 
        /* TEST 3 BEGIN. */
        kprintf("Test 3: write outside of fd->size limit [%lu]\n", fd->size);
+       kprintf("This test should FAIL!, you must see an assertion fail message.\n");
 
        /*
         * Go to the Flash end
         */
-       fd->seek(fd, -len, KSM_SEEK_END);
+       kfile_seek(fd, -len, KSM_SEEK_END);
 
        /*
-        * If necessary, user could save content,
+        * If necessary, user can save content,
         * for later restore.
         */
-       if (save_buf != NULL)
+       if (save_buf)
        {
-               ASSERT(len > save_buf_size);
-
-               fd->read(fd, save_buf, len);
-               fd->seek(fd, -len, KSM_SEEK_CUR);
+               kfile_read(fd, save_buf, len);
+               kfile_seek(fd, -len, KSM_SEEK_CUR);
                kprintf("Saved content..form [%lu] to [%lu]\n", fd->seek_pos, fd->seek_pos + len);
        }
 
        /*
-        * Test flash read/write to address (FLASHEND - size) ... FLASHEND
+        * Test read/write to address (filesize - size) ... filesize
         */
        if (kfile_rwTest(fd, test_buf, size))
                goto kfile_test_end;
 
-       kprintf("Test 3: ok !\n");
+       kprintf("Test 3: ok!\n");
 
        /*
         * Restore previous read content
         */
-       if (save_buf != NULL)
+       if (save_buf)
        {
-               fd->seek(fd, -len, KSM_SEEK_END);
+               kfile_seek(fd, -len, KSM_SEEK_END);
 
-               if (fd->write(fd, save_buf, len) != len)
+               if (kfile_write(fd, save_buf, len) != len)
                        goto kfile_test_end;
 
                kprintf("Restore content..form [%lu] to [%lu]\n", fd->seek_pos, fd->seek_pos + len);
@@ -285,11 +397,11 @@ bool kfile_test(KFile *fd, uint8_t *test_buf, size_t _size , uint8_t *save_buf,
 
        /* TEST 3 END. */
 
-       fd->close(fd);
+       kfile_close(fd);
        return true;
 
 kfile_test_end:
-       fd->close(fd);
+       kfile_close(fd);
        return false;
 }
 
index 3bb0d09fe369088fc12b1d264b45603bcad91bcf..4e529bcff871930804465134f4c934c32cf82d1f 100644 (file)
  * -->
  *
  * \brief Virtual KFile I/O interface.
+ * KFile is a generic interface for file I/O.
+ * Uses an of object oriented model to supply
+ * a generic interface for drivers to communicate.
+ * This module contains only definitions,data structure
+ * and an API.
+ * Each KFile user should implement at least some core functions.
+ * E.G.
+ * If you have a serial driver and want to comply to KFile interface,
+ * you have to declare your context structure:
+ *
+ * \code
+ * typedef struct KFileSerial
+ * {
+ *     KFile fd;
+ *     Serial *ser;
+ * } KFileSerial;
+ * \endcode
+ *
+ * You should also supply a macro for casting KFile to KFileSerial:
+ *
+ * \code
+ * INLINE KFileSerial * KFILESERIAL(KFile *fd)
+ * {
+ *     ASSERT(fd->_type == KFT_SERIAL);
+ *     return (KFileSerial *)fd;
+ * }
+ * \endcode
+ *
+ * Then you can implement as much interface functions as you like
+ * and leave the others to NULL.
+ * ser_close implementation example:
+ *
+ * \code
+ * static int ser_kfile_close(struct KFile *fd)
+ * {
+ *     KFileSerial *fds = KFILESERIAL(fd);
+ *     ser_close(fds->ser);
+ *     return 0;
+ * }
+ * \endcode
+ * KFILESERIAL macro helps to ensure that obj passed is really a Serial.
+ *
+ * KFile interface do not supply the open function: this is done deliberately,
+ * because in embedded systems each device has its own init parameters.
+ * For the same reason specific file settings (like baudrate for Serial, for example)
+ * are demanded to specific driver implementation.
  *
  * \version $Id$
  * \author Bernardo Innocenti <bernie@develer.com>
+ * \author Francesco Sacchi <batt@develer.com>
+ * \author Daniele Basile <asterix@develer.com>
  */
 
-#ifndef MWARE_KFILE_H
-#define MWARE_KFILE_H
+#ifndef KERN_KFILE_H
+#define KERN_KFILE_H
 
 #include <cfg/compiler.h>
+#include <cfg/debug.h>
 
 /* fwd decl */
-struct _KFile;
+struct KFile;
 
-typedef int32_t kfile_off_t;
+typedef int32_t kfile_off_t; ///< KFile offset type, used by kfile_seek function.
 
 /**
  * Costants for repositioning read/write file offset.
@@ -59,39 +108,171 @@ typedef enum KSeekMode
        KSM_SEEK_END, ///< Seek from file end.
 } KSeekMode;
 
+/**
+ * Prototypes for KFile access functions.
+ * I/O file function must be ANSI compliant.
+ * \note A KFile user can choose which function subset to implement,
+ *       but has to set to NULL unimplemented features.
+ * \{
+ */
+
+/**
+ * Read from file.
+ * \return the number of bytes read.
+ */
+typedef size_t (*ReadFunc_t) (struct KFile *fd, void *buf, size_t size);
+
+/**
+ * Write to file.
+ * \return the number of bytes written.
+ */
+typedef size_t (*WriteFunc_t) (struct KFile *fd, const void *buf, size_t size);
+
+/**
+ * Seek into file (if seekable).
+ * \return the new file offset or EOF on errors.
+ */
+typedef kfile_off_t (*SeekFunc_t) (struct KFile *fd, kfile_off_t offset, KSeekMode whence);
+
+/**
+ * Close and reopen file \a fd.
+ * The reopening is done with the former file parameters and access modes.
+ */
+typedef struct KFile * (*ReOpenFunc_t) (struct KFile *fd);
+
+/**
+ * Close file.
+ * \return 0 on success, EOF on errors.
+ */
+typedef int (*CloseFunc_t) (struct KFile *fd);
+
+/**
+ * Flush file I/O.
+ * \return 0 on success, EOF on errors.
+ */
+typedef int (*FlushFunc_t) (struct KFile *fd);
+
+/**
+ * Get file error mask.
+ * \return 0 on success or file error code, device specific.
+ */
+typedef int (*ErrorFunc_t) (struct KFile *fd);
+
+/**
+ * Clear errors.
+ */
+typedef void (*ClearErrFunc_t) (struct KFile *fd);
+/* \} */
 
-typedef size_t (*ReadFunc_t)  (struct _KFile *fd, void *buf, size_t size);
-typedef size_t (*WriteFunc_t) (struct _KFile *fd, const void *buf, size_t size);
-typedef        kfile_off_t (*SeekFunc_t)  (struct _KFile *fd, kfile_off_t offset, KSeekMode whence);
-typedef bool   (*OpenFunc_t)  (struct _KFile *fd, const char *name, int mode);
-typedef bool   (*CloseFunc_t) (struct _KFile *fd);
+/**
+ * KFile type.
+ * Used at runtime and in debug mode only to check
+ * "dynamic casts".
+ * \note Add here new KFile types.
+ */
+typedef enum KFileType
+{
+       KFT_GENERIC, ///< Generic
+       KFT_SERIAL,  ///< Serial driver
+       KFT_CNT
+} KFileType;
 
 /**
  * Context data for callback functions which operate on
  * pseudo files.
+ * \note If you change interface, remember to add corresponding access function.
  */
-typedef struct _KFile
+typedef struct KFile
 {
-       ReadFunc_t              read;
-       WriteFunc_t             write;
-       OpenFunc_t              open;
-       CloseFunc_t             close;
-       SeekFunc_t              seek;
+       ReadFunc_t     read;
+       WriteFunc_t    write;
+       ReOpenFunc_t   reopen;
+       CloseFunc_t    close;
+       SeekFunc_t     seek;
+       FlushFunc_t    flush;
+       ErrorFunc_t    error;
+       ClearErrFunc_t clearerr;
+       DB(KFileType _type); ///< Used to keep trace, at runtime, of obj type.
 
        /* NOTE: these must _NOT_ be size_t on 16bit CPUs! */
-       uint32_t                seek_pos;
-       uint32_t                size;
+       uint32_t seek_pos;
+       uint32_t size;
 } KFile;
 
+/**
+ * Check if \a fd is a generic KFile type.
+ */
+#define KFILE_ASSERT_GENERIC(fd) ASSERT(fd->_type == KFT_GENERIC)
 
-/*
- * Generic implementation of seek function.
+/**
+ * Generic implementation of kfile_seek.
  */
-kfile_off_t kfile_seek(struct _KFile *fd, kfile_off_t offset, KSeekMode whence);
+kfile_off_t kfile_genericSeek(struct KFile *fd, kfile_off_t offset, KSeekMode whence);
+
+int kfile_putc(int c, struct KFile *fd); ///< Generic putc implementation using kfile_write.
+int kfile_getc(struct KFile *fd);  ///< Generic getc implementation using kfile_read.
+int kfile_printf(struct KFile *fd, const char *format, ...);
+int kfile_print(struct KFile *fd, const char *s);
+int kfile_gets(struct KFile *fd, char *buf, int size);
+int kfile_gets_echo(struct KFile *fd, char *buf, int size, bool echo);
 
-/*
- * Kfile test function
+/**
+ * Interface functions for KFile access.
+ * \note Remember to change following functions if KFile interface changes.
+ * \{
+ */
+INLINE size_t kfile_read(struct KFile *fd, void *buf, size_t size)
+{
+       ASSERT(fd->read);
+       return fd->read(fd, buf, size);
+}
+
+INLINE size_t kfile_write(struct KFile *fd, const void *buf, size_t size)
+{
+       ASSERT(fd->write);
+       return fd->write(fd, buf, size);
+}
+
+INLINE KFile * kfile_reopen(struct KFile *fd)
+{
+       ASSERT(fd->reopen);
+       return fd->reopen(fd);
+}
+
+INLINE int kfile_close(struct KFile *fd)
+{
+       ASSERT(fd->close);
+       return fd->close(fd);
+}
+
+INLINE kfile_off_t kfile_seek(struct KFile *fd, kfile_off_t offset, KSeekMode whence)
+{
+       ASSERT(fd->seek);
+       return fd->seek(fd, offset, whence);
+}
+
+INLINE int kfile_flush(struct KFile *fd)
+{
+       ASSERT(fd->flush);
+       return fd->flush(fd);
+}
+
+INLINE int kfile_error(struct KFile *fd)
+{
+       ASSERT(fd->error);
+       return fd->error(fd);
+}
+
+INLINE void kfile_clearerr(struct KFile *fd)
+{
+       ASSERT(fd->clearerr);
+       fd->clearerr(fd);
+}
+/* \} */
+
+/**
+ * Kfile test function.
  */
-bool kfile_test(uint8_t *buf, size_t _size , uint8_t *save_buf, size_t save_buf_size);
+bool kfile_test(KFile *fd, uint8_t *test_buf, uint8_t *save_buf, size_t size);
 
-#endif /* MWARE_KFILE_H */
+#endif /* KERN_KFILE_H */