Janco Kock 9 yıl önce
ebeveyn
işleme
7ccf5ca560
22 değiştirilmiş dosya ile 9864 ekleme ve 0 silme
  1. 2 0
      .gitignore
  2. 32 0
      Makefile
  3. 223 0
      display.c
  4. 2154 0
      fat.c
  5. 399 0
      flash.c
  6. 303 0
      http.c
  7. 1538 0
      inet.c
  8. 326 0
      keyboard.c
  9. 111 0
      led.c
  10. 371 0
      log.c
  11. 324 0
      main.c
  12. 24 0
      make.sh
  13. 347 0
      mmc.c
  14. 758 0
      mmcdrv.c
  15. 151 0
      remcon.c
  16. 471 0
      rtc.c
  17. 550 0
      session.c
  18. 228 0
      spidrv.c
  19. 151 0
      uart0driver.c
  20. 267 0
      util.c
  21. 965 0
      vs10xx.c
  22. 169 0
      watchdog.c

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+*.exe
+*.hex

+ 32 - 0
Makefile

@@ -0,0 +1,32 @@
+PROJ = bin/internetradioA6
+
+
+LIBDIR = nutos/nutbld/lib
+top_srcdir = nutos/nut
+top_blddir = nutos/nutbld
+top_appdir = nutos/nutapp
+
+INCFIRST=$(INCPRE)$(top_blddir)/include
+include $(top_appdir)/NutConf.mk
+include $(top_srcdir)/app/Makedefs.avr-gcc
+
+
+SRCS =  $(wildcard *.c *.c)
+OBJS =  $(SRCS:%.c=%.o)
+LIBS =  $(LIBDIR)/nutinit.o -lnutpro -lnutnet -lnutos -lnutfs -lnutgorp -lnutarch -lnutdev -lnutarch -lnutcrt $(ADDLIBS)
+
+
+all: $(OBJS) $(TARG) $(ITARG) $(DTARG)
+
+include nutos/nutapp/Makerules
+
+clean:
+	-rm -f $(OBJS)
+	-rm -f $(TARG) $(ITARG) $(DTARG)
+	-rm -f $(PROJ).eep
+	-rm -f $(PROJ).obj
+	-rm -f $(PROJ).map
+	-rm -f $(SRCS:.c=.lst)
+	-rm -f $(SRCS:.c=.bak)
+	-rm -f $(SRCS:.c=.i)
+	-rm -f $(SRCS:.c=.d)

+ 223 - 0
display.c

@@ -0,0 +1,223 @@
+/* ========================================================================
+ * [PROJECT]    SIR100
+ * [MODULE]     Display
+ * [TITLE]      display source file
+ * [FILE]       display.c
+ * [VSN]        1.0
+ * [CREATED]    26092003
+ * [LASTCHNGD]  06102006
+ * [COPYRIGHT]  Copyright (C) STREAMIT BV
+ * [PURPOSE]    contains all interface- and low-level routines to
+ *              control the LCD and write characters or strings (menu-items)
+ * ======================================================================== */
+
+#define LOG_MODULE  LOG_DISPLAY_MODULE
+
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/timer.h>
+#include <sys/event.h>
+#include <sys/thread.h>
+#include <sys/heap.h>
+
+#include "system.h"
+#include "portio.h"
+#include "display.h"
+#include "log.h"
+
+/*-------------------------------------------------------------------------*/
+/* local defines                                                           */
+/*-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/* local variable definitions                                              */
+/*-------------------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------------------*/
+/* local routines (prototyping)                                            */
+/*-------------------------------------------------------------------------*/
+static void LcdWriteByte(u_char, u_char);
+static void LcdWriteNibble(u_char, u_char);
+static void LcdWaitBusy(void);
+
+/*!
+ * \addtogroup Display
+ */
+
+/*@{*/
+
+/*-------------------------------------------------------------------------*/
+/*                         start of code                                   */
+/*-------------------------------------------------------------------------*/
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief control backlight
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+void LcdBackLight(u_char Mode)
+{
+    if (Mode==LCD_BACKLIGHT_ON)
+    {
+        sbi(LCD_BL_PORT, LCD_BL_BIT);   // Turn on backlight
+    }
+
+    if (Mode==LCD_BACKLIGHT_OFF)
+    {
+        cbi(LCD_BL_PORT, LCD_BL_BIT);   // Turn off backlight
+    }
+}
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Write a single character on the LCD
+ *
+ * Writes a single character on the LCD on the current cursor position
+ *
+ * \param LcdChar character to write
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+void LcdChar(char MyChar)
+{
+    LcdWriteByte(WRITE_DATA, MyChar);
+}
+
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Low-level initialisation function of the LCD-controller
+ *
+ * Initialise the controller and send the User-Defined Characters to CG-RAM
+ * settings: 4-bit interface, cursor invisible and NOT blinking
+ *           1 line dislay, 10 dots high characters
+ *
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+ void LcdLowLevelInit()
+{
+    u_char i;
+
+    NutDelay(140);                               // wait for more than 140 ms after Vdd rises to 2.7 V
+
+    for (i=0; i<3; ++i)
+    {
+        LcdWriteNibble(WRITE_COMMAND, 0x33);      // function set: 8-bit mode; necessary to guarantee that
+        NutDelay(4);                              // SIR starts up always in 5x10 dot mode
+    }
+
+    LcdWriteNibble(WRITE_COMMAND, 0x22);        // function set: 4-bit mode; necessary because KS0070 doesn't
+    NutDelay(1);                                // accept combined 4-bit mode & 5x10 dot mode programming
+
+    //LcdWriteByte(WRITE_COMMAND, 0x24);        // function set: 4-bit mode, 5x10 dot mode, 1-line
+    LcdWriteByte(WRITE_COMMAND, 0x28);          // function set: 4-bit mode, 5x7 dot mode, 2-lines
+    NutDelay(5);
+
+    LcdWriteByte(WRITE_COMMAND, 0x0C);          // display ON/OFF: display ON, cursor OFF, blink OFF
+    NutDelay(5);
+
+    LcdWriteByte(WRITE_COMMAND, 0x01);          // display clear
+    NutDelay(5);
+
+    LcdWriteByte(WRITE_COMMAND, 0x06);          // entry mode set: increment mode, entire shift OFF
+
+
+    LcdWriteByte(WRITE_COMMAND, 0x80);          // DD-RAM address counter (cursor pos) to '0'
+}
+
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Low-level routine to write a byte to LCD-controller
+ *
+ * Writes one byte to the LCD-controller (by  calling LcdWriteNibble twice)
+ * CtrlState determines if the byte is written to the instruction register
+ * or to the data register.
+ *
+ * \param CtrlState destination: instruction or data
+ * \param LcdByte byte to write
+ *
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+static void LcdWriteByte(u_char CtrlState, u_char LcdByte)
+{
+    LcdWaitBusy();                      // see if the controller is ready to receive next byte
+    LcdWriteNibble(CtrlState, LcdByte & 0xF0);
+    LcdWriteNibble(CtrlState, LcdByte << 4);
+
+}
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Low-level routine to write a nibble to LCD-controller
+ *
+ * Writes a nibble to the LCD-controller (interface is a 4-bit databus, so
+ * only 4 databits can be send at once).
+ * The nibble to write is in the upper 4 bits of LcdNibble
+ *
+ * \param CtrlState destination: instruction or data
+ * \param LcdNibble nibble to write (upper 4 bits in this byte
+ *
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+static void LcdWriteNibble(u_char CtrlState, u_char LcdNibble)
+{
+    outp((inp(LCD_DATA_DDR) & 0x0F) | 0xF0, LCD_DATA_DDR);  // set data-port to output again
+
+    outp((inp(LCD_DATA_PORT) & 0x0F) | (LcdNibble & 0xF0), LCD_DATA_PORT); // prepare databus with nibble to write
+
+    if (CtrlState == WRITE_COMMAND)
+    {
+        cbi(LCD_RS_PORT, LCD_RS);     // command: RS low
+    }
+    else
+    {
+        sbi(LCD_RS_PORT, LCD_RS);     // data: RS high
+    }
+
+    sbi(LCD_EN_PORT, LCD_EN);
+
+    asm("nop\n\tnop");                    // small delay
+
+    cbi(LCD_EN_PORT, LCD_EN);
+    cbi(LCD_RS_PORT, LCD_RS);
+    outp((inp(LCD_DATA_DDR) & 0x0F), LCD_DATA_DDR);           // set upper 4-bits of data-port to input
+    outp((inp(LCD_DATA_PORT) & 0x0F) | 0xF0, LCD_DATA_PORT);  // enable pull-ups in data-port
+}
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Low-level routine to see if the controller is ready to receive
+ *
+ * This routine repeatetly reads the databus and checks if the highest bit (bit 7)
+ * has become '0'. If a '0' is detected on bit 7 the function returns.
+ *
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+static void LcdWaitBusy()
+{
+    u_char Busy = 1;
+	u_char LcdStatus = 0;
+
+    cbi (LCD_RS_PORT, LCD_RS);              // select instruction register
+
+    sbi (LCD_RW_PORT, LCD_RW);              // we are going to read
+
+    while (Busy)
+    {
+        sbi (LCD_EN_PORT, LCD_EN);          // set 'enable' to catch 'Ready'
+
+        asm("nop\n\tnop");                  // small delay
+        LcdStatus =  inp(LCD_IN_PORT);      // LcdStatus is used elsewhere in this module as well
+        Busy = LcdStatus & 0x80;            // break out of while-loop cause we are ready (b7='0')
+    }
+
+    cbi (LCD_EN_PORT, LCD_EN);              // all ctrlpins low
+    cbi (LCD_RS_PORT, LCD_RS);
+    cbi (LCD_RW_PORT, LCD_RW);              // we are going to write
+}
+
+/* ---------- end of module ------------------------------------------------ */
+
+/*@}*/

+ 2154 - 0
fat.c

@@ -0,0 +1,2154 @@
+/****************************************************************************
+*  This file is part of the AVRIDE device driver.
+*
+*  Copyright (c) 2002-2004 by Michael Fischer. All rights reserved.
+*
+*  Redistribution and use in source and binary forms, with or without 
+*  modification, are permitted provided that the following conditions 
+*  are met:
+*  
+*  1. Redistributions of source code must retain the above copyright 
+*     notice, this list of conditions and the following disclaimer.
+*  2. Redistributions in binary form must reproduce the above copyright
+*     notice, this list of conditions and the following disclaimer in the 
+*     documentation and/or other materials provided with the distribution.
+*  3. Neither the name of the author nor the names of its contributors may 
+*     be used to endorse or promote products derived from this software 
+*     without specific prior written permission.
+*
+*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
+*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
+*  THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
+*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 
+*  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
+*  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
+*  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
+*  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+*  SUCH DAMAGE.
+*
+****************************************************************************
+*  History:
+*
+*  14.12.02  mifi   First Version 
+*  23.12.02  mifi   Add FileOpen, FileClose, FileError, FileSize,
+*                   FileSeek and FileRead. But the FileSeek function.
+*                   does not work in the moment, later...
+*  28.12.02  mifi   Now support FAT16 AND FAT32.
+*  01.01.03  mifi   Support long directory entries, but without to 
+*                   check the checksum of the short entry.
+*                   Change FAT32FileSize return value from int to long.
+*                   The max size of a long filename segment is
+*                   (FAT_LONG_NAME_LEN-1). But the complete filename can
+*                   be longer. 
+*
+*                   segment1/segment2/segment3/index.html
+*
+*                   segmentX max length = (FAT_LONG_NAME_LEN-1)
+*
+*  04.01.03  mifi   Take a look at the return values...
+*  18.01.03  mifi   Change Licence from GPL to BSD.
+*  25.01.03  mifi   Implement a new FindFile function.
+*                   I have some trouble with short file names under
+*                   Win98. Win98 store a short name like "enlogo.gif"
+*                   as a long name, nasty OS. 
+*                   Remove FAT32_MAX_FILES and the array aFileHandle,
+*                   a file handle will now be allocated by NutHeapAlloc, 
+*                   therefore we have no restrictions about the count of 
+*                   the open file handle. (Only by available memory)
+*  27.01.03  mifi   Rename all FAT32xxx function to FATxxx.
+*
+*  28.01.03  mifi   Start porting to Nut/OS 3.X.X
+*  19.06.03  mifi   Change the call of IDEInit, now we use the BaseAddress
+*                   of 0. Because the fat module does not need to know the
+*                   address. It will be handled in ide.c.
+*  29.06.03  mifi   First ATAPI-Version
+*                   Now we can read files from a CD-ROM. But there exist
+*                   some open points:
+*                   - only first session from a multisession CD is supported
+*                   - only iso9660, no Joliet support now, later
+*  24.07.04  mifi   Some changes to support more than one drive (datanut)
+*  25.07.04  mifi   Add support for the PCMCIA_11 hardware.
+*  27.07.04  mifi   Start to add functionality for write function.
+*  03.08.04  mifi   Add format function.
+****************************************************************************/
+#define __FAT_C__
+
+#define LOG_MODULE  LOG_FAT_MODULE
+
+#include <string.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <sys/heap.h>
+#include <sys/event.h>
+#include <sys/thread.h>
+
+#include <sys/device.h>
+
+#include "typedefs.h"
+
+#include "fat.h"
+#include "fatdrv.h"
+#include "portio.h"
+#include "log.h"
+
+
+/*==========================================================*/
+/*  DEFINE: All Structures and Common Constants             */
+/*==========================================================*/
+//
+// FAT return codes
+//
+#define FAT_OK                0
+#define FAT_ERROR             -1
+#define FAT_ERROR_EOF         -2
+#define FAT_ERROR_IDE         -3
+
+//
+// Define for correct return values Nut/OS
+//
+#define NUTDEV_OK                       0
+#define NUTDEV_ERROR                    -1
+#define NUTDEV_WRONG_HW                 -2
+
+#define FAT_MAX_DRIVE                   3
+
+//
+// Some defines for the FAT structures
+//
+#define ZIP_DRIVE_BR_SECTOR             32
+
+#define BPB_RsvdSecCnt                  32
+#define BPB_NumFATs                     2
+#define BPB_HiddSec                     63
+
+#define FAT32_MEDIA                     0xf8
+#define FAT32_SECTOR_SIZE               HW_SECTOR_SIZE
+
+#define FAT32_OFFSET_FSINFO             1
+#define FAT32_OFFSET_BACKUP_BOOT        6
+
+#define FAT16_CLUSTER_EOF               0x0000FFFF
+#define FAT16_CLUSTER_ERROR             0x0000FFF7
+#define FAT16_CLUSTER_MASK              0x0000FFFF
+
+#define FAT32_CLUSTER_EOF               0x0FFFFFFF
+#define FAT32_CLUSTER_ERROR             0x0FFFFFF7
+#define FAT32_CLUSTER_MASK              0x0FFFFFFF
+
+#define FAT_SIGNATURE                   0xAA55
+
+#define MBR_SIGNATURE                   FAT_SIGNATURE
+#define MBR_FAT32                       0x0B
+
+#define FSINFO_FIRSTSIGNATURE           0x41615252
+#define FSINFO_FSINFOSIGNATURE          0x61417272
+#define FSINFO_SIGNATURE                FAT_SIGNATURE
+
+#define DIRECTORY_ATTRIBUTE_READ_ONLY   0x01
+#define DIRECTORY_ATTRIBUTE_HIDDEN      0x02
+#define DIRECTORY_ATTRIBUTE_SYSTEM_FILE 0x04
+#define DIRECTORY_ATTRIBUTE_VOLUME_ID   0x08
+#define DIRECTORY_ATTRIBUTE_DIRECTORY   0x10
+#define DIRECTORY_ATTRIBUTE_ARCHIVE     0x20
+
+//
+// DIRECTORY_ATTRIBUTE_READ_ONLY   |
+// DIRECTORY_ATTRIBUTE_HIDDEN      |
+// DIRECTORY_ATTRIBUTE_SYSTEM_FILE |
+// DIRECTORY_ATTRIBUTE_VOLUME_ID
+//
+#define DIRECTORY_ATTRIBUTE_LONG_NAME   0x0F
+
+//
+// DIRECTORY_ATTRIBUTE_READ_ONLY   |
+// DIRECTORY_ATTRIBUTE_HIDDEN      |
+// DIRECTORY_ATTRIBUTE_SYSTEM_FILE |
+// DIRECTORY_ATTRIBUTE_VOLUME_ID   |
+// DIRECTORY_ATTRIBUTE_DIRECTORY   |
+// DIRECTORY_ATTRIBUTE_ARCHIVE
+// 
+#define DIRECTORY_ATTRIBUTE_LONG_NAME_MASK  0x3F
+
+#define FAT_NAME_LEN                    8
+#define FAT_EXT_LEN                     3
+
+//
+// FAT_SHORT_NAME_LEN name len = 
+// name + ext + 1 for the point
+//
+#define FAT_SHORT_NAME_LEN              (FAT_NAME_LEN+FAT_EXT_LEN+1)
+#define FAT_LONG_NAME_LEN               64
+
+
+//
+// Some stuff for HD and CD, DRIVE_INFO Flags
+// 
+//
+#define FLAG_FAT_IS_CDROM               0x0001
+#define FLAG_FAT_IS_ZIP                 0x0002
+
+//
+//  DiskSize to SectorPerCluster table
+//
+typedef struct
+{
+    DWORD DiskSize;
+    BYTE  SecPerClusVal;
+} DSKSZTOSECPERCLUS;
+
+typedef struct _FAT32FileDataTime
+{
+    unsigned Seconds:5;
+    unsigned Minute:6;
+    unsigned Hour:5;
+    unsigned Day:5;
+    unsigned Month:4;
+    unsigned Year:7;
+} FAT32_FILEDATETIME, *PFAT32_FILEDATETIME;
+
+typedef struct _FAT32DirectoryEntry
+{
+    BYTE               Name[FAT_NAME_LEN];
+    BYTE               Extension[FAT_EXT_LEN];
+    BYTE               Attribute;
+    BYTE               Reserved[8];
+    WORD               HighCluster;
+    FAT32_FILEDATETIME Date;
+    WORD               LowCluster;
+    DWORD              FileSize;
+} FAT32_DIRECTORY_ENTRY;
+
+typedef struct _FAT32DirectoryEntryLong
+{
+    BYTE Order;
+    WORD Name1[5];
+    BYTE Attribute;
+    BYTE Type;
+    BYTE Chksum;
+    WORD Name2[6];
+    WORD LowCluster;
+    WORD Name3[2];
+} FAT32_DIRECTORY_ENTRY_LONG;
+
+typedef struct _FAT32FileSystemInformation
+{
+    DWORD FirstSignature;
+    BYTE  Reserved1[480];
+    DWORD FSInfoSignature;
+    DWORD NumberOfFreeClusters;
+    DWORD MostRecentlyAllocatedCluster;
+    BYTE  Reserved2[12];
+    BYTE  Reserved3[2];
+    WORD  Signature;
+} FAT32_FSINFO;
+
+typedef struct _FAT32PartitionEntry
+{
+    BYTE  BootInd;
+    BYTE  FirstHead;
+    BYTE  FirstSector;
+    BYTE  FirstTrack;
+    BYTE  FileSystem;
+    BYTE  LastHead;
+    BYTE  LastSector;
+    BYTE  LastTrack;
+    DWORD StartSectors;
+    DWORD NumSectors;
+} FAT32_PARTITION_ENTRY;
+
+typedef struct _FAT32PartionTable
+{
+    BYTE                  LoadInstruction[446];
+    FAT32_PARTITION_ENTRY Partition[4];
+    WORD                  Signature;               /* AA55 */
+} FAT32_PARTITION_TABLE;
+
+typedef struct _bpbfat16
+{
+    BYTE  DrvNum;
+    BYTE  Reserved1;
+    BYTE  BootSig;
+    DWORD VollID;
+    BYTE  VolLab[11];
+    BYTE  FilSysType[8];
+    BYTE  Reserved2[28];
+} BPBFAT16;
+
+typedef struct _bpbfat32
+{
+    DWORD FATSz32;          // xxx
+    WORD  ExtFlags;         // 0
+    WORD  FSVer;            // must 0
+    DWORD RootClus;         // 
+    WORD  FSInfo;           // typically 1
+    WORD  BkBootSec;        // typically 6
+    BYTE  Reserved[12];     // set all to zero
+    BYTE  DrvNum;           // must 0x80
+    BYTE  Reserved1;        // set all to zero
+    BYTE  BootSig;          // must 0x29 
+    DWORD VollID;           // xxx
+    BYTE  VolLab[11];       // "abcdefghijk"
+    BYTE  FilSysType[8];    // "FAT32   "
+} BPBFAT32;
+
+typedef union _bpboffset36
+{
+    BPBFAT16 FAT16;
+    BPBFAT32 FAT32;
+} BPBOFFSET36;
+
+typedef struct _FAT32BootRecord
+{
+    BYTE        JumpBoot[3];    // 0xeb, 0x58, 0x90
+    BYTE        OEMName[8];     // "MSWIN4.1"
+    WORD        BytsPerSec;     // must 512
+    BYTE        SecPerClus;     // 8 for 4K cluster
+    WORD        RsvdSecCnt;     // typically 32 for FAT32
+    BYTE        NumFATs;        // always 2
+    WORD        RootEntCnt;     // must 0 for FAT32
+    WORD        TotSec16;       // must 0 for FAT32
+    BYTE        Media;          // must 0xf8
+
+    WORD        FATSz16;        // 0   for FAT32
+    WORD        SecPerTrk;      // 63  for FAT32
+    WORD        NumHeads;       // 255 for FAT32
+    DWORD       HiddSec;        // 63  for FAT32
+
+    DWORD       TotSec32;       // xxx
+
+    BPBOFFSET36 Off36;
+
+    BYTE        Reserved[420];
+    WORD        Signature;      // must 0xAA55
+} FAT32_BOOT_RECORD, *PFAT32_BOOT_RECORD;
+
+typedef struct _fat_entry_table16
+{
+    WORD aEntry[256];
+} FAT_ENTRY_TABLE16;
+
+typedef struct _fat_entry_table32
+{
+    DWORD aEntry[128];
+} FAT_ENTRY_TABLE32;
+
+typedef union _fat_dir_table
+{
+    FAT32_DIRECTORY_ENTRY      aShort[16];
+    FAT32_DIRECTORY_ENTRY_LONG aLong[16];
+} FAT_DIR_TABLE;
+
+typedef struct _drive_info
+{
+    BYTE  bIsFAT32;
+    BYTE  bDevice;
+    BYTE  bSectorsPerCluster;
+    BYTE  bFlags;
+
+    WORD  wSectorSize;
+
+    DWORD dwRootDirSectors;
+    DWORD dwFirstRootDirSector;
+
+    DWORD dwRootCluster;
+    DWORD dwFAT1StartSector;
+    DWORD dwFAT2StartSector;
+    DWORD dwCluster2StartSector;
+
+    DWORD dwClusterSize;
+} DRIVE_INFO;
+
+typedef struct _fhandle
+{
+    DWORD      dwFileSize;
+    DWORD      dwStartCluster;
+    DWORD      dwReadCluster;
+    DWORD      dwFilePointer;          /* total file pointer   */
+    DWORD      dwClusterPointer;       /* cluster read pointer */
+
+    int         nLastError;
+    int         nEOF;
+
+    DRIVE_INFO *pDrive;
+} FHANDLE;
+
+static int QuickFormat(NUTDEVICE *dev, DRIVE_INFO *pDrive);
+
+/*==========================================================*/
+/*  DEFINE: Definition of all local Data                    */
+/*==========================================================*/
+static int        nIsInit = FALSE;
+
+static BYTE      *pSectorBuffer = NULL;
+static char      *pLongName1 = NULL;
+static char      *pLongName2 = NULL;
+static DRIVE_INFO sDriveInfo[FAT_MAX_DRIVE];
+
+static HANDLE hFATSemaphore;
+
+static DSKSZTOSECPERCLUS DskTableFAT32[] = {
+    {      66600,  0}, /* disks up to 32.5MB, the 0 value for SecPerClusVal trips an error */
+    {     532480,  1}, /* disks up to 260 MB, 0.5k cluster */
+    {   16777216,  8}, /* disks up to   8 GB,   4k cluster */
+    {   33554432, 16}, /* disks up to  16 GB,   8k cluster */
+    {   67108864, 32}, /* disks up to  32 GB,  16k cluster */
+    { 0xFFFFFFFF, 64}  /* disks greather than 32GB, 32k cluster */
+};
+
+#define DSK_TABLE_FAT32_ENTRY_COUNT (sizeof(DskTableFAT32) / sizeof(DSKSZTOSECPERCLUS))
+/*==========================================================*/
+/*  DEFINE: Definition of all local Procedures              */
+/*==========================================================*/
+
+void FATRelease()
+{
+    nIsInit=FALSE;
+}
+/************************************************************/
+/*  FATLock                                                 */
+/************************************************************/
+void FATLock(void)
+{
+    NutEventWait(&hFATSemaphore, 0);
+}
+
+/************************************************************/
+/*  FATFree                                                 */
+/************************************************************/
+void FATFree(void)
+{
+    NutEventPost(&hFATSemaphore);
+}
+
+/************************************************************/
+/*  FATSemaInit                                             */
+/************************************************************/
+void FATSemaInit(void)
+{
+    NutEventPost(&hFATSemaphore);
+}
+
+/************************************************************/
+/*  GetFirstSectorOfCluster                                 */
+/************************************************************/
+static DWORD GetFirstSectorOfCluster(DRIVE_INFO *pDrive, DWORD dwCluster)
+{
+    DWORD dwSector;
+
+    if (pDrive->bFlags & FLAG_FAT_IS_CDROM)
+    {
+        dwSector  = dwCluster;
+    }
+    else
+    {
+        dwSector  = (dwCluster - 2) * pDrive->bSectorsPerCluster;
+        dwSector += pDrive->dwCluster2StartSector;
+    }
+
+    return(dwSector);
+}
+
+/************************************************************/
+/*  GetNextCluster                                          */
+/************************************************************/
+static DWORD GetNextCluster(DRIVE_INFO *pDrive, DWORD dwCluster)
+{
+    DWORD             dwNextCluster;
+    DWORD             dwSector;
+    DWORD             dwIndex;
+    FAT_ENTRY_TABLE16 *pFatTable16;
+    FAT_ENTRY_TABLE32 *pFatTable32;
+
+    if (pDrive->bFlags & FLAG_FAT_IS_CDROM)
+    {
+        dwNextCluster = dwCluster + 1;
+    }
+    else
+    {
+        if (pDrive->bIsFAT32 == TRUE)
+        {
+            //
+            //  (HW_SECTOR_SIZE / sizeof(long)) == 128
+            // 
+            dwSector = (dwCluster / 128) + pDrive->dwFAT1StartSector;
+            dwIndex  = dwCluster % 128;
+
+            HWReadSectors(pDrive->bDevice, pSectorBuffer, dwSector, 1);
+            pFatTable32 = (FAT_ENTRY_TABLE32 *) pSectorBuffer;
+
+            dwNextCluster = (pFatTable32->aEntry[dwIndex] & FAT32_CLUSTER_MASK);
+            if ((dwNextCluster == FAT32_CLUSTER_EOF) || (dwNextCluster == FAT32_CLUSTER_ERROR))
+            {
+                dwNextCluster = 0;
+            }
+
+        }
+        else
+        {  /* FAT16 */
+            //
+            //  (HW_SECTOR_SIZE / sizeof(word)) == 256
+            // 
+            dwSector = (dwCluster / 256) + pDrive->dwFAT1StartSector;
+            dwIndex  = dwCluster % 256;
+
+            HWReadSectors(pDrive->bDevice, pSectorBuffer, dwSector, 1);
+            pFatTable16 = (FAT_ENTRY_TABLE16 *) pSectorBuffer;
+
+            dwNextCluster = (pFatTable16->aEntry[dwIndex] & FAT16_CLUSTER_MASK);
+            if ((dwNextCluster == FAT16_CLUSTER_EOF) || (dwNextCluster == FAT16_CLUSTER_ERROR))
+            {
+                dwNextCluster = 0;
+            }
+
+        } /* endif pDrive->bIsFAT32 */
+    }
+
+    return(dwNextCluster);
+}
+
+/************************************************************/
+/*  GetLongChar                                             */
+/************************************************************/
+static char GetLongChar(WORD wValue)
+{
+    BYTE Value;
+
+    Value = (BYTE)(wValue & 0x00FF);
+    if (Value == 0xFF)
+    {
+        Value = 0;
+    }
+
+    if (Value != 0)
+    {
+        Value = toupper(Value);
+    }
+
+    return((char)Value);
+}
+
+/************************************************************/
+/*  FindFile                                                */
+/*                                                          */
+/*  Find a file by a given name pLongName.                  */
+/*                                                          */
+/*  It is possible that a SHORT name like "enlogo.gif"      */
+/*  is stored as a LONG name. I have seen this              */
+/*  nasty behaviour by Win98. Therefore I will check        */
+/*  the long name too, even if nIsLongName is FALSE.        */
+/************************************************************/
+static DWORD FindFile(DRIVE_INFO            *pDrive,
+                      FAT32_DIRECTORY_ENTRY *pSearchEntry, 
+                      char                  *pLongName,
+                      DWORD                 dwDirCluster, 
+                      DWORD                 *pFileSize, 
+                      int                    nIsLongName)
+{
+    int                       i, x;
+    BYTE                      bError;
+    int                       nNameLen;
+    int                       nMaxLen;
+    BYTE                      bFound;
+    BYTE                      bEndLoop;
+    BYTE                      bOrder;
+    BYTE                      bMaxOrder;
+    int                       nDirMaxSector;
+    DWORD                     dwSector;
+    DWORD                     dwNewCluster;
+    FAT32_DIRECTORY_ENTRY      *pDirEntryShort;
+    FAT32_DIRECTORY_ENTRY_LONG *pDirEntryLong;
+    char                       *pDirName = 0;
+    FAT_DIR_TABLE              *pDirTable;
+
+    bError       = FALSE;
+    *pFileSize   = 0;
+    dwNewCluster = 0;
+
+    nNameLen  = strlen(pLongName);
+
+    bMaxOrder = (BYTE) ((nNameLen + 12) / 13);
+    nMaxLen   = (int) (bMaxOrder * 13);
+    if (nMaxLen >= (FAT_LONG_NAME_LEN - 1))
+    {
+        bError = TRUE;
+    }
+
+    bOrder = (BYTE) (0x40 | bMaxOrder);
+    if (bOrder == 0xE5)
+    {
+        //
+        // I do not know what should I do if the bOrder is 0xe5.
+        // This is a sign for a "empty" entry. 
+        //
+        bError = TRUE;
+    }
+
+    if (bError == FALSE)
+    {
+        bFound   = FALSE;
+        bEndLoop = FALSE;
+        while ((bEndLoop == FALSE) && (dwDirCluster != 0))
+        {
+            dwSector = GetFirstSectorOfCluster(pDrive, dwDirCluster);
+            nDirMaxSector = (int) pDrive->bSectorsPerCluster;
+
+            //
+            // Test for special case dwDirCluster and FAT16.
+            //
+            if ((dwDirCluster == 1) && (pDrive->bIsFAT32 == FALSE))
+            {
+                dwSector = pDrive->dwFirstRootDirSector;
+                nDirMaxSector = (int) pDrive->dwRootDirSectors;
+            }
+            //
+            // One cluster has SecPerCluster sectors.
+            //
+            for (i = 0; i < nDirMaxSector; i++)
+            {
+                HWReadSectors(pDrive->bDevice, pSectorBuffer, dwSector + i, 1);
+                pDirTable = (FAT_DIR_TABLE *) pSectorBuffer;
+
+                //
+                // And one sector has 16 entries.
+                //
+                // HW_SECTOR_SIZE / sizeof(FAT32_DIRECTORY_ENTRY) = 16
+                //
+                for (x = 0; x < 16; x++)
+                {
+                    if (bFound == TRUE)
+                    {
+                        pDirEntryShort = (FAT32_DIRECTORY_ENTRY *) & pDirTable->aShort[x];
+                        dwNewCluster   = pDirEntryShort->HighCluster;
+                        dwNewCluster   = (dwNewCluster << 16) | (DWORD) pDirEntryShort->LowCluster;
+                        *pFileSize     = pDirEntryShort->FileSize;
+                        bEndLoop       = TRUE;
+                        break;
+                    }
+                    //
+                    // Check for valid entry.
+                    //
+                    pDirEntryShort = (FAT32_DIRECTORY_ENTRY *) & pDirTable->aShort[x];
+                    pDirEntryLong = (FAT32_DIRECTORY_ENTRY_LONG *) & pDirTable->aLong[x];
+
+                    if (nIsLongName == FALSE)
+                    {
+                        //
+                        // Check if it could be a short name. If Win2000 tell us it
+                        // is a short name, it is true. But with Win98 we must
+                        // test both, short and long...
+                        //
+                        if ((pDirEntryShort->Name[0] != 0xE5) && (pDirEntryShort->Name[0] != 0x00))
+                        {
+                            if (memcmp(pDirEntryShort, pSearchEntry, 11) == 0)
+                            {
+                                //
+                                // Check for the correct attribute, this is done with
+                                // the '&' and not with the memcmp.
+                                // With the '&' it is possible to find a hidden archive too :-)
+                                //
+                                if ((pDirEntryShort->Attribute & pSearchEntry->Attribute) ==
+                                    pSearchEntry->Attribute)
+                                {
+
+                                    dwNewCluster = pDirEntryShort->HighCluster;
+                                    dwNewCluster = (dwNewCluster << 16) | (DWORD) pDirEntryShort->LowCluster;
+                                    *pFileSize   = pDirEntryShort->FileSize;
+
+                                    bEndLoop = TRUE;
+                                    break;
+
+                                } /* endif Attribute */
+                            } /* endif test Name+Ext */
+                        } /* endif Name[0] != 0xe5, 0x00 */
+                    }
+                    /*
+                     * endif nIsLongName == FALSE 
+                     */
+                    if ((pDirEntryLong->Attribute == DIRECTORY_ATTRIBUTE_LONG_NAME)
+                        && (pDirEntryLong->Order == bOrder))
+                    {
+                        //
+                        // Next bOrder is bOrder--
+                        //  
+                        if (bOrder & 0x40)
+                        {
+                            bOrder &= ~0x40;
+
+                            //
+                            // Get the space for the name.
+                            //
+                            pDirName = pLongName2;
+
+                            //
+                            // Set the end of string.
+                            //
+                            pDirName[nMaxLen--] = 0;
+                        }
+
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name3[1]);
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name3[0]);
+
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name2[5]);
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name2[4]);
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name2[3]);
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name2[2]);
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name2[1]);
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name2[0]);
+
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name1[4]);
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name1[3]);
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name1[2]);
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name1[1]);
+                        pDirName[nMaxLen--] = GetLongChar(pDirEntryLong->Name1[0]);
+
+                        bOrder--;
+                        if (bOrder == 0)
+                        {
+                            //
+                            // Now compare the name.
+                            //
+                            bOrder = (BYTE) (0x40 | bMaxOrder);
+                            nMaxLen = (int) (bMaxOrder * 13);
+                            if (memcmp(pLongName, pDirName, strlen(pLongName)) == 0)
+                            {
+                                //
+                                // The next entry will be the correct entry.
+                                //
+                                bFound = TRUE;
+                            }
+                        }
+                    }
+                    /*
+                     * pDirEntryLong->Order == bOrder 
+                     */
+                } /* endfor x<16 */
+
+                if (bEndLoop == TRUE)
+                {
+                    break;
+                }
+            }
+
+            if (bEndLoop == FALSE)
+            {
+                //
+                // No file found in this cluster, get the next one.
+                //
+                dwDirCluster = GetNextCluster(pDrive, dwDirCluster);
+            }
+        } /* endwhile */
+
+    }
+    /*
+     * endif bError == FALSE 
+     */
+    return(dwNewCluster);
+}
+
+/************************************************************/
+/*  MountHW                                                 */
+/************************************************************/
+static int MountHW(int nDrive)
+{
+    int                    nError;
+    int                    i;
+    DWORD                 dwSector;
+    DWORD                 dwFATSz;
+    DWORD                 dwRootDirSectors;
+    FAT32_PARTITION_TABLE *pPartitionTable;
+    FAT32_BOOT_RECORD     *pBootRecord;
+    DRIVE_INFO            *pDrive;
+
+    nError   = HW_OK;
+    i        = nDrive;
+    pDrive   = &sDriveInfo[nDrive];
+    dwSector = 0;
+
+    if (pDrive->bFlags & FLAG_FAT_IS_ZIP)
+    {
+        dwSector = ZIP_DRIVE_BR_SECTOR;
+    }
+    else
+    {
+        //
+        // Try to find a PartitionTable.
+        // 
+        nError = HWReadSectors(nDrive, pSectorBuffer, 0, 1);
+        if (nError == HW_OK)
+        {
+            pPartitionTable = (FAT32_PARTITION_TABLE *) pSectorBuffer;
+
+            if (pPartitionTable->Signature == FAT_SIGNATURE)
+            {
+                if (pPartitionTable->Partition[0].NumSectors)
+                {
+                    //
+                    // We found a PartitionTable, read BootRecord.
+                    // 
+                    dwSector = pPartitionTable->Partition[0].StartSectors;
+                }
+            }
+        }
+    }
+
+    if (dwSector != 0)
+    {
+        HWReadSectors(i, pSectorBuffer, dwSector, 1);
+        pBootRecord = (FAT32_BOOT_RECORD *) pSectorBuffer;
+
+        //
+        // Test valid BootRecord.
+        //
+        if (pBootRecord->Signature == FAT_SIGNATURE)
+        {
+            pDrive->bSectorsPerCluster = pBootRecord->SecPerClus;
+
+            if (pBootRecord->FATSz16 != 0)
+            {
+                dwFATSz = pBootRecord->FATSz16;
+                pDrive->bIsFAT32 = FALSE;
+                pDrive->dwRootCluster = 1;  /* special value, see */
+                                            /* FindFile           */
+            }
+            else
+            {
+                dwFATSz               = pBootRecord->Off36.FAT32.FATSz32;
+                pDrive->bIsFAT32      = TRUE;
+                pDrive->dwRootCluster = pBootRecord->Off36.FAT32.RootClus;
+            }
+
+            dwRootDirSectors =
+            ((pBootRecord->RootEntCnt * 32) +
+             (pBootRecord->BytsPerSec - 1)) / pBootRecord->BytsPerSec;
+
+            pDrive->dwFAT1StartSector = pBootRecord->HiddSec + pBootRecord->RsvdSecCnt;
+            pDrive->dwFAT2StartSector = pDrive->dwFAT1StartSector + dwFATSz;
+
+            pDrive->dwCluster2StartSector =
+            pBootRecord->HiddSec + pBootRecord->RsvdSecCnt +
+            (pBootRecord->NumFATs * dwFATSz) + dwRootDirSectors;
+
+            pDrive->dwClusterSize = pBootRecord->SecPerClus * pDrive->wSectorSize;
+
+            pDrive->dwRootDirSectors = dwRootDirSectors;
+            pDrive->dwFirstRootDirSector = pDrive->dwFAT2StartSector + dwFATSz;
+
+        } /* endif pBootRecord->Signature */
+    }
+    /*
+     * endif dwSector != 0 
+     */
+    return(nError);
+}
+
+/************************************************************/
+/*  MountAllDrives                                          */
+/************************************************************/
+static int MountDrive(BYTE bDrive)
+{
+    int nError;
+
+    nError = HW_OK;
+
+    FATLock();
+
+    if (pLongName1 == NULL)
+    {
+        pLongName1 = (char *)NutHeapAlloc(FAT_LONG_NAME_LEN);
+    }
+    if (pLongName2 == NULL)
+    {
+        pLongName2 = (char *)NutHeapAlloc(FAT_LONG_NAME_LEN);
+    }
+    if (pSectorBuffer == NULL)
+    {
+        pSectorBuffer = (BYTE *)NutHeapAlloc(MAX_SECTOR_SIZE);
+    }
+
+    if ((pSectorBuffer != NULL) && (pLongName1 != NULL) && (pLongName2 != NULL))
+    {
+        memset((BYTE *) & sDriveInfo[bDrive], 0x00, sizeof(DRIVE_INFO));
+
+        sDriveInfo[bDrive].bDevice     = bDrive;
+        sDriveInfo[bDrive].wSectorSize = HWGetSectorSize(bDrive);
+
+        if (HWIsCDROMDevice(bDrive) == TRUE)
+        {
+            sDriveInfo[bDrive].bFlags |= FLAG_FAT_IS_CDROM;
+        }
+
+        if (HWIsZIPDevice(bDrive) == TRUE)
+        {
+            sDriveInfo[bDrive].bFlags |= FLAG_FAT_IS_ZIP;
+        }
+
+        switch (sDriveInfo[bDrive].wSectorSize)
+        {
+            case HW_SECTOR_SIZE:{
+                    nError = MountHW(bDrive);
+                    break;
+                }
+            default:{
+                    nError = HW_ERROR;
+                    break;
+                }
+        }
+    }
+    /*
+     * endif pSectorBuffer != NULL 
+     */
+    FATFree();
+
+    return(nError);
+}
+
+/************************************************************/
+/*  MountAllDrives                                          */
+/************************************************************/
+static int MountAllDrives(void)
+{
+    BYTE  i;
+    int  nError = HW_OK;
+
+    for (i = 0; i < FAT_MAX_DRIVE; i++)
+    {
+        MountDrive(i);
+    }
+
+    return(nError);
+}
+
+/*==========================================================*/
+/*  DEFINE: All code exported by the NUTDEVICE              */
+/*==========================================================*/
+/************************************************************/
+/*  FATUnMountDrive                                         */
+/************************************************************/
+static int FATUnMountDrive(int nDrive)
+{
+    int         nError;
+    DRIVE_INFO *pDrive;
+
+    FATLock();
+
+    nError = FAT_OK;
+
+    if ((nDrive >= HW_DRIVE_C) && (nDrive <= HW_DRIVE_D))
+    {
+        pDrive = &sDriveInfo[nDrive];
+        pDrive->bSectorsPerCluster = 0;
+    }
+    else
+    {
+        nError = FAT_ERROR;
+    }
+
+    FATFree();
+
+    return(nError);
+}
+
+/************************************************************/
+/*  CFMount                                                 */
+/************************************************************/
+static void CFMount(int nDrive)
+{
+    BYTE *pSectorBuffer;
+
+    pSectorBuffer = (BYTE *) NutHeapAlloc(MAX_SECTOR_SIZE);
+    if (pSectorBuffer != NULL)
+    {
+        MountDrive(nDrive);
+        NutHeapFree(pSectorBuffer);
+    }
+}
+
+/************************************************************/
+/*  CFUnMount                                               */
+/************************************************************/
+static void CFUnMount(int nDrive)
+{
+    FATUnMountDrive(nDrive);
+}
+
+/************************************************************/
+/*  FATInit                                                 */
+/************************************************************/
+static int FATInit(NUTDEVICE * pDevice)
+{
+    int   nError;
+    int   nHWMode;  
+    BYTE *pSectorBuffer;
+
+    nError        = NUTDEV_ERROR;
+    nHWMode       = 0;
+    pSectorBuffer = NULL;
+
+    if ((nIsInit              == FALSE) && 
+        (pDevice->dev_name[0] == 'F')   &&
+        (pDevice->dev_name[1] == 'A')   &&
+        (pDevice->dev_name[2] == 'T'))
+    {
+        nError = NUTDEV_OK;      
+
+        //
+        // Get the mode.
+        //    
+        switch (pDevice->dev_base)
+        {
+
+#if (FAT_USE_MMC_INTERFACE >= 1)
+            case FAT_MODE_MMC:
+                nHWMode = 0;   
+                break; 
+#endif        
+            default:
+                nError = NUTDEV_ERROR;
+                break;
+        }
+
+        if (nError == NUTDEV_OK)
+        {
+            //
+            // Init my semaphore.
+            //
+            FATSemaInit();
+
+            pSectorBuffer = (BYTE *) NutHeapAlloc(MAX_SECTOR_SIZE);
+            if (pSectorBuffer != NULL)
+            {
+                /*
+                 * HW init
+                 */
+                nError = HWInit(nHWMode, CFMount, CFUnMount);
+                if (nError == HW_OK)
+                {
+                    HWMountAllDevices(nHWMode, pSectorBuffer);
+
+                    /*
+                     * FAT init
+                     */
+                    MountAllDrives();
+
+                    nError  = NUTDEV_OK;
+                    nIsInit = TRUE;    
+                }
+                else
+                {
+                    nError  = NUTDEV_WRONG_HW;
+                }  
+
+                NutHeapFree(pSectorBuffer);
+            } /* endif pSectorBuffer != NULL */
+        } /* endif nError == NUTDEV_OK */
+    }
+    else
+    {
+        if (nIsInit == TRUE)
+        {
+#if (FAT_USE_MMC_INTERFACE >= 1)
+            if ((pDevice->dev_name[0] == 'F')   &&
+                (pDevice->dev_name[1] == 'M')   &&
+                (pDevice->dev_name[2] == '0'))
+            {
+                /*
+                 * Always OK, because the MMC card
+                 * can be inserted later.
+                 */
+                nError = NUTDEV_OK;  
+            }
+#endif /* (FAT_USE_MMC_INTERFACE >= 0) */      
+
+        } /* endif (nIsInit == TRUE) */
+    } /* endif nIsInit == FALSE */
+
+    return(nError);
+}
+
+/************************************************************/
+/*  GetDriveByDevice                                        */
+/************************************************************/
+static DRIVE_INFO* GetDriveByDevice (NUTDEVICE *pDevice)
+{
+    DRIVE_INFO* pDrive = NULL;
+
+    if (pDevice != NULL)
+    {
+        switch (pDevice->dev_name[2])
+        {
+            case 'F':{
+                    pDrive = &sDriveInfo[HW_DRIVE_C];
+                    break;
+                }
+            case '0':{
+                    pDrive = &sDriveInfo[HW_DRIVE_D];
+                    break;
+                }
+            case '1':{
+                    pDrive = &sDriveInfo[HW_DRIVE_E];
+                    break;        
+                }      
+        }
+    }
+
+    return(pDrive);
+}  
+
+/************************************************************/
+/*  FATFileOpen                                             */
+/*                                                          */
+/*  Opens an existing file for reading.                     */
+/*                                                          */
+/*  Parameters: pName points to a string that specifies the */
+/*              name of the file to open. The name must     */
+/*              exactly match the full pathname of the file.*/
+/*                                                          */
+/*  Returns:    A pointer to a FILE structure that can be   */
+/*              used to read the file and retrieve          */
+/*              information about the file.                 */
+/*                                                          */
+/*              A return value of -1 indicates an error.    */
+/************************************************************/
+static NUTFILE *FATFileOpen(NUTDEVICE *pDevice, CONST char *pName, int nMode,
+                            int nAccess)
+{
+    int                    i, x;
+    int                    nError;
+    int                    nEndWhile;
+    DWORD                 dwFileSize;
+    DWORD                 dwCluster;
+    FHANDLE               *hFile;
+    DRIVE_INFO            *pDrive;
+    FAT32_DIRECTORY_ENTRY  sDirEntry;
+    NUTFILE               *hNUTFile;
+    int                    nLongName;
+    char                  *pLongName;
+    char                  *pShortName;
+    char                  *pExtension;
+
+    //
+    // If the user has forgotten to call NUTDeviceOpen,
+    // we must call FATInit.
+    //
+    if (nIsInit == FALSE)
+    {
+        FATInit(pDevice);
+    }
+
+    FATLock();
+
+    pDrive    = NULL;
+    nError    = TRUE;
+    nLongName = FALSE;
+    pLongName = (char *)pLongName1;
+
+    //
+    // hFile is our FAT-Handle.
+    //
+    hFile = NULL;
+
+    //
+    // hNUTFile is the.... correct, NUT handle.
+    //   
+    hNUTFile = (NUTFILE *) NUTDEV_ERROR;
+
+    pDrive = GetDriveByDevice(pDevice);
+
+    if ((pDrive != NULL) && (pDrive->bSectorsPerCluster != 0) && (pName[0] != 0))
+    {
+
+        //
+        // Create a new file handle.
+        //    
+        hFile = (FHANDLE *) NutHeapAlloc(sizeof(FHANDLE));
+
+        if ((pDrive->dwFAT1StartSector) && (hFile != NULL) && (*pName != '.'))
+        {
+
+            memset(hFile, 0x00, sizeof(FHANDLE));
+
+            //
+            // Start by the ROOT dir...
+            //
+            dwCluster = pDrive->dwRootCluster;
+
+            //
+            // If the first char a "/", jump over, e.g. "/index.html"
+            // 
+            //
+            if (*pName == '/')
+            {
+                pName++;
+            }
+
+            nEndWhile = FALSE;
+            while (nEndWhile == FALSE)
+            {  /* master loop */
+
+                nLongName = FALSE;
+
+                //
+                // Get Name
+                //
+                i = 0;
+                while ((*pName != '/') && (*pName != '\\') && (*pName != 0))
+                {
+
+                    if (i >= (FAT_LONG_NAME_LEN - 1))
+                    {
+                        nEndWhile = TRUE;
+                        break;
+                    }
+
+                    pLongName[i] = toupper(*pName);
+
+                    i++;
+                    pName++;
+                } /* endwhile Name */
+
+                if (nEndWhile == FALSE)
+                {
+                    pLongName[i] = 0;
+
+                    //
+                    // Check if it is a Long Directory Entry.
+                    // Yes, I know that 'i' is the length of the string.
+                    // But the code is easier to read with the next strlen.
+                    //
+                    if (strlen(pLongName) <= FAT_SHORT_NAME_LEN)
+                    {
+                        //
+                        // It could be a ShortName, but "abc.defg" is possible
+                        // and this is a long name too. Therfore we need some tests.
+                        //
+                        pExtension = strchr(pLongName, '.');
+                        if (pExtension == NULL)
+                        {
+                            if (strlen(pLongName) > FAT_NAME_LEN)
+                            {
+                                nLongName = TRUE;
+                            }
+                            else
+                            {
+                                nLongName = FALSE;
+                            }
+                        }
+                        else
+                        {
+                            //
+                            // Check the length of the extensions.
+                            //
+                            pExtension++; /* jump over the '.' */
+                            if (strlen(pExtension) > 3)
+                            {
+                                nLongName = TRUE;
+                            }
+                        }
+                    }
+                    else
+                    {  /* Len > FAT_SHORT_NAME_LEN */
+                        //
+                        // Now we have a LongName, sure.
+                        // See the "nasty Win98" in FindFile :-)
+                        //
+                        nLongName = TRUE;
+                    }
+
+                    //
+                    // Here we knows, if we have a LongName or ShortName.
+                    //
+                    if (nLongName == FALSE)
+                    {
+                        //
+                        // ShortName
+                        //
+                        pShortName = pLongName;
+                        memset(&sDirEntry, 0x00, sizeof(FAT32_DIRECTORY_ENTRY));
+                        memset(sDirEntry.Name, 0x20, FAT_NAME_LEN);
+                        memset(sDirEntry.Extension, 0x20, FAT_EXT_LEN);
+
+                        //
+                        // Get the name
+                        //
+                        i = 0;
+                        while ((pShortName[i] != '.') && (pShortName[i] != 0))
+                        {
+                            sDirEntry.Name[i] = pShortName[i];
+                            i++;
+                        }
+                        //
+                        // And the extension
+                        //
+                        if (pShortName[i] == '.')
+                        {
+                            i++;  /* jump over the '.' */
+                            x = 0;
+                            while (pShortName[i] != 0)
+                            {
+                                sDirEntry.Extension[x] = pShortName[i];
+                                i++;
+                                x++;
+                            }
+                        }
+                    }
+                    //
+                    // The file could be a long or short one.
+                    // I have seen that Win98 store the short filename
+                    // in a long one :-(
+                    //
+                    switch (*pName)
+                    {
+                        //
+                        // The file is an ARCHIVE
+                        //
+                        case 0:{
+                                nEndWhile = TRUE;
+                                sDirEntry.Attribute = DIRECTORY_ATTRIBUTE_ARCHIVE;
+
+                                if (pDrive->bFlags & FLAG_FAT_IS_CDROM)
+                                {
+                                    dwCluster = 0;
+                                }
+                                else
+                                {
+                                    dwCluster =
+                                    FindFile(pDrive, &sDirEntry, pLongName, dwCluster, &dwFileSize,
+                                             nLongName);
+                                }
+                                if (dwCluster != 0)
+                                {
+                                    hFile->dwFileSize       = dwFileSize;
+                                    hFile->dwStartCluster   = dwCluster;
+                                    hFile->dwReadCluster    = dwCluster;
+                                    hFile->dwFilePointer    = 0;
+                                    hFile->dwClusterPointer = 0;
+                                    hFile->pDrive           = pDrive;
+                                    hFile->nLastError       = FAT_OK;
+                                    hFile->nEOF             = FALSE;
+
+                                    nError                  = FALSE;
+                                }
+                                break;
+                            } /* endcase 0 */
+
+                            //
+                            // The file is a DIRECTORY
+                            //
+                        case '/':
+                        case '\\':{
+                                pName++;  /* jump over the char */
+
+                                sDirEntry.Attribute = DIRECTORY_ATTRIBUTE_DIRECTORY;
+
+                                if (pDrive->bFlags & FLAG_FAT_IS_CDROM)
+                                {
+                                    dwCluster = 0;
+                                }
+                                else
+                                {
+                                    dwCluster =
+                                    FindFile(pDrive, &sDirEntry, pLongName, dwCluster, &dwFileSize,
+                                             nLongName);
+                                }
+                                if (dwCluster != 0)
+                                {
+
+                                    //
+                                    // The new Cluster is the Cluster of the directory
+                                    //
+
+                                }
+                                else
+                                {
+                                    nEndWhile = TRUE;
+                                }
+                                break;
+                            } /* endcase / \ */
+
+                        default:{
+                                nEndWhile = TRUE;
+                                break;
+                            }
+                    } /* end switch */
+
+                }
+                /*
+                 * endif nEndWhile == FALSE 
+                 */
+            } /* end while */
+
+        }
+        /*
+         * endif pDrive->dwFAT1StartSector 
+         */
+        if (nError == TRUE)
+        {
+            //
+            // We found no file, therefore we can delete our FAT-Handle
+            //
+            if (hFile != NULL)
+            {
+                NutHeapFree(hFile);
+            }
+        }
+        else
+        {
+            //
+            // We have found a FILE and can create a NUT-Handle.
+            //
+            hNUTFile = NutHeapAlloc(sizeof(NUTFILE));
+            if (hNUTFile != NULL)
+            {
+                hNUTFile->nf_next = 0;
+                hNUTFile->nf_dev  = pDevice;
+                hNUTFile->nf_fcb  = hFile;
+            }
+            else
+            {
+                //
+                // Error, no mem for the NUT-Handle, therefore we 
+                // can delete our FAT-Handle too.
+                //
+                NutHeapFree(hFile);
+            }
+        }
+
+    }
+    /*
+     * endif pName[0] != 0 
+     */
+    FATFree();
+
+    return(hNUTFile);
+}
+
+/************************************************************/
+/*  FATFileClose                                            */
+/*                                                          */
+/*  Close a previously opened file.                         */
+/*                                                          */
+/*  Parameters: hNUTFile Identifies the file to close.      */
+/*              This pointer must have been created by      */
+/*              calling FAT32FileOpen().                    */
+/*                                                          */
+/*  Returns:    0 if the function is successfully closed,   */
+/*              -1 otherwise.                               */
+/************************************************************/
+static int FATFileClose(NUTFILE * hNUTFile)
+{
+    int      nError;
+    FHANDLE *hFile;
+
+    nError = NUTDEV_ERROR;
+
+    FATLock();
+
+    if (hNUTFile != NULL)
+    {
+        hFile = (FHANDLE *) hNUTFile->nf_fcb;
+        if (hFile != NULL)
+        {
+            //
+            // Clear our FAT-Handle
+            //
+            NutHeapFree(hFile);
+        }
+        //
+        // Clear the NUT-Handle
+        //
+        NutHeapFree(hNUTFile);
+
+        nError = NUTDEV_OK;
+    }
+
+    FATFree();
+
+    return(nError);
+}
+
+/************************************************************/
+/*  FATFileSize                                             */
+/*                                                          */
+/*  Retrieve the size of a file.                            */
+/*                                                          */
+/*  Parameters: pFile Identifies the file to query.         */
+/*              This pointer must have been created by      */
+/*              calling FAT32FileOpen().                    */
+/*                                                          */
+/*  Returns:    The number of bytes in this file or         */
+/*              -1 if an error occured                      */
+/************************************************************/
+long FATFileSize(NUTFILE * hNUTFile)
+{
+    long     lSize;
+    FHANDLE *hFile;
+
+    FATLock();
+
+    lSize = NUTDEV_ERROR;
+
+    if (hNUTFile != NULL)
+    {
+        hFile = (FHANDLE *) hNUTFile->nf_fcb;
+        if (hFile != NULL)
+        {
+            lSize = hFile->dwFileSize;
+        }
+    }
+
+    FATFree();
+
+    return(lSize);
+}
+
+#if 0
+/************************************************************/
+/*  FATFileSeek                                             */
+/*                                                          */
+/*  Move the file pointer to a new position.                */
+/*  It points to the next byte to be read from a file.      */
+/*  The file pointer is automatically incremented for       */
+/*  each byte read. When the file is opened, it is at       */
+/*  position 0, the beginning of the file.                  */
+/*                                                          */
+/*  Parameters: pFile Identifies the file to seek.          */
+/*              This pointer must have been created by      */
+/*              calling FAT32FileOpen().                    */
+/*                                                          */
+/*              lPos Specifies the new absolute position    */
+/*              of the file pointer.                        */
+/*                                                          */
+/*  Returns:    0 if the function is successful,            */
+/*              -1 otherwise.                               */
+/************************************************************/
+int FATFileSeek(FILE * pFile, long lPos)
+{
+    int      nError = NUTDEV_ERROR;
+    FHANDLE *hFile;
+
+    FATLock();
+
+    hFile = (FHANDLE *) pFile;
+
+    //
+    // We must do some work here...
+    //
+    nError = FAT32_ERROR;
+
+    FATFree();
+
+    return(nError);
+}
+#endif
+
+/************************************************************/
+/*  FATFileRead                                             */
+/*                                                          */
+/*  Read data from a file.                                  */
+/*                                                          */
+/*  Parameters: pFile Identifies the file to read from.     */
+/*              This pointer must have been created by      */
+/*              calling FAT32FileOpen().                    */
+/*                                                          */
+/*              pData Points to the buffer that receives    */
+/*              the data.                                   */
+/*                                                          */
+/*              nSize Specifies the number of bytes to      */
+/*              read from the file.                         */
+/*                                                          */
+/*  Returns:    The number of bytes read from the file or   */
+/*               -1 if an error occured.                    */
+/************************************************************/
+int FATFileRead(NUTFILE * hNUTFile, void *pData, int nSize)
+{
+    int         nError;
+    int         nBytesRead;
+    int         nBytesToRead;
+    FHANDLE    *hFile;
+    DRIVE_INFO *pDrive;
+    BYTE       *pByte;
+    DWORD      dwReadSector;
+    DWORD      dwSector;
+    int         nSectorCount;
+    int         nSectorOffset;
+    WORD        wSectorSize;
+
+    nBytesRead = 0;
+
+    FATLock();
+
+    hFile  = NULL;
+    nError = NUTDEV_ERROR;
+
+    if (hNUTFile != NULL)
+    {
+        hFile = (FHANDLE *) hNUTFile->nf_fcb;
+    }
+
+    if ((hFile != NULL) && (nSize != 0))
+    {
+        if (hFile->dwFilePointer < hFile->dwFileSize)
+        {
+
+            if ((hFile->dwFilePointer + nSize) > hFile->dwFileSize)
+            {
+                nSize = (int) (hFile->dwFileSize - hFile->dwFilePointer);
+            }
+
+            pDrive = (DRIVE_INFO *) hFile->pDrive;
+            pByte  = (BYTE *) pData;
+
+            nBytesRead  = nSize;
+            wSectorSize = pDrive->wSectorSize;
+
+            while (nSize)
+            {
+                dwSector = GetFirstSectorOfCluster(pDrive, hFile->dwReadCluster);
+                nSectorCount = hFile->dwClusterPointer / wSectorSize;
+                nSectorOffset = hFile->dwClusterPointer % wSectorSize;
+
+                //
+                // (Sector + SectorCount) is the sector to read
+                // SectorOffset is the start position in the sector itself
+                //
+                dwReadSector = dwSector + nSectorCount;
+
+                nError = HWReadSectors(pDrive->bDevice, pSectorBuffer, dwReadSector, 1);
+                if (nError == HW_OK)
+                {
+                    //
+                    // Find the size we can read from ONE sector
+                    //
+                    if (nSize > (int) wSectorSize)
+                    {
+                        nBytesToRead = wSectorSize;
+                    }
+                    else
+                    {
+                        nBytesToRead = nSize;
+                    }
+
+                    //
+                    // Test inside a sector
+                    //
+                    if ((nSectorOffset + nBytesToRead) > (int) wSectorSize)
+                    {
+                        nBytesToRead = wSectorSize - nSectorOffset;
+                    }
+
+                    memcpy(pByte, &pSectorBuffer[nSectorOffset], nBytesToRead);
+                    pByte += nBytesToRead;
+
+                    hFile->dwFilePointer    += nBytesToRead;
+                    hFile->dwClusterPointer += nBytesToRead;
+
+                    //
+                    // Check for EOF
+                    if (hFile->dwFilePointer >= hFile->dwFileSize)
+                    {
+                        hFile->nEOF = TRUE;
+                    }
+
+                    if (hFile->dwClusterPointer >= pDrive->dwClusterSize)
+                    {
+                        //
+                        // We must switch to the next cluster
+                        //
+                        hFile->dwReadCluster = GetNextCluster(pDrive, hFile->dwReadCluster);
+                        hFile->dwClusterPointer = 0;
+                    }
+
+                    nSize -= nBytesToRead;
+
+                }
+                else
+                {  /* HWReadSectors Error */
+
+                    nBytesRead = 0;
+                    hFile->nLastError = FAT_ERROR_IDE;
+                    break;
+                } /* endif nError == HW_OK */
+
+            } /* endwhile */
+
+        }
+        else
+        {  /* reached the EOF */
+            hFile->nLastError = FAT_ERROR_EOF;
+        } /* endif hFile->dwFilePointer < hFile->dwFileSize */
+
+    }
+    /*
+     * endif (hFile != NULL) && (nSize != 0) 
+     */
+    FATFree();
+
+    return(nBytesRead);
+}
+
+/************************************************************/
+/*  FATFileWrite                                            */
+/*                                                          */
+/*  Write data to a file.                                   */
+/*                                                          */
+/*  Parameters: pFile Identifies the file to write to.      */
+/*              This pointer must have been created by      */
+/*              calling FAT32FileOpen().                    */
+/*                                                          */
+/*              pData Points to the buffer that holds       */
+/*              the data.                                   */
+/*                                                          */
+/*              nSize Specifies the number of bytes to      */
+/*              write to the file.                          */
+/*                                                          */
+/*  Returns:    The number of bytes written to the file or  */
+/*               -1 if an error occured.                    */
+/************************************************************/
+static int FATFileWrite(NUTFILE * hNUTFile, CONST void *pData, int nSize)
+{
+    int nError;
+
+    nError = NUTDEV_ERROR;
+
+    return(nError);
+}
+
+#ifdef __HARVARD_ARCH__
+static int FATFileWriteP(NUTFILE * hNUTFile, PGM_P pData, int nSize)
+{
+    int nError;
+
+    nError = NUTDEV_ERROR;
+
+    return(nError);
+}
+#endif
+
+/************************************************************/
+/*  FATIOCtl                                                */
+/*                                                          */
+/*  Perform IOCTL control functions.                        */
+/*                                                          */
+/*  reg May be set to one of the following constants:       */
+/*                                                          */
+/*  Parameters: dev  Identifies the device to use.          */
+/*              reg  Requested control function.            */
+/*              conf Points to a buffer that contains any   */
+/*                   data required for the given control    */
+/*                   function or receives data from that    */
+/*                   function.                              */
+/*  Returns:    0 on success, or -1 if an error occured.    */
+/************************************************************/
+int FATIOCtl(NUTDEVICE *dev, int req, void *conf)
+{
+    int         nError = NUTDEV_ERROR;
+    DRIVE_INFO *pDrive = GetDriveByDevice(dev);
+
+    if (pDrive != NULL)
+    {
+        switch (req)
+        {
+
+#if (FAT_SUPPORT_FORMAT >= 1)    
+            case FAT_IOCTL_QUICK_FORMAT: {
+                    nError = QuickFormat(dev, pDrive);
+                    break;
+                }
+#endif
+
+            default: {
+                    nError = NUTDEV_ERROR;
+                    break; 
+                }
+
+        }  
+    }
+
+    return(nError);
+}
+
+//
+// FAT Device information structure.
+// Is mapped to the FAT-Device.
+// The user MUST first register the FAT hardware.
+//
+NUTDEVICE devFAT = {
+    0,              /* Pointer to next device. */
+
+    /*
+     * Unique device name.     
+     */
+    {'F', 'A', 'T', 0, 0, 0, 0, 0, 0},
+
+    IFTYP_STREAM,   /* Type of device.                   */
+    2,              /* Base address.                     */
+    0,              /* First interrupt number.           */
+    0,              /* Interface control block.          */
+    0,              /* Driver control block.             */
+    FATInit,        /* Driver initialization routine.    */
+    0,              /* Driver specific control function. */
+    0,              /* Driver specific read function.    */
+    0,              /* Driver specific write function.   */
+
+#ifdef __HARVARD_ARCH__
+    0,              /* Driver specific write_p function. */
+#endif
+
+    0,              /* Driver specific open function.    */
+    0,              /* Driver specific close function.   */
+    0
+};
+
+#if (FAT_USE_MMC_INTERFACE >= 1)
+//
+// FATUSB Device information structure.
+// Is mapped to the first USB drive
+//
+NUTDEVICE devFATMMC0 = {
+    0,              /* Pointer to next device. */
+
+    /*
+     * Unique device name.     
+     */
+    {'F', 'M', '0', 0, 0, 0, 0, 0, 0},
+
+    IFTYP_STREAM,   /* Type of device.                   */
+    2,              /* Base address.                     */
+    0,              /* First interrupt number.           */
+    0,              /* Interface control block.          */
+    0,              /* Driver control block.             */
+    FATInit,        /* Driver initialization routine.    */
+    FATIOCtl,       /* Driver specific control function. */
+    FATFileRead,    /* Driver specific read function.    */
+    FATFileWrite,   /* Driver specific write function.   */
+
+#ifdef __HARVARD_ARCH__
+    FATFileWriteP,  /* Driver specific write_p function. */
+#endif
+
+    FATFileOpen,    /* Driver specific open function.    */
+    FATFileClose,   /* Driver specific close function.   */
+    FATFileSize
+};
+#endif /* (FAT_USE_MMC_INTERFACE >= 1) */
+
+
+#if (FAT_SUPPORT_FORMAT >= 1)
+/************************************************************/
+/*  GetClusterSize                                          */
+/************************************************************/
+static BYTE GetClusterSize (DWORD dTotalSectors)
+{
+    BYTE bIndex;
+    BYTE bSecPerClus = 0;
+
+    for (bIndex=0; bIndex<DSK_TABLE_FAT32_ENTRY_COUNT; bIndex++)
+    {
+        if (dTotalSectors <= DskTableFAT32[bIndex].DiskSize)
+        {
+            bSecPerClus = DskTableFAT32[bIndex].SecPerClusVal;
+            break;      
+        }
+    }
+
+    return(bSecPerClus);
+}
+
+/************************************************************/
+/*  QuickFormat                                             */
+/************************************************************/
+static int QuickFormat(NUTDEVICE *dev, DRIVE_INFO *pDrive)
+{
+    DWORD                   i;
+    int                    nError = NUTDEV_ERROR;
+    BYTE                  *pBuffer;
+    BYTE                   bDevice;
+    BYTE                   bSecPerClus;  
+    DWORD                  dSector;
+    DWORD                  dFirstDataSector;
+    DWORD                  dLastDataSector;
+    DWORD                  dMaxClusterCount;
+    DWORD                  dFAT1StartSector=0;
+    DWORD                  dFAT2StartSector=0;
+    DWORD                  dCluster2StartSector;
+    DWORD                  dClearCount;
+    DWORD                  dTotalSectors;
+    DWORD                  dPossibleClusterCount;
+    DWORD                  dFATSz32;
+    FAT32_PARTITION_TABLE *pPartitionTable;  
+    FAT32_BOOT_RECORD     *pBootRecord;
+    FAT32_FSINFO          *pFSInfo;
+    FAT32_DIRECTORY_ENTRY *pDirEntry;
+    LONG                  *pLong;
+    DWORD                  dVollID;
+
+    FATLock();
+
+    /*
+     * Get the sector buffer
+     */  
+    pBuffer = (u_char *)NutHeapAlloc(pDrive->wSectorSize);
+    if (pBuffer != NULL)
+    {
+
+        bDevice        = pDrive->bDevice;    
+        dTotalSectors  = HWGetTotalSectors(bDevice);
+        dTotalSectors -= BPB_RsvdSecCnt;
+
+        bSecPerClus = GetClusterSize(dTotalSectors);     
+        if (bSecPerClus == 0)
+        {
+            /*
+             * Sorry, to small for FAT32,
+             * the lib will support FAT16 later....
+             */
+            NutHeapFree(pBuffer);  
+            return(NUTDEV_ERROR);    
+        }
+
+        dPossibleClusterCount = (dTotalSectors / (LONG)bSecPerClus);
+
+        dFATSz32              = (dPossibleClusterCount * sizeof(long) + 
+                                 (FAT32_SECTOR_SIZE -1) ) / FAT32_SECTOR_SIZE; 
+
+        /*
+         * First we will clear the sectors which we will use
+         * for the MBR, BPB and the FA's.
+         */
+        dClearCount  = BPB_HiddSec + BPB_RsvdSecCnt;
+        dClearCount += (BPB_NumFATs * dFATSz32) + bSecPerClus;
+
+        memset(pBuffer, 0x00, pDrive->wSectorSize);        
+        for (i=0; i<dClearCount; i++)
+        {
+            nError = HWWriteSectors(bDevice, pBuffer, i, 1);    
+            if (nError != HW_OK)
+            {
+                break;
+            }
+        }
+
+        /*
+         * Create the Volume serial number,
+         * we have no random value, therefore
+         * use the dTotalSectors and time.
+         */
+        dVollID = (DWORD)time(NULL) * dTotalSectors;
+
+        /*
+         * Create MBR (MasterBootRecord)
+         */
+        memset(pBuffer, 0x00, pDrive->wSectorSize);
+        pPartitionTable = (FAT32_PARTITION_TABLE *)pBuffer;
+
+        pPartitionTable->Partition[0].BootInd      = 0x00;
+        pPartitionTable->Partition[0].FirstHead    = 1;
+        pPartitionTable->Partition[0].FirstSector  = 1;
+        pPartitionTable->Partition[0].FirstTrack   = 0;
+        pPartitionTable->Partition[0].FileSystem   = MBR_FAT32;
+        pPartitionTable->Partition[0].LastHead     = 0;
+        pPartitionTable->Partition[0].LastSector   = 0;
+        pPartitionTable->Partition[0].StartSectors = BPB_HiddSec;
+        pPartitionTable->Partition[0].NumSectors   = dTotalSectors;
+        pPartitionTable->Signature                 = MBR_SIGNATURE;
+
+        /*
+         * Write the MBR to the drive
+         */
+        nError = HWWriteSectors(bDevice, pBuffer, 0, 1);    
+        if (nError == HW_OK)
+        {
+            /*
+             * Create the BPB
+             */
+            memset(pBuffer, 0x00, pDrive->wSectorSize);
+            pBootRecord = (FAT32_BOOT_RECORD *)pBuffer;
+
+            pBootRecord->JumpBoot[0] = 0xEB;
+            pBootRecord->JumpBoot[1] = 0x58;
+            pBootRecord->JumpBoot[2] = 0x90;
+
+            pBootRecord->OEMName[0]  = 'M';
+            pBootRecord->OEMName[1]  = 'S';
+            pBootRecord->OEMName[2]  = 'D';
+            pBootRecord->OEMName[3]  = 'O';
+            pBootRecord->OEMName[4]  = 'S';
+            pBootRecord->OEMName[5]  = '5';
+            pBootRecord->OEMName[6]  = '.';
+            pBootRecord->OEMName[7]  = '0';
+
+            pBootRecord->BytsPerSec  = FAT32_SECTOR_SIZE;
+            pBootRecord->SecPerClus  = bSecPerClus;
+            pBootRecord->RsvdSecCnt  = BPB_RsvdSecCnt;
+            pBootRecord->NumFATs     = BPB_NumFATs;
+            pBootRecord->RootEntCnt  = 0;
+            pBootRecord->TotSec16    = 0;
+            pBootRecord->Media       = FAT32_MEDIA;      
+
+            pBootRecord->FATSz16     = 0;
+            pBootRecord->SecPerTrk   = 63;
+            pBootRecord->NumHeads    = 255;
+
+            pBootRecord->HiddSec     = BPB_HiddSec;
+            pBootRecord->TotSec32    = dTotalSectors;
+
+            pBootRecord->Off36.FAT32.FATSz32       = dFATSz32;
+            pBootRecord->Off36.FAT32.ExtFlags      = 0;
+            pBootRecord->Off36.FAT32.FSVer         = 0;
+            pBootRecord->Off36.FAT32.RootClus      = 2;
+            pBootRecord->Off36.FAT32.FSInfo        = FAT32_OFFSET_FSINFO;
+            pBootRecord->Off36.FAT32.BkBootSec     = FAT32_OFFSET_BACKUP_BOOT;
+
+            pBootRecord->Off36.FAT32.DrvNum        = 0x00; 
+            pBootRecord->Off36.FAT32.BootSig       = 0x29;
+            pBootRecord->Off36.FAT32.VollID        = dVollID;
+            pBootRecord->Off36.FAT32.VolLab[0]     = 'N';
+            pBootRecord->Off36.FAT32.VolLab[1]     = 'O';
+            pBootRecord->Off36.FAT32.VolLab[2]     = ' ';
+            pBootRecord->Off36.FAT32.VolLab[3]     = 'N';
+            pBootRecord->Off36.FAT32.VolLab[4]     = 'A'; 
+            pBootRecord->Off36.FAT32.VolLab[5]     = 'M'; 
+            pBootRecord->Off36.FAT32.VolLab[6]     = 'E'; 
+            pBootRecord->Off36.FAT32.VolLab[7]     = ' '; 
+            pBootRecord->Off36.FAT32.VolLab[8]     = ' '; 
+            pBootRecord->Off36.FAT32.VolLab[9]     = ' '; 
+            pBootRecord->Off36.FAT32.VolLab[10]    = ' '; 
+
+            pBootRecord->Off36.FAT32.FilSysType[0] = 'F';
+            pBootRecord->Off36.FAT32.FilSysType[1] = 'A';
+            pBootRecord->Off36.FAT32.FilSysType[2] = 'T';
+            pBootRecord->Off36.FAT32.FilSysType[3] = '3';
+            pBootRecord->Off36.FAT32.FilSysType[4] = '2';
+            pBootRecord->Off36.FAT32.FilSysType[5] = ' ';
+            pBootRecord->Off36.FAT32.FilSysType[6] = ' ';
+            pBootRecord->Off36.FAT32.FilSysType[7] = ' ';
+
+            pBootRecord->Signature                 = FAT_SIGNATURE;
+
+            /*
+             * Write the BPB to the drive
+             */
+            dSector = BPB_HiddSec;
+            nError  = HWWriteSectors(bDevice, pBuffer, dSector, 1);    
+            if (nError == HW_OK)
+            {
+                dSector += FAT32_OFFSET_BACKUP_BOOT;
+                nError   = HWWriteSectors(bDevice, pBuffer, dSector, 1);            
+            }
+        }
+
+        /*
+         * Create the FSINFO
+         */
+        if (nError == HW_OK)
+        {
+            memset(pBuffer, 0x00, pDrive->wSectorSize);
+            pFSInfo = (FAT32_FSINFO *)pBuffer;
+
+            dFirstDataSector = BPB_HiddSec + BPB_RsvdSecCnt;
+            dLastDataSector  = dTotalSectors;
+            dMaxClusterCount = dLastDataSector - dFirstDataSector;
+            dMaxClusterCount = dMaxClusterCount / (LONG)bSecPerClus;
+
+            pFSInfo->FirstSignature               = FSINFO_FIRSTSIGNATURE;
+            pFSInfo->FSInfoSignature              = FSINFO_FSINFOSIGNATURE;
+            pFSInfo->NumberOfFreeClusters         = dMaxClusterCount;
+            pFSInfo->MostRecentlyAllocatedCluster = 2;
+            pFSInfo->Signature                    = FSINFO_SIGNATURE;
+
+            /*
+             * Write the FSINFO to the drive
+             */      
+            dSector = BPB_HiddSec + FAT32_OFFSET_FSINFO;
+            nError  = HWWriteSectors(bDevice, pBuffer, dSector, 1);        
+        }
+
+        /*
+         * Write first FAT entry after format.
+         */
+        if (nError == HW_OK)
+        {
+            dFAT1StartSector = BPB_HiddSec + BPB_RsvdSecCnt;    
+            dFAT2StartSector = dFAT1StartSector + dFATSz32; 
+
+            memset(pBuffer, 0x00, pDrive->wSectorSize);
+            pLong = (LONG *)pBuffer;
+
+            /*
+             * Set the two reserved cluster after the format,
+             * take a look in the MS Hardware White Paper
+             * Version 1.03, December 6, 2000 for more information.
+             */
+            *pLong++ = 0x0FFFFFF8;
+            *pLong++ = FAT32_CLUSTER_EOF;
+            *pLong++ = FAT32_CLUSTER_EOF;
+
+            nError = HWWriteSectors(bDevice, pBuffer, dFAT1StartSector, 1);        
+            if (nError == HW_OK)
+            {
+                nError = HWWriteSectors(bDevice, pBuffer, dFAT2StartSector, 1);              
+            }
+        }
+
+        /*
+         * Create RootDir
+         */
+        if (nError == HW_OK)
+        {
+            memset(pBuffer, 0x00, pDrive->wSectorSize);
+            pDirEntry = (FAT32_DIRECTORY_ENTRY *)pBuffer;
+
+            pDirEntry->Name[0]      = 'E'; 
+            pDirEntry->Name[1]      = 'T';
+            pDirEntry->Name[2]      = 'H';
+            pDirEntry->Name[3]      = 'E';
+            pDirEntry->Name[4]      = 'R';
+            pDirEntry->Name[5]      = 'N';
+            pDirEntry->Name[6]      = 'U';
+            pDirEntry->Name[7]      = 'T';
+
+            pDirEntry->Extension[0] = ' ';
+            pDirEntry->Extension[1] = ' ';
+            pDirEntry->Extension[2] = ' ';
+
+            pDirEntry->Attribute    = DIRECTORY_ATTRIBUTE_VOLUME_ID;
+            pDirEntry->HighCluster  = 0;
+            pDirEntry->LowCluster   = 0;
+            pDirEntry->FileSize     = 0;
+
+            dCluster2StartSector = dFAT2StartSector + dFATSz32;
+            nError = HWWriteSectors(bDevice, pBuffer, dCluster2StartSector, 1);                    
+        }
+
+        NutHeapFree(pBuffer);  
+
+        FATFree();
+
+
+        /*
+         * Don't forget to mount the drive
+         */   
+        if (nError == HW_OK)
+        {
+            nError = MountDrive(bDevice);
+        }
+
+        if (nError == HW_OK)
+        {
+            nError = NUTDEV_OK;        
+        }
+        else
+        {
+            nError = NUTDEV_ERROR;    
+        }    
+    }
+
+    return(nError);
+}
+#endif /* (FAT_SUPPORT_FORMAT >= 1) */
+

+ 399 - 0
flash.c

@@ -0,0 +1,399 @@
+/* ========================================================================
+ * [PROJECT]    SIR
+ * [MODULE]     Flash
+ * [TITLE]      Routines for Atmel AT45 serial dataflash memory chips.
+ * [FILE]       flash.c
+ * [VSN]        1.0
+ * [CREATED]    11042007
+ * [LASTCHNGD]  11042007
+ * [COPYRIGHT]  Copyright (C) STREAMIT BV 2010
+ * [PURPOSE]    contains all interface- and low-level routines to
+ *              read/write/delete blocks in the serial DataFlash (AT45DBXX)
+ * ======================================================================== */
+
+#define LOG_MODULE  LOG_FLASH_MODULE
+
+//#include <stdio.h>
+#include <cfg/os.h>
+#include <cfg/memory.h>
+
+#include <sys/timer.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "typedefs.h"
+#include "flash.h"
+#include "portio.h"
+#include "log.h"
+#include "spidrv.h"
+
+
+/*-------------------------------------------------------------------------*/
+/* local defines                                                           */
+/*-------------------------------------------------------------------------*/
+#ifndef MAX_AT45_CMDLEN
+#define MAX_AT45_CMDLEN         8
+#endif
+
+#ifndef AT45_ERASE_WAIT
+#define AT45_ERASE_WAIT         3000
+#endif
+
+#ifndef AT45_CHIP_ERASE_WAIT
+#define AT45_CHIP_ERASE_WAIT    50000
+#endif
+
+#ifndef AT45_WRITE_POLLS
+#define AT45_WRITE_POLLS        1000
+#endif
+
+#define DFCMD_READ_PAGE         0xD2    /* Read main memory page. */
+#define DFCMD_READ_STATUS       0xD7    /* Read status register. */
+#define DFCMD_CONT_READ         0xE8    /* Continuos read. */
+#define DFCMD_PAGE_ERASE        0x81    /* Page erase. */
+#define DFCMD_BUF1_WRITE        0x84    /* Buffer 1 write. */
+#define DFCMD_BUF1_FLASH        0x83    /* Buffer 1 flash with page erase. */
+
+/*
+ *  \brief last page of flash (264 bytes) can be dedicated for parameter storage
+ *   Special routines are provided for that goal but can be disabled here to save
+ *   codespace (about 360 bytes of code for GCC)
+ */
+
+//#define USE_FLASH_PARAM_PAGE
+
+/*-------------------------------------------------------------------------*/
+/* typedefs & structs                                                      */
+/*-------------------------------------------------------------------------*/
+/*!
+ * \brief Known device type entry.
+ */
+typedef struct _AT45_DEVTAB
+{
+    u_long devt_pages;
+    u_int devt_pagsiz;
+    u_int devt_offs;
+    u_char devt_srmsk;
+    u_char devt_srval;
+} AT45_DEVTAB;
+
+/*!
+ * \brief Active device entry.
+ */
+typedef struct _AT45DB_DCB
+{
+    AT45_DEVTAB *dcb_devt;
+    u_char dcb_cmdbuf[MAX_AT45_CMDLEN];
+} AT45DB_DCB;
+
+/*!
+ * \brief Table of known Dataflash types.
+ */
+AT45_DEVTAB at45_devt[] = {
+    {512,  264,  9,  0x3C, 0x0C},   // AT45DB011B - 128kB
+    {1025, 264,  9,  0x3C, 0x14},   // AT45DB021B - 256kB
+    {2048, 264,  9,  0x3C, 0x1C},   // AT45DB041B - 512kB
+    {4096, 264,  9,  0x3C, 0x24},   // AT45DB081B - 1MB
+    {4096, 528,  10, 0x3C, 0x2C},   // AT45DB0161B - 2MB
+    {8192, 528,  10, 0x3C, 0x34},   // AT45DB0321B - 4MB
+    {8192, 1056, 11, 0x38, 0x38},   // AT45DB0642 - 8MB
+    {0,    0,    0,  0,    0}       // End of table
+};
+
+/*-------------------------------------------------------------------------*/
+/* local variable definitions                                              */
+/*-------------------------------------------------------------------------*/
+/*!
+ * \brief Table of active devices.
+ */
+static AT45DB_DCB dcbtab;
+
+/*-------------------------------------------------------------------------*/
+/* local routines (prototyping)                                            */
+/*-------------------------------------------------------------------------*/
+static int At45dbTransfer(CONST void *txbuf, void *rxbuf, int xlen, CONST void *txnbuf, void *rxnbuf, int xnlen);
+
+/*!
+ * \addtogroup SerialFlash
+ */
+
+/*@{*/
+
+/*-------------------------------------------------------------------------*/
+/*                         start of code                                   */
+/*-------------------------------------------------------------------------*/
+
+/*!
+ * \brief mid-level SPI-interface routine
+ *
+ *  This routine handles sending a command an reading back the reply in one routine
+ *  It will perform 'xlen' + 'xnlen' SPI-byte cycles. During the 'xlen' cycles, data in 'txbuf'
+ *  is sent using the SPI, and each resulting byte is stored in 'rxbuf'. Then it starts with
+ *  the 'xnlen' cycles whereby the contents of the 'txnbuf' are sent using the SPI.
+ *  Each resulting byte then is stored in 'rxnbuf'
+ */
+//                                        cb,          cb,        len,             tdata,         rdata,      datalen
+static int At45dbTransfer(CONST void *txbuf, void *rxbuf, int xlen, CONST void *txnbuf, void *rxnbuf, int xnlen)
+{   int i;
+    u_char *ptTxbuf, *ptRxbuf;
+
+    SPIselect(SPI_DEV_FLASH);
+
+    ptTxbuf=(u_char*)txbuf;
+    ptRxbuf=(u_char*)rxbuf;
+    /*
+     *  send command, store the bytes that were read the same time
+     */
+    for (i=0; i<xlen; ++i)
+    {
+        *ptRxbuf++=SPItransferByte(*ptTxbuf++);
+    }
+
+    ptTxbuf=(u_char*)txnbuf;
+    ptRxbuf=(u_char*)rxnbuf;
+    /*
+     *  send dummy data, store the bytes that were read the same time
+     */
+    for (i=0; i<xnlen; ++i)
+    {
+        *ptRxbuf++=SPItransferByte(*ptTxbuf++);
+    }
+
+    SPIdeselect();
+
+    return(0);  // always...
+
+}
+
+/*!
+ * \brief send a command to the AT45dbXX
+ *
+ */
+int At45dbSendCmd(u_char op, u_long parm, int len, CONST void *tdata, void *rdata, int datalen)
+{
+    u_char *cb = dcbtab.dcb_cmdbuf;
+
+    if (len > MAX_AT45_CMDLEN)
+    {
+        return (-1);
+    }
+    memset(cb, 0, len);
+    cb[0] = op;
+    if (parm)
+    {
+        cb[1] = (u_char) (parm >> 16);
+        cb[2] = (u_char) (parm >> 8);
+        cb[3] = (u_char) parm;
+    }
+    return (At45dbTransfer(cb, cb, len, tdata, rdata, datalen));
+}
+
+/*!
+ * \brief read status
+ *
+ */
+u_char At45dbGetStatus()
+{
+    u_char buf[2] = { DFCMD_READ_STATUS, 0xFF};
+
+    if (At45dbTransfer(buf, buf, 2, NULL, NULL, 0))
+    {
+        return(u_char) - 1;
+    }
+    return (buf[1]);
+}
+
+/*!
+ * \brief Wait until flash memory cycle finished.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+int At45dbWaitReady(u_long tmo, int poll)
+{
+    u_char sr;
+
+    while (((sr = At45dbGetStatus()) & 0x80) == 0)
+    {
+        if (!poll)
+        {
+            NutSleep(1);
+        }
+        if (tmo-- == 0)
+        {
+            return (-1);
+        }
+    }
+    return (0);
+}
+
+/*!
+ * \brief runtime detection of serial flash device
+ */
+int At45dbInit()
+{
+    u_char sr;
+    u_char i;
+
+    At45dbGetStatus();
+    sr = At45dbGetStatus();
+
+    for (i=0; at45_devt[i].devt_pages; i++)
+    {
+        if ((sr & at45_devt[i].devt_srmsk) == at45_devt[i].devt_srval)
+        {
+            dcbtab.dcb_devt = &at45_devt[i];
+            break;
+        }
+    }
+    return (i);
+}
+
+/*!
+ * \brief Erase sector at the specified offset.
+ */
+int At45dbPageErase(u_int pgn)
+{
+    return (At45dbSendCmd(DFCMD_PAGE_ERASE, pgn, 4, NULL, NULL, 0));
+}
+
+/*!
+ * \brief Erase entire flash memory chip.
+ */
+int At45dbChipErase(void)
+{
+    return (-1);
+}
+
+/*!
+ * \brief Read data from flash memory.
+ *
+ * \param pgn  Page number to read, starting at 0.
+ * \param data Points to a buffer that receives the data.
+ * \param len  Number of bytes to read.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+int At45dbPageRead(u_long pgn, void *data, u_int len)
+{
+    pgn <<= dcbtab.dcb_devt->devt_offs;
+    return (At45dbSendCmd(DFCMD_CONT_READ, pgn, 8, data, data, len));
+}
+
+/*!
+ * \brief Write data into flash memory.
+ *
+ * The related sector must have been erased before calling this function.
+ *
+ * \param pgn  Start location within the chip, starting at 0.
+ * \param data Points to a buffer that contains the bytes to be written.
+ * \param len  Number of bytes to write.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+int At45dbPageWrite(u_long pgn, CONST void *data, u_int len)
+{
+    int rc = -1;
+    void *rp;
+
+    if ((rp = malloc(len)) != NULL)
+    {
+        /* Copy data to dataflash RAM buffer. */
+        if (At45dbSendCmd(DFCMD_BUF1_WRITE, 0, 4, data, rp, len) == 0)
+        {
+            /* Flash RAM buffer. */
+            pgn <<= dcbtab.dcb_devt->devt_offs;
+            if (At45dbSendCmd(DFCMD_BUF1_FLASH, pgn, 4, NULL, NULL, 0) == 0)
+            {
+                rc = At45dbWaitReady(AT45_WRITE_POLLS, 1);
+            }
+        }
+        free(rp);
+    }
+    return (rc);
+}
+
+#ifdef USE_FLASH_PARAM_PAGE
+
+u_long At45dbParamPage(void)
+{
+#ifdef AT45_CONF_PAGE
+    return (AT45_CONF_PAGE);
+#else
+    return (dcbtab.dcb_devt->devt_pages - 1);
+#endif
+}
+
+int At45dbParamSize(void)
+{
+    int rc;
+
+#ifdef AT45_CONF_SIZE
+    rc = AT45_CONF_SIZE;
+#else
+    rc = dcbtab.dcb_devt->devt_pagsiz;
+#endif
+    return (rc);
+}
+
+/*!
+ * \brief Load configuration parameters from flash memory.
+ *
+ * \param pos  Start location within configuration sector.
+ * \param data Points to a buffer that receives the contents.
+ * \param len  Number of bytes to read.
+ *
+ * \return Always 0.
+ */
+int At45dbParamRead(u_int pos, void *data, u_int len)
+{
+    int rc = -1;
+    u_char *buff;
+    int csize = At45dbParamSize();
+    u_long cpage = At45dbParamPage();
+
+    /* Load the complete configuration area. */
+    if (csize > len && (buff = malloc(csize)) != NULL)
+    {
+        rc = At45dbPageRead(cpage, buff, csize);
+        /* Copy requested contents to caller's buffer. */
+        memcpy(data, buff + pos, len);
+        free(buff);
+    }
+    return (rc);
+}
+
+/*!
+ * \brief Store configuration parameters in flash memory.
+ *
+ * \param pos   Start location within configuration sector.
+ * \param data  Points to a buffer that contains the bytes to store.
+ * \param len   Number of bytes to store.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+int At45dbParamWrite(u_int pos, CONST void *data, u_int len)
+{
+    int rc = -1;
+    u_char *buff;
+    int csize = At45dbParamSize();
+    u_long cpage = At45dbParamPage();
+
+    /* Load the complete configuration area. */
+    if (csize > len && (buff = malloc(csize)) != NULL)
+    {
+        rc = At45dbPageRead(cpage, buff, csize);
+        /* Compare old with new contents. */
+        if (memcmp(buff + pos, data, len))
+        {
+            /* New contents differs. Copy it into the sector buffer. */
+            memcpy(buff + pos, data, len);
+            /* Erase sector and write new data. */
+            rc = At45dbPageWrite(cpage, buff, csize);
+        }
+        free(buff);
+    }
+    return (rc);
+}
+
+#endif // USE_FLASH_PARAM_PAGE

+ 303 - 0
http.c

@@ -0,0 +1,303 @@
+/*
+ *  Copyright STREAMIT BV, 2010.
+ *
+ *  Project             : SIR
+ *  Module              : Http
+ *  File name  $Workfile: Http.c  $
+ *       Last Save $Date: 2003/08/23 18:39:38  $
+ *             $Revision: 0.1  $
+ *  Creation Date       : 2003/08/23 18:39:38
+ *
+ *  Description         : Http client routines
+ *
+ */
+
+#define LOG_MODULE  LOG_HTTP_MODULE
+
+/*--------------------------------------------------------------------------*/
+/*  Include files                                                           */
+/*--------------------------------------------------------------------------*/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/confos.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+//#pragma text:appcode
+
+#include "system.h"
+#include "log.h"
+#include "settings.h"
+#include "util.h"
+
+#include "http.h"
+
+/*--------------------------------------------------------------------------*/
+/*  Constant definitions                                                    */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Type declarations                                                       */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Local variables                                                         */
+/*--------------------------------------------------------------------------*/
+static CONST char EncTable[] =
+{
+    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+    'w', 'x', 'y', 'z', '0', '1', '2', '3',
+    '4', '5', '6', '7', '8', '9', '+', '/'
+};
+
+/*--------------------------------------------------------------------------*/
+/*  Global variables                                                        */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Local functions                                                         */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Global functions                                                        */
+/*--------------------------------------------------------------------------*/
+
+/*!
+ * \brief Calculate the space needed to store data in Base64
+ *
+ * \param NrOfBytes     number of bytes to encode
+ *
+ * \return Number of bytes needed
+ */
+int Base64EncodedSize(size_t tNrOfBytes)
+{
+    return ((tNrOfBytes + 2) / 3 * 4) + 1;
+}
+
+/*!
+ * \brief Encode (binary) data into a Base64 encoded string.
+ *
+ * \param szDest        pointer to destination
+ * \param pSrc          pointer to (binary) data to encode.
+ * \param tSize         nrof bytes to read from pSrc
+ *
+ * \return Number of bytes copied to szDest
+ *         0 if errors or nothing to encode
+ */
+size_t Base64Encode(char *szDest, CONST u_char *pSrc, size_t tSize)
+{
+    size_t DestLen = 0;                 /* nrof bytes in szDest */
+    u_char Index = 0;
+    unsigned char Tmp[3];               /* source data */
+
+    /* Encode all input data */
+    while (tSize > 0)
+    {
+        /* Get a piece of data to encode */
+        memset(Tmp, 0, sizeof(Tmp));
+        for (Index = 0; Index < sizeof(Tmp) && tSize > 0; Index++, tSize--)
+        {
+            Tmp[Index] = *pSrc++;
+        }
+
+        /* Encode 3 chars into 4 */
+        *szDest++ =                     EncTable[                          ((Tmp[0] >> 2) & 0x3F) ];
+        *szDest++ =                     EncTable[ ((Tmp[0] << 4) & 0x3F) | ((Tmp[1] >> 4) & 0x0F) ];
+        *szDest++ = (Index < 1) ? '=' : EncTable[ ((Tmp[1] << 2) & 0x3C) | ((Tmp[2] >> 6) & 0x03) ];
+        *szDest++ = (Index < 2) ? '=' : EncTable[ ( Tmp[2]       & 0x3F) ];
+        DestLen += 4;
+    }
+
+    *szDest = '\0';
+    return (DestLen);
+}
+
+/*!
+ * \brief Get ip address of a host
+ *
+ * \param szHostName Name or string of the IP address
+ *                   of the host
+ * \return The IP address of the host.
+ *         0 if not an IP address, or we could not resolve the name
+ */
+u_long GetHostByName(CONST char *szHostName)
+{
+    u_long dwAddress;
+
+    if ((dwAddress = inet_addr(szHostName)) == (u_long)-1)
+    {
+        dwAddress = NutDnsGetHostByName((u_char*)szHostName);
+    }
+    return (dwAddress);
+}
+
+/*!
+ * \brief Break a Url down in parts
+ *
+ * The hostname, port and URI pointers are
+ * set to the appropriate locations or an empty string
+ * if not present.
+ *
+ * \note szUrl is modified
+ *
+ * \param szUrl Url to parse
+ */
+void HttpParseUrl(char *szUrl, TUrlParts *tUrlParts)
+{
+    char *szStart;      /* Points to the first character of the part */
+    char *szEnd;        /* Points to the last character of the part */
+
+    /*
+     * In case we don't find a Host, port or URI, point
+     * to empty string
+     */
+    tUrlParts->pszHost = tUrlParts->pszPort = tUrlParts->pszPath = (char *)(szUrl + strlen(szUrl));
+
+    /*
+     * skip the prefix
+     */
+    szStart = strstr_P(szUrl, PSTR("://"));
+    if (szStart != NULL)
+    {
+        szStart += 3;
+    }
+    else
+    {
+        /*
+         * Apparently there is no prefix
+         */
+        szStart  = (char *)szUrl;
+    }
+
+    /*
+     * We have found the hostname
+     */
+    tUrlParts->pszHost = szStart;
+
+    /*
+     * Find the end of the hostname
+     * End of it is indicated by ':' or '/'
+     * If neither are found, assume we have a URL in
+     * the form 'http://demeterkast.net'
+     */
+    szEnd = strchr(szStart, ':');
+    if (szEnd != NULL)
+    {
+        /*
+         * There is a port specification, get it now
+         */
+        *szEnd = '\0';          /* Terminate the previous part */
+        szStart = szEnd + 1;        /* point to the portnumber */
+        tUrlParts->pszPort = szStart;
+    }
+
+    szEnd = strchr(szStart, '/');
+    if (szEnd != NULL)
+    {
+        /*
+         * There is a URI specification, get it now
+         */
+        *szEnd = '\0';          /* Terminate the previous part */
+        tUrlParts->pszPath = szEnd + 1;   /* point to the URI */
+    }
+}
+
+/*!
+ * \brief Send a request to a server.
+ *
+ * The connection to the server should already be
+ * present and associated with a stream.
+ *
+ * \param   ptStream [in] Opened stream to send the request to
+ * \param   pszHeaders [in] The headers to send
+ * \param   wMode [in] Bitmask for the request to send
+ *              - HTTP_AUTH to send a Basic authentication consisting of our hostname and an empty password
+ * \return  The number of characters written or a negative value to
+ *          indicate an error.
+ */
+int HttpSendRequest(FILE *ptStream, CONST char *pszHeaders, u_short wMode)
+{
+    int nResult = 0;    /* Bytes sent during last call (or -1) */
+    int nSent = 0;      /* Total bytes sent */
+
+    if ((pszHeaders == NULL) || (ptStream == NULL))
+    {
+        nResult = -1;
+    }
+
+    /*
+     * Send the headers
+     */
+    if (nResult >= 0)
+    {
+        nSent += nResult;
+        nResult = fprintf_P(ptStream, PSTR("%s"), pszHeaders);
+    }
+
+    /*
+     * Add authentication info (if requested)
+     */
+    if (nResult >= 0)
+    {
+        nSent += nResult;
+        nResult = 0;
+
+        if (wMode & HTTP_AUTH)
+        {
+            char *szEncoded;
+            char szToken[sizeof(confos.hostname)+1];
+
+            /*
+             * Compose the username:password
+             * (We use our hostname and an empty password)
+             */
+            strncpy(szToken, confos.hostname, sizeof(szToken)-1);
+            szToken[sizeof(szToken)-1] = '\0';
+            strcat_P(szToken, PSTR(":"));
+
+            szEncoded = MyMalloc(Base64EncodedSize(strlen(szToken)));
+            if (szEncoded == NULL)
+            {
+                nResult = -1;
+            }
+            else
+            {
+                (void)Base64Encode(szEncoded, (u_char*)szToken, strlen(szToken));
+
+                nResult = fprintf_P(ptStream, PSTR("Authorization: Basic %s\r\n"), szEncoded);
+
+                MyFree(szEncoded);
+            }
+        }
+    }
+
+    /*
+     * Print the end of header
+     */
+    if (nResult >= 0)
+    {
+        nSent += nResult;
+        nResult = fprintf_P(ptStream, PSTR("\r\n"));
+    }
+
+    fflush(ptStream);
+
+    if (nResult >= 0)
+    {
+        nSent += nResult;
+    }
+    else
+    {
+        nSent = -1;
+    }
+
+    return (nSent);
+}
+

+ 1538 - 0
inet.c

@@ -0,0 +1,1538 @@
+/*
+ *  Copyright STREAMIT BV, 2010.
+ *
+ *  Project             : SIR
+ *  Module              : Inet
+ *  File name  $Workfile: Inet.c  $
+ *       Last Save $Date: 2006/02/24 13:46:16  $
+ *             $Revision: 0.1  $
+ *  Creation Date       : 2006/02/24 13:46:16
+ *
+ *  Description         :
+ *
+ */
+
+#define LOG_MODULE  LOG_INET_MODULE
+
+/*--------------------------------------------------------------------------*/
+/*  Include files                                                           */
+/*--------------------------------------------------------------------------*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/timer.h>
+#include <sys/socket.h>
+#include <sys/heap.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+//#pragma text:appcode
+
+#include "system.h"
+#include "version.h"
+#include "log.h"
+#include "settings.h"
+#include "util.h"
+
+#include "inet.h"
+
+/*--------------------------------------------------------------------------*/
+/*  Constant definitions                                                    */
+/*--------------------------------------------------------------------------*/
+/*!\brief Allow some problems before giving up */
+//#define MAX_NODNS               5
+#define MAX_NODNS               2
+#define MAX_NOCONNECT           2
+#define MAX_BADRESPONSE         5
+#define MAX_REDIRECT            5   /* see RFC2616 */
+
+/*!\brief Default Receive timeout */
+#define TCP_RECVTO_DEFAULT      5000
+
+/*!\brief HTTP line buffer size. Allocates in chunks of this size. */
+#define HTTP_HEADER_LINE_SIZE   512
+
+#ifdef DEBUG
+//#define INET_DEBUG
+#endif /* #ifdef DEBUG */
+
+/*--------------------------------------------------------------------------*/
+/*  Type declarations                                                       */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Local variables                                                         */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Global variables                                                        */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Local functions                                                         */
+/*--------------------------------------------------------------------------*/
+static TError Connect(HINET hInet);
+static void GetHeaders(HINET hInet);
+static int CreateRequest(HINET hInet, CONST char *pszMethod, CONST char *pszPath, CONST char *pszAccept);
+static void CloseDescriptors(HINET hInet);
+
+#ifdef INET_DEBUG
+static void ShowDebug(void)
+{
+    LogMsg_P(LOG_DEBUG, PSTR("free %d"), NutHeapAvailable());
+}
+#else
+#define ShowDebug()
+#endif
+
+/*!
+ * \brief Opens an Internet session.
+ *
+ * \param   hInet [in] Handle returned by a previous call to InternetOpen.
+ *
+ * \return  0 if the connection is successful
+ *          TError otherwise.
+ */
+static TError Connect(HINET hInet)
+{
+    TError tError = OK;
+    unsigned char byDone = 0;
+    char ModeString[5];
+
+    /*
+     * Connect to the server. Retry in case of problems
+     */
+    while (byDone == 0)
+    {
+        if (hInet->tState == INET_STATE_CLOSING)
+        {
+            tError = USER_ABORT;
+        }
+
+        if (tError == OK)
+        {
+            hInet->tState = INET_STATE_BUSY;
+        }
+
+        /*
+         * Translate to an IP number and port
+         */
+        if (tError == OK)
+        {
+            if ((hInet->wPort = atoi(hInet->tUrlParts.pszPort)) == 0)
+            {
+                // Use defaults if not specified
+                hInet->wPort = 80;
+            }
+            LogMsg_P(LOG_DEBUG, PSTR("Looking up [%s]"), hInet->tUrlParts.pszHost);
+
+            if ((hInet->ulIpAddress = GetHostByName(hInet->tUrlParts.pszHost)) == 0)
+            {
+                tError = INET_HOSTNOTFOUND;
+
+                /*
+                 * Check if we can retry
+                 */
+                if (++hInet->tRetries.byNoDnsCount >= MAX_NODNS)
+                {
+                    /* Too many failures, stop */
+                    tError = INET_HOST_NONEXISTANT;
+                }
+            }
+        }
+
+        /*
+         * We could have been asleep; Check if we have received a close request
+         */
+        if (hInet->tState == INET_STATE_CLOSING)
+        {
+            tError = USER_ABORT;
+        }
+
+        /*
+         * Create a socket.
+         */
+        ShowDebug();
+        if (tError == OK)
+        {
+            if ((hInet->ptSocket = NutTcpCreateSocket()) == 0)
+            {
+                tError = INET_CREATE_SOCKET;
+            }
+        }
+#ifdef INET_DEBUG
+        LogMsg_P(LOG_DEBUG, PSTR("ptSocket @%X"), hInet->ptSocket);
+#endif /* #ifdef INET_DEBUG */
+        ShowDebug();
+
+        /*
+         * Set socket options
+         */
+        if (tError == OK)
+        {
+            /* We use our own default if not specified */
+            if (hInet->ulRecvTimeout == 0)
+            {
+                hInet->ulRecvTimeout = TCP_RECVTO_DEFAULT;
+            }
+            /* Nut/OS defaults to infinite receive timeout. So always set our default timeout */
+            if (NutTcpSetSockOpt(hInet->ptSocket, SO_RCVTIMEO, &hInet->ulRecvTimeout, sizeof(hInet->ulRecvTimeout)))
+            {
+                tError = INET_SOCK_RCVTO;
+            }
+        }
+        if (tError == OK)
+        {
+            /* Use NutOS's default if not specified */
+            if ((hInet->unMss != 0) &&
+                (NutTcpSetSockOpt(hInet->ptSocket, TCP_MAXSEG, &hInet->unMss, sizeof(hInet->unMss))))
+            {
+                tError = INET_SOCK_MSS;
+            }
+        }
+        if (tError == OK)
+        {
+            /* Use NutOS's default if not specified */
+            if ((hInet->unTcpRecvBufSize != 0) &&
+                (NutTcpSetSockOpt(hInet->ptSocket, SO_RCVBUF, &hInet->unTcpRecvBufSize, sizeof(hInet->unTcpRecvBufSize))))
+            {
+                tError = INET_SOCK_RXBUF;
+            }
+        }
+
+        /*
+         * Connect to destination
+         */
+        if (tError == OK)
+        {
+            LogMsg_P(LOG_DEBUG, PSTR("Connecting to %s:%d"), inet_ntoa(hInet->ulIpAddress), hInet->wPort);
+            if (NutTcpConnect(hInet->ptSocket, hInet->ulIpAddress, hInet->wPort) != 0)
+            {
+                tError = INET_NOCONNECT;
+
+                LogMsg_P(LOG_ERR, PSTR("No connect"));
+
+                if (++hInet->tRetries.byNoConnectCount >= MAX_NOCONNECT)
+                {
+                    tError = INET_TOO_MANY_NOCONNECTS;
+                }
+            }
+            else
+            {
+                /* Connected, stop */
+                byDone = 1;
+
+                LogMsg_P(LOG_DEBUG, PSTR("TCP Connected"));
+                /* Let the TCP/IP stack settle down first */
+                NutSleep(500);
+            }
+        }
+
+        /*
+         * We could have been asleep; Check if we have receive a close request
+         */
+        if (hInet->tState == INET_STATE_CLOSING)
+        {
+            tError = USER_ABORT;
+        }
+
+        /*
+         * Create a stream from the socket.
+         */
+        if (tError == OK)
+        {
+            strcpy_P(ModeString, PSTR("r+b"));
+            if ((hInet->ptStream = _fdopen((int)hInet->ptSocket, ModeString)) == 0)
+            {
+                LogMsg_P(LOG_ERR, PSTR("No stream %d"), errno);
+                tError = INET_CREATE_STREAM;
+            }
+        }
+
+#ifdef INET_DEBUG
+        LogMsg_P(LOG_DEBUG, PSTR("ptStream @%X"), hInet->ptStream);
+#endif /* #ifdef INET_DEBUG */
+
+
+        if (tError != OK)
+        {
+            LogMsg_P(LOG_ERR, PSTR("Error [%d]"), tError);
+
+            /*
+             * Check if we need to try again
+             */
+            if ((tError > PLAYER_WARNINGS) && (tError < PLAYER_ERRORS))
+            {
+                LogMsg_P(LOG_INFO, PSTR("Retry"));
+
+                CloseDescriptors(hInet);
+
+                /*
+                 * Try again
+                 */
+                tError = OK;
+
+                /*
+                 * Give other threads some time before
+                 * we try again
+                 */
+                NutSleep(300);
+            }
+            else
+            {
+                /* Errors, stop */
+                byDone = 1;
+            }
+        }
+    } /* end while */
+
+    ShowDebug();
+
+    hInet->tState = INET_STATE_IDLE;
+    return (tError);
+}
+
+/*!
+ * \brief Create a new request to be sent to an Internet server.
+ *
+ * hInet->hRequest should already have allocated memory behind it, as this
+ * routine does not allocate (or free) memory.
+ *
+ * \param   hInet [in] Handle returned by a previous call to InternetOpen.
+ * \param   pszMethod [in] A pointer to a null-terminated string that contains
+ *          the method to use in the request. If this parameter is NULL, the
+ *          function uses GET.
+ * \param   pszPath [in] A pointer to a null-terminated string that contains
+ *          the path to act upon.
+ * \param   pszAccept[in] A pointer to a null-terminated string that indicates
+ *          the media types accepted by the client. If this parameter is NULL,
+ *          a string that indicates that all types are accepted is sent to the
+ *          server.
+ *
+ * \return  0 when the request was successfully created
+ *          -1 on errors
+ */
+static int CreateRequest(HINET hInet, CONST char *pszMethod, CONST char *pszPath, CONST char *pszAccept)
+{
+    static prog_char cszAction_P[]      = "%s /%s%s HTTP/1.0\r\n";
+    static prog_char cszUserAgent_P[]   = "User-Agent: %s/%s s/n:%s\r\n";
+
+    // create buffers for RAM-strings...
+    static char szEmptyString[1];
+    static char szGet[4];
+    static char szSerialNum[9];
+    static char szAcceptDefault[20];
+
+    int nResult = 0;
+    char *pszSerialNr;
+
+    // apply defaults for RAM-strings....
+    szEmptyString[0] = '\0';
+    szSerialNum[0] = '\0';
+    strcpy_P(szGet, PSTR("GET"));
+    strcpy_P(szAcceptDefault, PSTR("Accept: */*\r\n"));
+
+    pszSerialNr = szEmptyString;
+
+#ifdef INET_DEBUG
+    LogMsg_P(LOG_DEBUG, PSTR("Create request"));
+#endif /* #ifdef INET_DEBUG */
+
+    if ((hInet == NULL) || (hInet->hRequest == NULL))
+    {
+        /* Bad argument */
+        nResult = -1;
+    }
+
+    if (nResult >= 0)
+    {
+        /*
+         * Erase any previous requests
+         */
+        hInet->hRequest->unRequestInUse = 0;
+        hInet->hRequest->pszRequest[0] = '\0';
+
+        /*
+         * Use defaults for the method and Uri if not further specified
+         */
+        if (pszMethod == NULL)
+        {
+            pszMethod = szGet;
+        }
+        if (pszPath == NULL)
+        {
+            pszPath = hInet->tUrlParts.pszPath;
+        }
+
+        //sprintf_P(szSerialNum, PSTR("%5.5lX"), SettingsGetSerialnumber());
+    }
+
+    /*
+     * Check if we need to add our serial number to the end of the URL
+     */
+    if (nResult >= 0)
+    {
+        if ((hInet->hRequest->wOptions & INET_FLAG_ADD_SERIAL) == INET_FLAG_ADD_SERIAL)
+        {
+            if (strlen(pszPath) > 0)
+            {
+                if (pszPath[strlen(pszPath)-1] == '=')
+                {
+                    pszSerialNr = szSerialNum;
+                }
+            }
+        }
+    }
+
+    /*
+     * Create the request
+     */
+    if (nResult >= 0)
+    {
+        nResult = sprintf_P(&hInet->hRequest->pszRequest[hInet->hRequest->unRequestInUse],
+                            cszAction_P,
+                            pszMethod,
+                            pszPath,
+                            pszSerialNr);
+        if (nResult >= 0)
+        {
+#ifdef INET_DEBUG
+            /* We already print out the request at the end of this routine */
+#else /* #ifdef INET_DEBUG */
+//            LogMsg_P(LOG_DEBUG, PSTR("Request [%s]"), &hInet->hRequest->pszRequest[hInet->hRequest->unRequestInUse]);
+#endif /* #ifdef INET_DEBUG */
+            hInet->hRequest->unRequestInUse += nResult;
+            if (hInet->hRequest->unRequestInUse > hInet->hRequest->unRequestBufSize)
+            {
+                nResult = -1;
+            }
+        }
+    }
+
+    /*
+     * Add the User-Agent
+     */
+    if (nResult >= 0)
+    {
+        //nResult = sprintf_P(&hInet->hRequest->pszRequest[hInet->hRequest->unRequestInUse],
+        //                    cszUserAgent_P,
+                            //VersionGetAppProductName(),
+                            //VersionGetAppString(),
+        //                    szSerialNum);
+        if (nResult >= 0)
+        {
+            hInet->hRequest->unRequestInUse += nResult;
+            if (hInet->hRequest->unRequestInUse > hInet->hRequest->unRequestBufSize)
+            {
+                nResult = -1;
+            }
+        }
+    }
+
+    /*
+     * Add the host header if needed
+     */
+    if (nResult >= 0)
+    {
+        if ((hInet->tUrlParts.pszHost != NULL) && (strlen(hInet->tUrlParts.pszHost) > 0))
+        {
+            if ((hInet->tUrlParts.pszPort != NULL) && (strlen(hInet->tUrlParts.pszPort) > 0))
+            {
+                nResult = sprintf_P(&hInet->hRequest->pszRequest[hInet->hRequest->unRequestInUse],
+                                    PSTR("Host: %s:%s\r\n"),
+                                    hInet->tUrlParts.pszHost,
+                                    hInet->tUrlParts.pszPort);
+            }
+            else
+            {
+                nResult = sprintf_P(&hInet->hRequest->pszRequest[hInet->hRequest->unRequestInUse],
+                                    PSTR("Host: %s\r\n"),
+                                    hInet->tUrlParts.pszHost);
+            }
+            if (nResult >= 0)
+            {
+                hInet->hRequest->unRequestInUse += nResult;
+                if (hInet->hRequest->unRequestInUse > hInet->hRequest->unRequestBufSize)
+                {
+                    nResult = -1;
+                }
+            }
+        }
+    }
+
+    /*
+     * Add the accept header
+     */
+    if (nResult >= 0)
+    {
+        if (pszAccept == NULL)
+        {
+            pszAccept = szAcceptDefault;
+        }
+        nResult = sprintf(&hInet->hRequest->pszRequest[hInet->hRequest->unRequestInUse],
+                          szAcceptDefault,
+                          pszAccept);
+
+        if (nResult >= 0)
+        {
+            hInet->hRequest->unRequestInUse += nResult;
+            if (hInet->hRequest->unRequestInUse > hInet->hRequest->unRequestBufSize)
+            {
+                nResult = -1;
+            }
+        }
+    }
+
+    /*
+     * Check if we need to do a request for ICY meta data
+     */
+    if (nResult >= 0)
+    {
+        if ((hInet->hRequest->wOptions & INET_FLAG_ICY_META_REQ) == INET_FLAG_ICY_META_REQ)
+        {
+            nResult = sprintf_P(&hInet->hRequest->pszRequest[hInet->hRequest->unRequestInUse],
+                                PSTR("Icy-MetaData:1\r\n"));
+            if (nResult >= 0)
+            {
+                hInet->hRequest->unRequestInUse += nResult;
+                if (hInet->hRequest->unRequestInUse > hInet->hRequest->unRequestBufSize)
+                {
+                    nResult = -1;
+                }
+            }
+        }
+    }
+
+    /*
+     * Check if we need to close the connection
+     */
+    if (nResult >= 0)
+    {
+        if ((hInet->hRequest->wOptions & INET_FLAG_CLOSE) == INET_FLAG_CLOSE)
+        {
+            nResult = sprintf_P(&hInet->hRequest->pszRequest[hInet->hRequest->unRequestInUse],
+                                PSTR("Connection: close\r\n"));
+            if (nResult >= 0)
+            {
+                hInet->hRequest->unRequestInUse += nResult;
+                if (hInet->hRequest->unRequestInUse > hInet->hRequest->unRequestBufSize)
+                {
+                    nResult = -1;
+                }
+            }
+        }
+    }
+
+    /* Correct lenght in use for last \0 */
+    if (hInet->hRequest->unRequestInUse > 0)
+    {
+        hInet->hRequest->unRequestInUse += 1;
+    }
+
+    /*
+     * Log the request
+     */
+    if (nResult >= 0)
+    {
+//#ifdef INET_DEBUG
+        LogMsg_P(LOG_DEBUG, PSTR("Request %u [%s]"), hInet->hRequest->unRequestInUse, hInet->hRequest->pszRequest);
+//#endif /* #ifdef INET_DEBUG */
+    }
+
+    return (nResult);
+}
+
+
+/*!
+ * \brief Get the HTTP response headers.
+ *
+ * This function returns after all response headers have been
+ * received.
+ *
+ * \param   hInet [in] Handle returned by a previous call to InternetOpen.
+ *
+ * \return  -
+ */
+static void GetHeaders(HINET hInet)
+{
+    unsigned char byDone = 0;
+
+    /*
+     * Create room for buffers
+     */
+    char *pszRespLine = MyMalloc(HTTP_HEADER_LINE_SIZE);
+    if (pszRespLine == NULL)
+    {
+        /* No memory */
+        byDone = 1;
+    }
+
+    ShowDebug();
+
+    if (hInet->hRequest == NULL)
+    {
+        /* Bad argument */
+        byDone = 1;
+    }
+    else
+    {
+        /* Reset received counter */
+        hInet->hRequest->unResponseInUse = 0;
+    }
+
+    /*
+     * Process all header lines
+     */
+    while ((byDone == 0) &&
+           (fgets(pszRespLine, HTTP_HEADER_LINE_SIZE, hInet->ptStream) != NULL))
+    {
+        /*
+         * We could have been asleep; Check if we have received a close request
+         */
+        if (hInet->tState == INET_STATE_CLOSING)
+        {
+            byDone = 1;
+        }
+        else if ((pszRespLine[0] == '\r') && (pszRespLine[1] == '\n'))
+        {
+            /*
+             * An empty line indicates the end of the headers
+             */
+            byDone = 1;
+        }
+        else
+        {
+            /*
+             * Log the line (without the end of line stuff)
+             */
+            unsigned int unLength = 0;
+            char *pszLogEol = strchr(pszRespLine, '\r');
+            if (pszLogEol != NULL)
+            {
+                unLength = pszLogEol - pszRespLine;
+            }
+            LogMsg_P(LOG_DEBUG, PSTR("Read [%.*s]"), unLength, pszRespLine);
+
+            if (BufferAddString(&hInet->hRequest->pszResponse,
+                                &hInet->hRequest->unResponseBufSize,
+                                &hInet->hRequest->unResponseInUse,
+                                pszRespLine) != 0)
+            {
+                byDone = 1;
+            }
+        }
+    }
+
+    ShowDebug();
+#ifdef INET_DEBUG
+    LogMsg_P(LOG_DEBUG, PSTR("%d Read"), hInet->hRequest->unResponseInUse);
+#endif /* #ifdef INET_DEBUG */
+
+    /*
+     * Cleanup
+     */
+    MyFree(pszRespLine);
+}
+
+/*!
+ * \brief Close the socket and file descriptors of an INET handle.
+ *
+ * \param   hInet [in] Handle returned by a previous call to InternetOpen.
+ *
+ * \return  -
+ */
+static void CloseDescriptors(HINET hInet)
+{
+    if (hInet != NULL)
+    {
+        /*
+         * Close the stream and the connection
+         */
+        if (hInet->ptStream != NULL)
+        {
+            (void)fclose(hInet->ptStream);
+            hInet->ptStream = NULL;
+        }
+        if (hInet->ptSocket != NULL)
+        {
+            (void)NutTcpCloseSocket(hInet->ptSocket);
+            hInet->ptSocket = NULL;
+        }
+    }
+}
+
+
+
+
+/*--------------------------------------------------------------------------*/
+/*  Global functions                                                        */
+/*--------------------------------------------------------------------------*/
+
+
+HINET InetOpen(void)
+{
+    HINET hInet = NULL;
+
+    ShowDebug();
+
+    hInet = (HINET)MyMalloc(sizeof(INET));
+    if (hInet != NULL)
+    {
+//        LogMsg_P(LOG_DEBUG, PSTR("Open %X"), hInet);
+
+        memset(hInet, 0, sizeof(INET));
+        hInet->tState = INET_STATE_IDLE;
+    }
+
+    return (hInet);
+}
+
+TError InetConnect(HINET hInet, CONST char *pszUrl, unsigned long ulRecvTimeout, unsigned int unMss, unsigned int unTcpRecvBufSize)
+{
+    TError tError = OK;
+
+    ShowDebug();
+
+    /*
+     * Parse the Url
+     */
+    if (tError == OK)
+    {
+        hInet->pszUrl = strdup(pszUrl);
+        if (hInet->pszUrl != NULL)
+        {
+            HttpParseUrl(hInet->pszUrl, &hInet->tUrlParts);
+        }
+        else
+        {
+            tError = INET_NOMEM;
+        }
+    }
+
+    if (tError == OK)
+    {
+        /* Store the connect parameters */
+        hInet->ulRecvTimeout = ulRecvTimeout;
+        hInet->unMss = unMss;
+        hInet->unTcpRecvBufSize = unTcpRecvBufSize;
+
+        /* Reset the problem counters */
+        memset(&hInet->tRetries, 0, sizeof(hInet->tRetries));
+
+        tError = Connect(hInet);
+    }
+
+    return (tError);
+}
+
+TError InetHttpOpenRequest(HINET hInet, CONST char *pszMethod, CONST char *pszPath, CONST char *pszAccept, unsigned short wOptions)
+{
+    const unsigned int cunReqBufSize = 256;
+    TError tError = OK;
+
+    ShowDebug();
+
+    if (hInet == NULL)
+    {
+        /* Bad argument */
+        tError = INET_NOMEM;
+    }
+
+    /*
+     * Create the request
+     */
+    if (tError == OK)
+    {
+        if (hInet->hRequest == NULL)
+        {
+            hInet->hRequest = (HINETREQ)MyMalloc(sizeof(INETREQ));
+            if (hInet->hRequest == NULL)
+            {
+                /* No memory */
+                tError = INET_NOMEM;
+            }
+            else
+            {
+                memset(hInet->hRequest, 0, sizeof(INETREQ));
+            }
+        }
+    }
+
+    /*
+     * Create the request buffer
+     */
+    if (tError == OK)
+    {
+        if (hInet->hRequest->pszRequest == NULL)
+        {
+            /* Allocate the request buffer */
+            hInet->hRequest->pszRequest = MyMalloc(cunReqBufSize);
+            if (hInet->hRequest->pszRequest == NULL)
+            {
+                /* No memory */
+                tError = INET_NOMEM;
+            }
+            else
+            {
+                hInet->hRequest->unRequestBufSize = cunReqBufSize;
+            }
+        }
+    }
+
+#ifdef INET_DEBUG
+    LogMsg_P(LOG_DEBUG, PSTR("hInet @%X"), hInet);
+    LogMsg_P(LOG_DEBUG, PSTR("hRequest @%X"), hInet->hRequest);
+#endif /* #ifdef INET_DEBUG */
+
+    /*
+     * Create the actual request
+     */
+    if (tError == OK)
+    {
+        int nResult = 0;
+
+        /* Store the requested options */
+        hInet->hRequest->wOptions = wOptions;
+
+        /* Create the request */
+        nResult = CreateRequest(hInet, pszMethod, pszPath, pszAccept);
+        if (nResult < 0)
+        {
+            tError = INET_NOMEM;
+        }
+    }
+
+    ShowDebug();
+
+    return (tError);
+}
+
+int InetHttpAddRequestHeaders(HINET hInet, CONST char *pszNewHeaders)
+{
+    int nResult = 0;
+
+    ShowDebug();
+
+    if ((hInet == NULL) || (hInet->hRequest == NULL))
+    {
+        /* Bad argument */
+        nResult = -1;
+    }
+
+    if (nResult >= 0)
+    {
+        nResult = BufferAddString(&hInet->hRequest->pszRequest,
+                                  &hInet->hRequest->unRequestBufSize,
+                                  &hInet->hRequest->unRequestInUse,
+                                  pszNewHeaders);
+    }
+
+#ifdef INET_DEBUG
+    LogMsg_P(LOG_DEBUG, PSTR("Request %u [%s]"), hInet->hRequest->unRequestInUse, hInet->hRequest->pszRequest);
+#endif /* #ifdef INET_DEBUG */
+    ShowDebug();
+
+    return (nResult);
+}
+
+TError InetHttpSendRequest(HINET hInet)
+{
+    TError tError = OK;
+    unsigned char byDone = 0;
+    unsigned char byRedirectCount = 0;
+    int nHeaderNumber;
+    long lResponseCode;
+    void *plResponseCode;
+    unsigned int unInfoSize;
+
+    /*
+     * Talk to the server and parse its reponse
+     */
+    while (byDone == 0)
+    {
+        int nResult = +1;
+        int nResponse = -1;
+
+        ShowDebug();
+
+        if (hInet->tState == INET_STATE_CLOSING)
+        {
+            tError = USER_ABORT;
+        }
+
+        if (tError == OK)
+        {
+            hInet->tState = INET_STATE_BUSY;
+        }
+
+        /*
+         * Send the request
+         */
+        if (tError == OK)
+        {
+            nResult = HttpSendRequest(hInet->ptStream, hInet->hRequest->pszRequest, hInet->hRequest->wHttpMode);
+            if (nResult < 0)
+            {
+                tError = INET_SEND_FAIL;
+            }
+            LogMsg_P(LOG_DEBUG, PSTR("Sent %d"), nResult);
+        }
+
+        /*
+         * We could have been asleep; Check if we still need to play
+         */
+        if (hInet->tState == INET_STATE_CLOSING)
+        {
+            tError = USER_ABORT;
+        }
+
+        /*
+         * Get the response
+         */
+        if (tError == OK)
+        {
+            ShowDebug();
+
+            GetHeaders(hInet);
+
+            nHeaderNumber = 0;
+            lResponseCode = 0;
+            plResponseCode = &lResponseCode;
+            unInfoSize = sizeof(lResponseCode);
+
+            nResult = InetHttpQueryInfo(hInet,
+                                        INET_HTTP_QUERY_STATUS_CODE | INET_HTTP_QUERY_MOD_NUMERIC,
+                                        &plResponseCode,
+                                        &unInfoSize,
+                                        &nHeaderNumber);
+            if (nResult > 0)
+            {
+                nResponse = lResponseCode;
+            }
+            else
+            {
+                /* Unable to process the response */
+                nResponse = -1;
+            }
+            ShowDebug();
+
+            /*
+             * We could have been asleep; Check if we still need to play
+             */
+            if (hInet->tState == INET_STATE_CLOSING)
+            {
+                tError = USER_ABORT;
+            }
+        }
+
+        if (tError == OK)
+        {
+            /*
+             * Check the response code
+             */
+            if (nResponse >= 100 && nResponse < 300)
+            {
+                /* Connected: we're done */
+                LogMsg_P(LOG_INFO, PSTR("Connect [%d]"), nResponse);
+                byDone = 1;
+            }
+            else if (nResponse >= 300 && nResponse < 400)
+            {
+                /* Redirect */
+                LogMsg_P(LOG_INFO, PSTR("Redirect [%d]"), nResponse);
+
+                tError = INET_REDIRECT;
+                /*
+                 * Check if we can retry
+                 */
+                if (++byRedirectCount >= MAX_REDIRECT)
+                {
+                    /* Too many redirects, stop */
+                    tError = INET_TOO_MANY_REDIRECTS;
+                }
+                else
+                {
+                    int nHeaderNumber = 0;
+                    char *pszLocation = NULL;
+                    void *ppszLocation = &pszLocation;
+                    unsigned int unSize = 0;
+
+                    /* Get size of the new location string */
+                    nResult = InetHttpQueryInfo(hInet,
+                                                INET_HTTP_QUERY_LOCATION,
+                                                ppszLocation,
+                                                &unSize,
+                                                &nHeaderNumber);
+                    /*
+                     * (Re)parse the newly received URL
+                     */
+                    if (nResult > 0)
+                    {
+                        MyFree(hInet->pszUrl);
+                        hInet->pszUrl = pszLocation;
+                        LogMsg_P(LOG_INFO, PSTR("To [%s]"), hInet->pszUrl);
+                        HttpParseUrl(hInet->pszUrl, &hInet->tUrlParts);
+
+                        nResult = CreateRequest(hInet, NULL, NULL, NULL);
+                    }
+
+                    if (nResult >= 0)
+                    {
+                        /*
+                         * Allow each server a maximum of problems :-)
+                         */
+                        memset(&hInet->tRetries, 0, sizeof(hInet->tRetries));
+                    }
+                    else
+                    {
+                        tError = INET_NOMEM;
+                    }
+                }
+            }
+            else if (nResponse == 401)
+            {
+                /*
+                 * If we did not try authentication, try again with authentication.
+                 * If we already tried using authentication or shouldn't use it, give up
+                 */
+                if (((hInet->hRequest->wHttpMode & HTTP_AUTH) != HTTP_AUTH) &&
+                    ((hInet->hRequest->wOptions & INET_FLAG_NO_AUTH) != INET_FLAG_NO_AUTH))
+                {
+                    hInet->hRequest->wHttpMode |= HTTP_AUTH;
+                    tError = INET_ACCESS_RESTRICTED;
+                }
+                else
+                {
+                    tError = INET_ACCESS_DENIED;
+                    LogMsg_P(LOG_CRIT, PSTR("Access denied"));
+                }
+            }
+            else
+            {
+                /* Bad response or timeout */
+                LogMsg_P(LOG_ERR, PSTR("Bad response [%d]"), nResponse);
+
+                tError = INET_BADRESPONSE;
+
+                if (++hInet->tRetries.byBadResponseCount >= MAX_BADRESPONSE)
+                {
+                    /* Too many badresponses, stop */
+                    tError = INET_TOO_MANY_BADRESPONSES;
+                }
+            }
+        }
+
+        if (tError != OK)
+        {
+            LogMsg_P(LOG_ERR, PSTR("Error [%d]"), tError);
+
+            /*
+             * Check if we need to try again
+             */
+            if ((tError > PLAYER_WARNINGS) && (tError < PLAYER_ERRORS))
+            {
+                LogMsg_P(LOG_INFO, PSTR("Retry"));
+
+                CloseDescriptors(hInet);
+
+                /*
+                 * Try again
+                 */
+                tError = Connect(hInet);
+
+                /*
+                 * Give other threads some time before
+                 * we try again
+                 */
+                NutSleep(300);
+            }
+            else
+            {
+                /* Errors, stop */
+                byDone = 1;
+            }
+        }
+    } /* end while */
+
+    ShowDebug();
+
+    hInet->tState = INET_STATE_IDLE;
+    return (tError);
+}
+
+
+int InetHttpQueryInfo(HINET hInet, unsigned short wInfoLevel, void **pInfo, unsigned int *punInfoSize, int *pnIndex)
+{
+    /* Status prefixes: */
+    static prog_char cszHttpVer_P[]         = "HTTP/";
+    static prog_char cszIcy_P[]             = "ICY";
+    /* HTTP headers: */
+    static prog_char cszLocation_P[]        = "Location:";
+    static prog_char cszContentLength_P[]   = "Content-Length:";
+    static prog_char cszContentType_P[]     = "Content-Type:";
+    /* ICY headers: */
+    static prog_char cszIcyMetaData_P[]     = "icy-metaint:";
+
+    int nResult = 0;
+    char *pszRespLine = hInet->hRequest->pszResponse;
+    char *pszStart = pszRespLine;
+    char *pszEnd = pszRespLine;
+    unsigned int unResultSize = 0;
+
+    if ((pInfo == NULL) || (punInfoSize == NULL) || (pszRespLine == NULL))
+    {
+        /* Bad argument */
+        nResult = -1;
+    }
+
+    while ((nResult == 0) && (pszEnd != NULL))
+    {
+        /* Skip empty lines and whitepace */
+        while (isspace(*pszRespLine))
+        {
+            pszRespLine++;
+        }
+
+        pszEnd = strchr(pszRespLine, '\r');
+        if (pszEnd != NULL)
+        {
+            if ((wInfoLevel & INET_HTTP_QUERY_STATUS_CODE) == INET_HTTP_QUERY_STATUS_CODE)
+            {
+                int iProcessed = 0;
+
+                /*
+                 * Check the response code in the first line
+                 */
+                if (strncasecmp_P(pszRespLine, cszHttpVer_P, sizeof(cszHttpVer_P)-1) == 0)
+                {
+                    hInet->hRequest->byProto = INET_PROTO_HTTP;
+                    iProcessed = sizeof(cszHttpVer_P)-1;
+                }
+                else if (strncasecmp_P(pszRespLine, cszIcy_P, sizeof(cszIcy_P)-1) == 0)
+                {
+                    hInet->hRequest->byProto = INET_PROTO_ICY;
+                    iProcessed = sizeof(cszIcy_P)-1;
+                }
+
+                if (iProcessed)
+                {
+                    nResult = 1;
+
+                    /* Skip version number */
+                    for (pszStart = pszRespLine + iProcessed; *pszStart != '\r' && *pszStart != ' '; pszStart++)
+                    {
+                        ;
+                    }
+                    if (*pszStart == '\r')
+                    {
+                        /* Could not find whitespace after the version number */
+                        nResult = -1;
+                    }
+                    else
+                    {
+                        /* Skip leading whitespace */
+                        for (; *pszStart == ' '; pszStart++)
+                        {
+                            ;
+                        }
+                        /* Strip trailing whitespace */
+                        for (; ((pszEnd > pszStart) && (*(pszEnd-1) == ' ')); pszEnd--)
+                        {
+                            ;
+                        }
+                    }
+                }
+            }
+            else if ((wInfoLevel & INET_HTTP_QUERY_LOCATION) == INET_HTTP_QUERY_LOCATION)
+            {
+                /*
+                 * Get the new location (redirect URL)
+                 */
+                if (strncasecmp_P(pszRespLine, cszLocation_P, sizeof(cszLocation_P)-1) == 0)
+                {
+                    nResult = 1;
+
+                    /* Skip leading whitespace */
+                    for (pszStart = pszRespLine + sizeof(cszLocation_P)-1; (*pszStart == ' '); pszStart++)
+                    {
+                        ;
+                    }
+
+                    /* Strip trailing whitespace */
+                    for (; ((pszEnd > pszStart) && (*(pszEnd-1) == ' ')); pszEnd--)
+                    {
+                        ;
+                    }
+                }
+            }
+            else if ((wInfoLevel & INET_HTTP_QUERY_CONTENT_LENGTH) == INET_HTTP_QUERY_CONTENT_LENGTH)
+            {
+                /*
+                 * Get the content-length
+                 */
+                if (strncasecmp_P(pszRespLine, cszContentLength_P, sizeof(cszContentLength_P)-1) == 0)
+                {
+                    nResult = 1;
+
+                    /* Skip leading whitespace */
+                    for (pszStart = pszRespLine + sizeof(cszContentLength_P)-1; (*pszStart == ' '); pszStart++)
+                    {
+                        ;
+                    }
+                }
+            }
+            else if ((wInfoLevel & INET_HTTP_QUERY_CONTENT_TYPE) == INET_HTTP_QUERY_CONTENT_TYPE)
+            {
+                /*
+                 * Get the content-type
+                 */
+                if (strncasecmp_P(pszRespLine, cszContentType_P, sizeof(cszContentType_P)-1) == 0)
+                {
+                    nResult = 1;
+
+                    /* Skip leading whitespace */
+                    for (pszStart = pszRespLine + sizeof(cszContentType_P)-1; (*pszStart == ' '); pszStart++)
+                    {
+                        ;
+                    }
+                }
+            }
+            else if ((wInfoLevel & INET_HTTP_QUERY_ICY_METADATA) == INET_HTTP_QUERY_ICY_METADATA)
+            {
+                /*
+                 * Get the content-type
+                 */
+                if (strncasecmp_P(pszRespLine, cszIcyMetaData_P, sizeof(cszIcyMetaData_P)-1) == 0)
+                {
+                    nResult = 1;
+
+                    /* Skip leading whitespace */
+                    for (pszStart = pszRespLine + sizeof(cszIcyMetaData_P)-1; (*pszStart == ' '); pszStart++)
+                    {
+                        ;
+                    }
+                }
+            }
+        }
+        /* Find next line */
+        pszRespLine = pszEnd;
+    } /* end while */
+
+    /*
+     * If we found what we are looking for, pass and optionally convert the resulting value to the caller
+     */
+    if (nResult > 0)
+    {
+        long lNumericValue = -1;
+
+        if ((wInfoLevel & INET_HTTP_QUERY_MOD_NUMERIC) == INET_HTTP_QUERY_MOD_NUMERIC)
+        {
+            unResultSize = sizeof(lNumericValue);
+        }
+        else
+        {
+            unResultSize = pszEnd - pszStart;
+            if (unResultSize > 0)
+            {
+                /* Correction so we can store the \0 */
+                unResultSize += 1;
+            }
+        }
+
+        /* Allocate a buffer if the caller did not */
+        if (*punInfoSize == 0)
+        {
+            *pInfo = MyMalloc(unResultSize);
+            *punInfoSize = unResultSize;
+        }
+
+        /* Copy if there is room */
+        if ((*punInfoSize >= unResultSize) &&
+            (*pInfo != NULL))
+        {
+            if ((wInfoLevel & INET_HTTP_QUERY_MOD_NUMERIC) == INET_HTTP_QUERY_MOD_NUMERIC)
+            {
+                long *plDest = *pInfo;
+                lNumericValue = strtol(pszStart, (char **) NULL, 0);
+                *plDest = lNumericValue;
+            }
+            else
+            {
+                if (unResultSize > 0)
+                {
+                    char *pszDest = *pInfo;
+                    memcpy(pszDest, pszStart, unResultSize-1);
+                    pszDest[unResultSize-1] = '\0';
+                }
+            }
+        }
+        else
+        {
+            /* No room to store the result */
+            nResult = -1;
+        }
+        *punInfoSize = unResultSize;
+    }
+    return (nResult);
+}
+
+/*\brief Mime types */
+static prog_char cszTypeAudio_P[]   = "audio/";
+static prog_char cszTypeText_P[]    = "text/";
+
+/*\brief Audio subtypes */
+static prog_char cszTypeM3u_P[]     = "x-mpegurl";
+static prog_char cszTypePls_P[]     = "x-scpls";
+//static prog_char cszSubtypeWma[]  = "x-ms-wma";
+//static prog_char cszSubtypeMp3[]  = "mpeg";
+//static prog_char cszSubtypeAac[]  = "aacp";
+
+/*\brief File extensions */
+static prog_char cszPlsExtension_P[]    = "pls";
+static prog_char cszM3uExtension_P[]    = "m3u";
+
+int InetGetMimeType(HINET hInet)
+{
+    int nResult = +1;
+    int nType = MIME_TYPE_UNKNOWN;
+    char *pszContentType = NULL;
+
+    /*
+     * Try to determine the filetype based on the content type header
+     */
+    int nHeaderNumber = 0;
+    void *ppszContentType = &pszContentType;
+    unsigned int unInfoSize = 0;
+
+    nResult = InetHttpQueryInfo(hInet,
+                                INET_HTTP_QUERY_CONTENT_TYPE,
+                                ppszContentType,
+                                &unInfoSize,
+                                &nHeaderNumber);
+    if (nResult > 0)
+    {
+        if (strncasecmp_P(pszContentType, cszTypeAudio_P, sizeof(cszTypeAudio_P)-1) == 0)
+        {
+            nType = MIME_TYPE_MP3;
+
+            char *pszSubType = pszContentType + sizeof(cszTypeAudio_P)-1;
+            if (strncasecmp_P(pszSubType, cszTypePls_P, sizeof(cszTypePls_P)-1) == 0)
+            {
+                 nType = MIME_TYPE_PLS;
+            }
+            else if (strncasecmp_P(pszSubType, cszTypeM3u_P, sizeof(cszTypeM3u_P)-1) == 0)
+            {
+                nType = MIME_TYPE_M3U;
+            }
+        }
+        else if (strncasecmp_P(pszContentType, cszTypeText_P, sizeof(cszTypeText_P)-1) == 0)
+        {
+            /* Assume the generic text type */
+            nType = MIME_TYPE_TEXT;
+        }
+    }
+    else
+    {
+        nType = MIME_TYPE_MP3;
+    }
+
+    /*
+     * Icecast servers serve only audio
+     */
+    if ((nType == MIME_TYPE_UNKNOWN) && (hInet->hRequest->byProto == INET_PROTO_ICY))
+    {
+        nType = MIME_TYPE_MP3;
+    }
+
+    /*
+     * If all else fails, use the extension of the path
+     */
+    if ((nType == MIME_TYPE_UNKNOWN) || (nType == MIME_TYPE_TEXT))
+    {
+        /* Assume the extension starts at the last '.' we can find */
+        char *szExtension = strrchr(hInet->tUrlParts.pszPath, '.');
+        if (szExtension != NULL)
+        {
+            szExtension++; /* Skip the . */
+            if (strncasecmp_P(szExtension, cszPlsExtension_P, sizeof(cszPlsExtension_P)-1) == 0)
+            {
+                nType = MIME_TYPE_PLS;
+            }
+            else if (strncasecmp_P(szExtension, cszM3uExtension_P, sizeof(cszM3uExtension_P)-1) == 0)
+            {
+                nType = MIME_TYPE_M3U;
+            }
+        }
+    }
+
+    LogMsg_P(LOG_INFO, PSTR("File type %d"), nType);
+
+    MyFree(pszContentType);
+
+    return (nType);
+}
+
+int InetRead(HINET hInet, char *pcBuf, unsigned int unBufSize)
+{
+    int nResult = -1;
+
+    if (hInet != NULL)
+    {
+        if (hInet->tState != INET_STATE_CLOSING)
+        {
+            hInet->tState = INET_STATE_BUSY;
+
+            //nResult = _read((int)hInet->ptSocket, pcBuf, unBufSize);
+            nResult = NutTcpReceive(hInet->ptSocket, pcBuf, unBufSize);
+            if (nResult < 0)
+            {
+                /*
+                 * Either an error occurred or the other side closed the connection (= EOF).
+                 */
+                LogMsg_P(LOG_INFO, PSTR("EOF %d"), NutTcpError(hInet->ptSocket));
+            }
+            else if (nResult == 0)
+            {
+                LogMsg_P(LOG_INFO, PSTR("Read Timeout"));
+            }
+        }
+
+        hInet->tState = INET_STATE_IDLE;
+    }
+    return (nResult);
+}
+
+int InetReadExact(HINET hInet, unsigned char *pbyBuf, unsigned int unBufSize)
+{
+    int nResult = +1;
+    unsigned int unBufInUse = 0;
+
+    /* Sanity check */
+    if ((hInet == NULL) || (pbyBuf == NULL))
+    {
+        return (-1);
+    }
+
+    /* Keep reading until we are done */
+    while ((nResult > 0) && (unBufSize-unBufInUse) > 0)
+    {
+        nResult = InetRead(hInet, (char*)&pbyBuf[unBufInUse], unBufSize-unBufInUse);
+        //LogMsg_P(LOG_DEBUG, PSTR("r %d,max %u"), nResult, unBufSize-unBufInUse);
+
+        if (nResult > 0)
+        {
+            unBufInUse += nResult;
+        }
+    }
+    return (unBufInUse);
+}
+
+int InetReadFile(HINET hInet, char **ppcBuf, unsigned int *punBufSize)
+{
+    int nResult = +1;
+    unsigned int unBufInUse = 0;
+    unsigned char byDoResize = 0;
+
+    /* Sanity check */
+    if ((ppcBuf == NULL) || (punBufSize == NULL))
+    {
+        nResult = -1;
+    }
+
+    /* If you did not provide a buffer, one will be provided for you */
+    if (nResult > 0)
+    {
+        if (*ppcBuf == NULL)
+        {
+            byDoResize = 1;
+            *punBufSize = 0;
+        }
+    }
+
+    /* Keep reading until we are done */
+    while (nResult > 0)
+    {
+        if (byDoResize)
+        {
+            nResult = BufferMakeRoom((char**)ppcBuf, punBufSize, unBufInUse, 100);
+            //LogMsg_P(LOG_DEBUG, PSTR("r %d,size %u, %x"), nResult, *punBufSize, &(*ppcBuf)[unBufInUse]);
+        }
+
+        if (nResult >= 0)
+        {
+            nResult = InetRead(hInet, (char*)&(*ppcBuf)[unBufInUse], *punBufSize-unBufInUse);
+            //LogMsg_P(LOG_DEBUG, PSTR("r %d,max %u"), nResult, *punBufSize-unBufInUse);
+        }
+
+        if (nResult > 0)
+        {
+            unBufInUse += nResult;
+        }
+        else
+        {
+            /*
+             * We have a eof/disconnect (-1), or a read timeout (0)
+             */
+        }
+    }
+
+    if (unBufInUse == 0)
+    {
+        LogMsg_P(LOG_WARNING, PSTR("No data"));
+    }
+    return (unBufInUse);
+}
+
+HINET InetClose(HINET hInet)
+{
+//    LogMsg_P(LOG_DEBUG, PSTR("Close %X %d"), hInet, hInet->tState);
+
+    if (hInet != NULL)
+    {
+        if (hInet->tState != INET_STATE_IDLE)
+        {
+            hInet->tState = INET_STATE_CLOSING;
+
+            /*
+             * Wait for the close to be handled
+             */
+            while (hInet->tState != INET_STATE_IDLE)
+            {
+                unsigned int nCount = 0;
+
+                NutSleep(100);
+
+                /* After 10 seconds */
+                if (++nCount == 100)
+                {
+                    LogMsg_P(LOG_EMERG, PSTR("Close failed"));
+                    break;
+                }
+            }
+        }
+
+        CloseDescriptors(hInet);
+        MyFree(hInet->pszUrl);
+        ShowDebug();
+
+        /*
+         * Deallocate the request struct
+         */
+        if (hInet->hRequest != NULL)
+        {
+            MyFree(hInet->hRequest->pszRequest);
+            ShowDebug();
+            MyFree(hInet->hRequest->pszResponse);
+            ShowDebug();
+            MyFree(hInet->hRequest);
+            ShowDebug();
+        }
+        MyFree(hInet);
+    }
+    ShowDebug();
+    return (hInet);
+}
+

+ 326 - 0
keyboard.c

@@ -0,0 +1,326 @@
+/*! \file
+ * keyboard.c contains the low-level keyboard scan and the
+ * interfacing with NutOS (signalling)
+ *  Copyright STREAMIT, 2010.
+ *  \version 1.0
+ *  \date 26 september 2003
+ */
+
+
+#define LOG_MODULE  LOG_KEYBOARD_MODULE
+
+#include <sys/atom.h>
+#include <sys/event.h>
+
+//#pragma text:appcode
+
+#include "keyboard.h"
+#include "portio.h"
+#include "system.h"
+
+/*-------------------------------------------------------------------------*/
+/* local defines                                                          */
+/*-------------------------------------------------------------------------*/
+/*
+ *  definition of raw keys as found in keyboardscan
+ *  Note that these 16-bit values are remapped before
+ *  the application uses them
+ */
+#define RAW_KEY_01         0xFFFB
+#define RAW_KEY_02         0xFFFD
+#define RAW_KEY_03         0xFF7F
+#define RAW_KEY_04         0xFFF7
+#define RAW_KEY_05         0xFFFE
+#define RAW_KEY_ALT        0xFFBF
+
+#define RAW_KEY_ESC        0xFFEF
+#define RAW_KEY_UP         0xF7FF
+#define RAW_KEY_OK         0xFFDF
+#define RAW_KEY_LEFT       0xFEFF
+#define RAW_KEY_DOWN       0xFBFF
+#define RAW_KEY_RIGHT      0xFDFF
+
+#define RAW_KEY_POWER      0xEFFF
+
+#define RAW_KEY_SETUP      0xFFCF       // combine 'ESCAPE' (0xFFEF') with 'OK' (0xFFDF)
+
+/*-------------------------------------------------------------------------*/
+/* local variable definitions                                              */
+/*-------------------------------------------------------------------------*/
+static HANDLE  hKBEvent;
+static u_short KeyFound;        // use short for 4 nibbles (4 colums)
+static u_char KeyBuffer[KB_BUFFER_SIZE];
+static u_short HoldCounter;
+static u_char KbState;
+static u_char KeyRepeatArray[KEY_NROF_KEYS];
+
+/*-------------------------------------------------------------------------*/
+/* local routines (prototyping)                                            */
+/*-------------------------------------------------------------------------*/
+static void KbClearEvent(HANDLE*);
+static u_char KbRemapKey(u_short LongKey);
+
+
+/*!
+ * \addtogroup Keyboard
+ */
+
+/*@{*/
+
+/*-------------------------------------------------------------------------*/
+/*                         start of code                                   */
+/*-------------------------------------------------------------------------*/
+
+
+
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Clear the eventbuffer of this module
+ *
+ * This routine is called during module initialization.
+ *
+ * \param *pEvent pointer to the event queue
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+static void KbClearEvent(HANDLE *pEvent)
+{
+    NutEnterCritical();
+
+    *pEvent = 0;
+
+    NutExitCritical();
+}
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Low-level keyboard scan
+ *
+ * KbScan is called each 4.44 msec from MainBeat interrupt
+ * Remember: pressed key gives a '0' on KB_IN_READ
+ *
+ * After each keyboard-scan, check for a valid MMCard
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+void KbScan()
+{
+    u_char KeyNibble0, KeyNibble1, KeyNibble2, KeyNibble3;
+
+    /*
+     *  we must scan 4 colums, 2 in PORTG and 2 in PORTD
+     */
+
+#ifndef USE_JTAG
+    // scan keys in COL 0
+    cbi (KB_OUT_WRITE_A, KB_COL_0);
+    asm("nop\n\tnop");                    // small delay
+    KeyNibble0 = inp(KB_IN_READ) & KB_ROW_MASK;
+    sbi (KB_OUT_WRITE_A, KB_COL_0);
+
+    // scan keys in COL 1
+    cbi (KB_OUT_WRITE_A, KB_COL_1);
+    asm("nop\n\tnop");                    // small delay
+    KeyNibble1 = inp(KB_IN_READ) & KB_ROW_MASK;
+    sbi (KB_OUT_WRITE_A, KB_COL_1);
+
+    // scan keys in COL 2
+    cbi (KB_OUT_WRITE_B, KB_COL_2);
+    asm("nop\n\tnop");                    // small delay
+    KeyNibble2 = inp(KB_IN_READ) & KB_ROW_MASK;
+    sbi (KB_OUT_WRITE_B, KB_COL_2);
+
+    // scan keys in COL 3
+    cbi (KB_OUT_WRITE_B, KB_COL_3);
+    asm("nop\n\tnop");                    // small delay
+    KeyNibble3 = inp(KB_IN_READ) & KB_ROW_MASK;
+    sbi (KB_OUT_WRITE_B, KB_COL_3);
+
+
+    /*
+     *  we want to detect exactly 1 key in exactly 1 colom
+     *  exception is the combination of VOLMIN & POWER (-> SETUP)
+     *  meaning: Keynibble0==[0000 1011] (KEY_VOLMIN) & KeyNibble1==[0111 0000] (KEY_POWER)
+     */
+
+    /*
+     *  put all 4 seperate nibbles in place in 'KeyFound'
+     *
+     *  KeyNibble0 on b3...b0  (col 0)
+     *  KeyNibble1 on b7...b4  (col 1)
+     *  KeyNibble2 on b11..b8  (col 2)
+     *  KeyNibble3 on b15..b12 (col 3)
+     */
+
+    KeyFound =  ((KeyNibble0>>4) & 0x000F);     // b7..b4 in 'KeyNibble0' to b3...b0  in 'KeyFound' >> shift 4 right
+    KeyFound |= (KeyNibble1 & 0x00F0);          // b7..b4 in 'KeyNibble1' to b7...b4  in 'KeyFound' -- do nothing
+    KeyFound |= ((KeyNibble2<<4) & 0x0F00);     // b7..b4 in 'KeyNibble2' to b11..b8  in 'KeyFound' << shift 4 left
+    KeyFound |= ((KeyNibble3<<8) & 0xF000);     // b7..b4 in 'KeyNibble3' to b15..b12 in 'KeyFound' << shift 8 left
+
+
+#endif  // USE_JTAG
+
+}
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Remap the 16-bit value for the active key to an 8-bit value
+ *
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+static u_char KbRemapKey(u_short LongKey)
+{
+    switch (LongKey)
+    {
+        case RAW_KEY_01:        return(KEY_01);
+        case RAW_KEY_02:        return(KEY_02);
+        case RAW_KEY_03:        return(KEY_03);
+        case RAW_KEY_04:        return(KEY_04);
+        case RAW_KEY_05:        return(KEY_05);
+        case RAW_KEY_ALT:       return(KEY_ALT);
+
+        case RAW_KEY_ESC:       return(KEY_ESC);
+        case RAW_KEY_UP:        return(KEY_UP);
+        case RAW_KEY_OK:        return(KEY_OK);
+        case RAW_KEY_LEFT:      return(KEY_LEFT);
+        case RAW_KEY_DOWN:      return(KEY_DOWN);
+        case RAW_KEY_RIGHT:     return(KEY_RIGHT);
+
+        case RAW_KEY_POWER:     return(KEY_POWER);
+        case RAW_KEY_SETUP:     return(KEY_SETUP);      // combined key
+
+        default:                return(KEY_UNDEFINED);
+    }
+}
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Return the repeating property for this key
+ *
+ * \return 'TRUE' in case the key was repeating, 'FALSE' if not
+ *
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+static u_char KbKeyIsRepeating(u_short Key)
+{
+    return(KeyRepeatArray[KbRemapKey(Key)]==KEY_REPEAT);
+}
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief set the property of this key to repeating or not-repeating
+ *
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+void KbSetKeyRepeating(u_char Key, u_char Property)
+{
+    // check arguments
+    if (((Property==KEY_REPEAT) || (Property==KEY_NO_REPEAT)) && (Key < KEY_NROF_KEYS))
+    {
+        KeyRepeatArray[Key]=Property;
+    }
+}
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Wait until an event was pushed on the eventqueue for this module
+ *
+ * This routine provides the event interface for other Luks-modules
+ *
+ * \param dwTimeout time in milisecs that this routine should wait before
+ * it will return with KB_ERROR
+ *
+ * \return KB_OK in case an event was found
+ * \return KB_ERROR in case no event was found (return due to timeout)
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+int KbWaitForKeyEvent(u_long dwTimeout)
+{
+
+    int nError = KB_OK;
+    int nTimeout;
+
+    nTimeout = NutEventWait(&hKBEvent, dwTimeout);
+    if (nTimeout == -1)
+    {
+        nError = KB_ERROR;
+    }
+
+    return(nError);
+}
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Return the databyte that was receeived in the IR-stream
+ *
+ * In case a valid key is found in the keyboard scan, the key-code is
+ * stored in the keyboardbuffer. This routine returns the first available
+ * valid key in this buffer
+
+ * \return the keycode that was found by the keyboard scan
+ *
+ * \todo implement a key-buffer for this routine
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+u_char KbGetKey()
+{
+    return(KeyBuffer[0]);
+}
+
+/*!
+ * \brief inject a virtual key into the system
+ *
+ */
+void KbInjectKey(u_char VirtualKey)
+{
+    KeyBuffer[0]=VirtualKey;
+    NutEventPostFromIrq(&hKBEvent);   // 'valid key' detected -> generate Event
+}
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Initialise the Keyboard module
+ *
+ *
+ * - initialise the keyboard read- and write port
+ * - flush the keyboardbuffer
+ * - flush the eventqueue for this module
+ *
+ * \note PORTF uses internal pull-ups. That's why a '1' is read
+ * when no key is pressed. Use negative logic to detect keys.
+ * So default state of the colums is '1'
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+void KbInit()
+{
+    u_char i;
+
+    sbi (KB_OUT_WRITE_A, KB_COL_0);
+    sbi (KB_OUT_WRITE_A, KB_COL_1);
+    sbi (KB_OUT_WRITE_B, KB_COL_2);
+    sbi (KB_OUT_WRITE_B, KB_COL_3);
+
+    KbState = KB_IDLE;
+    KeyFound = KEY_NO_KEY;
+
+    KbClearEvent(&hKBEvent);
+
+    for (i=0;i<KB_BUFFER_SIZE;++i)
+    {
+        KeyBuffer[i] = (u_char)KEY_NO_KEY;
+    }
+
+    for (i=0; i<KEY_NROF_KEYS; ++i)
+    {
+        KeyRepeatArray[i]=KEY_NO_REPEAT;
+    }
+
+    HoldCounter=0;
+
+    // arrow keys are repeating keys by default
+    KbSetKeyRepeating(KEY_UP, KEY_REPEAT);
+    KbSetKeyRepeating(KEY_DOWN, KEY_REPEAT);
+    KbSetKeyRepeating(KEY_LEFT, KEY_REPEAT);
+    KbSetKeyRepeating(KEY_RIGHT, KEY_REPEAT);
+}
+/* ---------- end of module ------------------------------------------------ */
+
+/*@}*/

+ 111 - 0
led.c

@@ -0,0 +1,111 @@
+/*! \file
+ * Led.c contains all interface- and low-level routines to
+ * control the LED
+ *  Copyright STREAMIT BV 2010
+ *  \version 1.0
+ *  \date 26 september 2003
+ */
+
+
+#define LOG_MODULE  LOG_LED_MODULE
+
+#include <sys/timer.h>
+#include <sys/thread.h>
+#include <sys/event.h>
+
+#include "system.h"
+#include "portio.h"
+#include "led.h"
+
+static u_char LedStatus;
+
+/*-------------------------------------------------------------------------*/
+/* local variable definitions                                              */
+/*-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/* local routines (prototyping)                                            */
+/*-------------------------------------------------------------------------*/
+
+
+/*!
+ * \addtogroup Led
+ */
+
+/*@{*/
+
+/*-------------------------------------------------------------------------*/
+/*                         start of code                                   */
+/*-------------------------------------------------------------------------*/
+
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Interface routine to control the SIR LED
+ *
+ * Using this routine, the LED can be set to 'ON', 'OFF" or it's state can
+ * be toggeld
+ *
+ * \param LedMode can be eiter 'ON', 'OFF' or 'TOGGLE'
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/************************************************************/
+/*  Control the LED: ON, OFF or TOGGLE                      */
+/*                   in case Lucas is 'OFF' -> return       */
+/************************************************************/
+void LedControl(u_char LedMode)
+{
+   switch (LedMode)
+    {
+        case LED_OFF:
+        case LED_POWER_OFF:
+        case LED_FLASH_OFF:
+            cbi (LED_OUT_WRITE, LED_PIN);
+            LedStatus = LedMode;
+            break;
+        case LED_ON:
+        case LED_POWER_ON:
+        case LED_FLASH_ON:
+            sbi (LED_OUT_WRITE, LED_PIN);
+            LedStatus = LedMode;
+            break;
+        case LED_TOGGLE:
+            if (LedStatus == LED_ON)
+            {
+                cbi (LED_OUT_WRITE, LED_PIN);
+                LedStatus = LED_OFF;
+            }
+            else
+            {
+                sbi (LED_OUT_WRITE, LED_PIN);
+                LedStatus = LED_ON;
+            }
+            break;
+    }
+}
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Initialise the Led module
+ *
+ * clear LED and update LedStatus
+ *
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+void LedInit()
+{
+    cbi (LED_OUT_WRITE, LED_PIN);
+    LedStatus = LED_OFF;
+}
+
+/*!
+ * \brief Get LedStatus for external use
+ *
+ */
+u_char LedGetStatus()
+{
+  return(LedStatus);
+}
+
+/* ---------- end of module ------------------------------------------------ */
+
+/*@}*/

+ 371 - 0
log.c

@@ -0,0 +1,371 @@
+/*
+ *  Copyright STREAMIT BV, 2010.
+ *
+ *  Project             : SIR
+ *  Module              : Log
+ *  File name  $Workfile:   Log.c  $
+ *       Last Save $Date:   2003/08/16 15:01:19  $
+ *             $Revision:   0.1  $
+ *  Creation Date       : 2003/08/16 15:01:19
+ *
+ *  Description         : Keeps track of log messages
+ *                        As an initial implementation this module
+ *                        outputs messages to the serial port and uses
+ *                        no buffering. (As a result logging delays execution
+ *                        as long as the serial write takes)
+ *                        At a later stage this module will have a logging
+ *                        queue. This will not have much impact on execution
+ *                        time. It will output messages to either a serial port
+ *                        or telnet client.
+ *
+ */
+
+/*--------------------------------------------------------------------------*/
+/*  Include files                                                           */
+/*--------------------------------------------------------------------------*/
+#define LOG_MODULE  LOG_LOG_MODULE
+
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/thread.h>
+#include <sys/heap.h>
+#include <sys/device.h>
+#include <sys/osdebug.h>
+
+//#pragma text:appcode
+
+#include "uart0driver.h"
+//#include "settings.h"
+#include "log.h"
+
+/*--------------------------------------------------------------------------*/
+/*  Constant definitions                                                    */
+/*--------------------------------------------------------------------------*/
+/*!\brief Max length of address */
+#define MAX_OFFSET_LEN  8
+
+/*!\brief Max byte values printed on a line */
+#define BYTES_PER_LINE  16
+
+/*!\brief Max number of characters hex dump takes: 2 digits plus trailing blank */
+#define HEX_DUMP_LEN    (BYTES_PER_LINE*3)
+
+/*!\brief Number of characters hex dump + ascii take: 3 chars, 2 blanks, 1 char */
+#define DATA_DUMP_LEN   (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
+
+/*!\brief Number of characters per line: address, 2 blanks, data dump */
+#define MAX_LINE_LEN    (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
+
+#define LEVEL_MASK      0x07        // b0...b2
+#define NAME_MASK       0xF8        // b3..b7
+
+
+/*--------------------------------------------------------------------------*/
+/*  Type declarations                                                       */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Local variables                                                         */
+/*--------------------------------------------------------------------------*/
+/*!\brief The current log level */
+static TLogLevel    g_tLevel;
+
+/*!\brief Stream to output the log data to */
+static FILE         *g_tStream;
+
+/*--------------------------------------------------------------------------*/
+/*  Global variables                                                        */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Local functions                                                         */
+/*--------------------------------------------------------------------------*/
+
+/*!
+ * \brief Return the prefix-level for the given log level.
+ *
+ * \param   tLevel [in] The log level.
+ *
+ * \return  Pointer to a string in program space.
+ */
+static PGM_P LogPrefixLevel_P(TLogLevel tLevel)
+{
+
+    switch (tLevel)
+    {
+        case LOG_EMERG_LEV   :return(PSTR("\n#Emerg "));
+        case LOG_ALERT_LEV   :return(PSTR("\n#Alert "));
+        case LOG_CRIT_LEV    :return(PSTR("\n#Crit  "));
+        case LOG_ERR_LEV     :return(PSTR("\n#Err   "));
+        case LOG_WARNING_LEV :return(PSTR("\n#Warn  "));
+        case LOG_NOTICE_LEV  :return(PSTR("\n#Notic "));
+        case LOG_INFO_LEV    :return(PSTR("\n#Info  "));
+        case LOG_DEBUG_LEV   :return(PSTR("\n#Debug "));
+        default          :return(PSTR("\n"));
+    }
+}
+
+/*!
+ * \brief Return the prefix-name for the given log module.
+ *
+ * \param   tLevel [in] The log module.
+ *
+ * \return  Pointer to a string in program space.
+ */
+static PGM_P LogPrefixName_P(TLogLevel tLevel)
+{
+
+    switch (tLevel)
+    {
+        case LOG_AUDIO_MODULE        :return(PSTR("AU: "));
+        case LOG_CHANNEL_MODULE      :return(PSTR("CH: "));
+        case LOG_COMAND_MODULE       :return(PSTR("CM: "));
+        case LOG_DISPLAY_MODULE      :return(PSTR("DP: "));
+        case LOG_FAT_MODULE          :return(PSTR("FA: "));
+        case LOG_FLASH_MODULE        :return(PSTR("FL: "));
+        case LOG_HTTP_MODULE         :return(PSTR("HT: "));
+        case LOG_INET_MODULE         :return(PSTR("IN: "));
+        case LOG_KEYBOARD_MODULE     :return(PSTR("KB: "));
+        case LOG_LED_MODULE          :return(PSTR("LE: "));
+        case LOG_LOG_MODULE          :return(PSTR("LG: "));
+        case LOG_MAIN_MODULE         :return(PSTR("SY: "));
+        case LOG_MENU_MODULE         :return(PSTR("ME: "));
+        case LOG_MMC_MODULE          :return(PSTR("MM: "));
+        case LOG_MMCDRV_MODULE       :return(PSTR("MD: "));
+        case LOG_PARSE_MODULE        :return(PSTR("PA: "));
+        case LOG_PLAYER_MODULE       :return(PSTR("PL: "));
+        case LOG_REMCON_MODULE       :return(PSTR("RC: "));
+        case LOG_RTC_MODULE          :return(PSTR("RT: "));
+        case LOG_SELFTEST_MODULE     :return(PSTR("ST: "));
+        case LOG_SESSION_MODULE      :return(PSTR("SE: "));
+        case LOG_SETTINGS_MODULE     :return(PSTR("SG: "));
+        case LOG_SPIDRV_MODULE       :return(PSTR("SP: "));
+        case LOG_STREAMER_MODULE     :return(PSTR("SR: "));
+        case LOG_UART0DRIVER_MODULE  :return(PSTR("UA: "));
+        case LOG_UPDATE_MODULE       :return(PSTR("UD: "));
+        case LOG_UTIL_MODULE         :return(PSTR("UT: "));
+        case LOG_VERSION_MODULE      :return(PSTR("VE: "));
+        case LOG_VS10XX_MODULE       :return(PSTR("VS: "));
+        case LOG_WATCHDOG_MODULE     :return(PSTR("WD: "));
+        default          :return(PSTR("?? <DMK> "));
+    }
+}
+
+/*--------------------------------------------------------------------------*/
+/*  Global functions                                                        */
+/*--------------------------------------------------------------------------*/
+
+/*!
+ * \brief Initialises this module
+ *
+ * \param   -
+ *
+ * \return  -
+ */
+void LogInit(void)
+{
+    /* Set default level */
+    g_tLevel = LOG_DEBUG_LEV;
+
+    LogOpen();
+}
+
+/*!
+ * \brief Opens the module for use.
+ *
+ * \param   -
+ *
+ * \return  -
+ */
+void LogOpen(void)
+{
+    /* Associate our stream with a device */
+    g_tStream = Uart0DriverGetStream();
+}
+
+/*!
+ * \brief Closes the module.
+ *
+ * All interface functions from this module will result in void
+ * operations.
+ *
+ * \param   -
+ *
+ * \return  -
+ */
+void LogClose(void)
+{
+    FILE *tPrevStream = g_tStream;
+
+    /* Don't allow adding of new output. */
+    g_tStream = NULL;
+
+    /* Finish all current output. */
+    fflush(tPrevStream);
+}
+
+/*!
+ * \brief Log a message to the log medium using a fixed string.
+ *
+ * The fixed string must reside in program space. It is parsed
+ * using the rules of (s)printf.
+ *
+ * \param tLevel priority level of the message.
+ * \param szMsg  format string of the message.
+ * \param ...    arguments to the format string.
+ */
+void LogMsg_P(TLogLevel tLevel, PGM_P szMsg, ...)
+{
+    va_list ap;
+
+    if (g_tStream)
+    {
+        /* Log the string if the message is more important than the current level */
+        if ((tLevel&LEVEL_MASK) <= g_tLevel)
+        {
+            fputs_P(LogPrefixLevel_P(tLevel&LEVEL_MASK), g_tStream);
+            fputs_P(LogPrefixName_P(tLevel&NAME_MASK), g_tStream);
+            va_start(ap, szMsg);
+            vfprintf_P(g_tStream, szMsg, ap);
+            va_end(ap);
+        }
+    }
+}
+
+void LogChar_P(const char bChar)
+{
+    if (g_tStream)
+    {
+        fputc(bChar, g_tStream);
+    }
+}
+
+/*!
+ * \brief Set the priority level
+ *
+ * \param tNewLevel New priority level
+ *
+ * \return The previous priority level
+ */
+TLogLevel LogSetLevel(TLogLevel tNewLevel)
+{
+    TLogLevel tPrevLevel = g_tLevel;
+
+    if (tNewLevel <= LOG_DEBUG_LEV)
+    {
+        g_tLevel = tNewLevel;
+    }
+    return(tPrevLevel);
+}
+
+/*!
+ * \brief Print a block of memory
+ *
+ * Prints out 16 bytes of data per line
+ * Every line starts with the address (offset),
+ * the data in hex and that same data in ascii.
+ *
+ * \param tStream The stream to print to
+ * \param cp The data
+ * \param length The length of the data
+ *
+ * \return none
+ */
+void HexDump(FILE *tStream, CONST u_char *cp, size_t length)
+{
+    register unsigned int address, i, hex_pos, ascii_pos, l;
+    unsigned int address_len;
+    unsigned char c;
+    char line[MAX_LINE_LEN + 1];
+    static CONST char binhex[16] =
+    {
+        '0', '1', '2', '3', '4', '5', '6', '7',
+        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    if (tStream != NULL)
+    {
+        /*
+         * How many digits do we need for the address?
+         * We use at least 4 digits or more as needed
+         */
+        if (((length - 1) & 0xF0000000) != 0)
+        {
+            address_len = 8;
+        }
+        else if (((length - 1) & 0x0F000000) != 0)
+        {
+            address_len = 7;
+        }
+        else if (((length - 1) & 0x00F00000) != 0)
+        {
+            address_len = 6;
+        }
+        else if (((length - 1) & 0x000F0000) != 0)
+        {
+            address_len = 5;
+        }
+        else
+        {
+            address_len = 4;
+        }
+
+        address = 0;
+        i = 0;
+        hex_pos = 0;
+        ascii_pos = 0;
+        while (i < length)
+        {
+            if ((i & 15) == 0)
+            {
+                /*
+                 * Start of a new line.
+                 */
+                memset(line, ' ', sizeof(line));
+                hex_pos = 0;
+                ascii_pos = 0;
+                l = address_len;
+                do
+                {
+                    l--;
+                    c = (address >> (l*4)) & 0xF;
+                    line[hex_pos++] = binhex[c];
+                } while (l != 0);
+
+                /* 2 spaces */
+                hex_pos += 2;
+
+                /*
+                 * Offset in line of ASCII dump.
+                 */
+                ascii_pos = hex_pos + HEX_DUMP_LEN + 2;
+            }
+            c = *cp++;
+
+            /* Dump the hex value */
+            line[hex_pos++] = binhex[c >> 4];
+            line[hex_pos++] = binhex[c & 0xF];
+            hex_pos++;
+
+            /* Print the ascii value */
+            line[ascii_pos++] = (c >= ' ' && c < 127) ? c : '.';
+
+            i++;
+            if ((i & 15) == 0 || i == length)
+            {
+                /*
+                 * We'll be starting a new line, or
+                 * we're finished printing this buffer;
+                 * dump out the line we've constructed,
+                 * and advance the offset.
+                 */
+                line[ascii_pos] = '\0';
+                fputs(line, tStream);
+                fputc('\n', tStream);
+                address += BYTES_PER_LINE;
+            }
+        }
+    }
+}
+

+ 324 - 0
main.c

@@ -0,0 +1,324 @@
+/*! \mainpage SIR firmware documentation
+ *
+ *  \section intro Introduction
+ *  A collection of HTML-files has been generated using the documentation in the sourcefiles to
+ *  allow the developer to browse through the technical documentation of this project.
+ *  \par
+ *  \note these HTML files are automatically generated (using DoxyGen) and all modifications in the
+ *  documentation should be done via the sourcefiles.
+ */
+
+/*! \file
+ *  COPYRIGHT (C) STREAMIT BV 2010
+ *  \date 19 december 2003
+ */
+ 
+ 
+ 
+
+#define LOG_MODULE  LOG_MAIN_MODULE
+
+/*--------------------------------------------------------------------------*/
+/*  Include files                                                           */
+/*--------------------------------------------------------------------------*/
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/thread.h>
+#include <sys/timer.h>
+#include <sys/version.h>
+#include <dev/irqreg.h>
+
+#include "system.h"
+#include "portio.h"
+#include "display.h"
+#include "remcon.h"
+#include "keyboard.h"
+#include "led.h"
+#include "log.h"
+#include "uart0driver.h"
+#include "mmc.h"
+#include "watchdog.h"
+#include "flash.h"
+#include "spidrv.h"
+
+#include <time.h>
+#include "rtc.h"
+
+
+/*-------------------------------------------------------------------------*/
+/* global variable definitions                                             */
+/*-------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/* local variable definitions                                              */
+/*-------------------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------------------*/
+/* local routines (prototyping)                                            */
+/*-------------------------------------------------------------------------*/
+static void SysMainBeatInterrupt(void*);
+static void SysControlMainBeat(u_char);
+
+/*-------------------------------------------------------------------------*/
+/* Stack check variables placed in .noinit section                         */
+/*-------------------------------------------------------------------------*/
+
+/*!
+ * \addtogroup System
+ */
+
+/*@{*/
+
+
+/*-------------------------------------------------------------------------*/
+/*                         start of code                                   */
+/*-------------------------------------------------------------------------*/
+
+
+/* ����������������������������������������������������������������������� */
+/*!
+ * \brief ISR MainBeat Timer Interrupt (Timer 2 for Mega128, Timer 0 for Mega256).
+ *
+ * This routine is automatically called during system
+ * initialization.
+ *
+ * resolution of this Timer ISR is 4,448 msecs
+ *
+ * \param *p not used (might be used to pass parms from the ISR)
+ */
+/* ����������������������������������������������������������������������� */
+static void SysMainBeatInterrupt(void *p)
+{
+
+    /*
+     *  scan for valid keys AND check if a MMCard is inserted or removed
+     */
+    KbScan();
+    CardCheckCard();
+}
+
+
+/* ����������������������������������������������������������������������� */
+/*!
+ * \brief Initialise Digital IO
+ *  init inputs to '0', outputs to '1' (DDRxn='0' or '1')
+ *
+ *  Pull-ups are enabled when the pin is set to input (DDRxn='0') and then a '1'
+ *  is written to the pin (PORTxn='1')
+ */
+/* ����������������������������������������������������������������������� */
+void SysInitIO(void)
+{
+    /*
+     *  Port B:     VS1011, MMC CS/WP, SPI
+     *  output:     all, except b3 (SPI Master In)
+     *  input:      SPI Master In
+     *  pull-up:    none
+     */
+    outp(0xF7, DDRB);
+
+    /*
+     *  Port C:     Address bus
+     */
+
+    /*
+     *  Port D:     LCD_data, Keypad Col 2 & Col 3, SDA & SCL (TWI)
+     *  output:     Keyboard colums 2 & 3
+     *  input:      LCD_data, SDA, SCL (TWI)
+     *  pull-up:    LCD_data, SDA & SCL
+     */
+    outp(0x0C, DDRD);
+    outp((inp(PORTD) & 0x0C) | 0xF3, PORTD);
+
+    /*
+     *  Port E:     CS Flash, VS1011 (DREQ), RTL8019, LCD BL/Enable, IR, USB Rx/Tx
+     *  output:     CS Flash, LCD BL/Enable, USB Tx
+     *  input:      VS1011 (DREQ), RTL8019, IR
+     *  pull-up:    USB Rx
+     */
+    outp(0x8E, DDRE);
+    outp((inp(PORTE) & 0x8E) | 0x01, PORTE);
+
+    /*
+     *  Port F:     Keyboard_Rows, JTAG-connector, LED, LCD RS/RW, MCC-detect
+     *  output:     LCD RS/RW, LED
+     *  input:      Keyboard_Rows, MCC-detect
+     *  pull-up:    Keyboard_Rows, MCC-detect
+     *  note:       Key row 0 & 1 are shared with JTAG TCK/TMS. Cannot be used concurrent
+     */
+#ifndef USE_JTAG
+    sbi(JTAG_REG, JTD); // disable JTAG interface to be able to use all key-rows
+    sbi(JTAG_REG, JTD); // do it 2 times - according to requirements ATMEGA128 datasheet: see page 256
+#endif //USE_JTAG
+
+    outp(0x0E, DDRF);
+    outp((inp(PORTF) & 0x0E) | 0xF1, PORTF);
+
+    /*
+     *  Port G:     Keyboard_cols, Bus_control
+     *  output:     Keyboard_cols
+     *  input:      Bus Control (internal control)
+     *  pull-up:    none
+     */
+    outp(0x18, DDRG);
+}
+
+/* ����������������������������������������������������������������������� */
+/*!
+ * \brief Starts or stops the 4.44 msec mainbeat of the system
+ * \param OnOff indicates if the mainbeat needs to start or to stop
+ */
+/* ����������������������������������������������������������������������� */
+static void SysControlMainBeat(u_char OnOff)
+{
+    int nError = 0;
+
+    if (OnOff==ON)
+    {
+        nError = NutRegisterIrqHandler(&OVERFLOW_SIGNAL, SysMainBeatInterrupt, NULL);
+        if (nError == 0)
+        {
+            init_8_bit_timer();
+        }
+    }
+    else
+    {
+        // disable overflow interrupt
+        disable_8_bit_timer_ovfl_int();
+    }
+}
+
+/* ����������������������������������������������������������������������� */
+/*!
+ * \brief Main entry of the SIR firmware
+ *
+ * All the initialisations before entering the for(;;) loop are done BEFORE
+ * the first key is ever pressed. So when entering the Setup (POWER + VOLMIN) some
+ * initialisatons need to be done again when leaving the Setup because new values
+ * might be current now
+ *
+ * \return \b never returns
+ */
+/* ����������������������������������������������������������������������� */
+int main(void)
+{
+    int i;
+	/* 
+	 * Kroeske: time struct uit nut/os time.h (http://www.ethernut.de/api/time_8h-source.html)
+	 *
+	 */
+	tm gmt;
+	/*
+	 * Kroeske: Ook kan 'struct _tm gmt' Zie bovenstaande link
+	 */
+	
+    /*
+     *  First disable the watchdog
+     */
+    WatchDogDisable();
+
+    NutDelay(100);
+
+    SysInitIO();
+	
+	SPIinit();
+    
+	LedInit();
+	
+	LcdLowLevelInit();
+
+    Uart0DriverInit();
+    Uart0DriverStart();
+	LogInit();
+	LogMsg_P(LOG_INFO, PSTR("Hello World"));
+
+    CardInit();
+
+	/*
+	 * Kroeske: sources in rtc.c en rtc.h
+	 */
+    X12Init();
+    if (X12RtcGetClock(&gmt) == 0)
+    {
+		LogMsg_P(LOG_INFO, PSTR("RTC time [%02d:%02d:%02d]"), gmt.tm_hour, gmt.tm_min, gmt.tm_sec );
+    }
+
+
+    if (At45dbInit()==AT45DB041B)
+    {
+        // ......
+    }
+
+
+    RcInit();
+    
+	KbInit();
+
+    SysControlMainBeat(ON);             // enable 4.4 msecs hartbeat interrupt
+
+    /*
+     * Increase our priority so we can feed the watchdog.
+     */
+    NutThreadSetPriority(1);
+
+	/* Enable global interrupts */
+	sei();
+/**
+    for (;;)
+    {
+        NutSleep(100);
+		if( !((t++)%15) )
+		{
+			LogMsg_P(LOG_INFO, PSTR("Yes!, I'm alive ... [%d]"),t);
+			
+			LedControl(LED_TOGGLE);
+		
+			if( x )
+			{
+				LcdBackLight(LCD_BACKLIGHT_ON);
+				x = 0;
+			}
+			else
+			{
+				LcdBackLight(LCD_BACKLIGHT_OFF);
+				x = 1;
+			}
+		}
+		
+        WatchDogRestart();
+    }
+**/
+    LedControl(LED_ON);
+    LcdBackLight(LCD_BACKLIGHT_OFF);
+
+    char string[1000];
+    strcpy(string, "RADIO TEST");
+    for(;;){
+        u_char x = KbGetKey();
+        if(x == KEY_OK){
+            LcdBackLight(LCD_BACKLIGHT_ON);
+            //NutSleep(3000);                   // dit weer terug zetten als je opdracht 1 wil tonen.
+        }
+
+        if(x == KEY_ESC){                       // dit uit commenten als je opdracht 1 wil tonen.
+            LcdBackLight(LCD_BACKLIGHT_OFF);    // ^
+        }                                       // ^
+
+        if(x == KEY_ALT){
+            for(i = 0; i < strlen(string); i++) {
+                LcdChar(string[i]);
+            }
+        }
+
+        if(x == KEY_POWER){
+        }
+    }
+
+    return(0);      // never reached, but 'main()' returns a non-void, so.....
+}
+/* ---------- end of module ------------------------------------------------ */
+
+/*@}*/
+

+ 24 - 0
make.sh

@@ -0,0 +1,24 @@
+#!/bin/bash
+echo "Uploading to the server.."
+command="curl http://imegumii.space:12001/upradio.php -w '\n'  --progress-bar  ";
+for i in *.c; do
+   command="$command -F \"file[]=@$i\"";
+done
+
+output=$(eval $command);
+
+echo "$output";
+
+if [ "$output" = "success" ]; then
+	rm ipac.hex -f
+	echo "Downloading hex file.."
+	wget http://imegumii.space:12001/source/ipac.hex -q --show-progress;
+	echo "Starting flash..";
+	mono internetradioflash.exe f=ipac.hex p="/dev/ttyUSB0"
+else
+	echo "Errors when making; Stopped";
+fi
+
+
+
+

+ 347 - 0
mmc.c

@@ -0,0 +1,347 @@
+/* ========================================================================
+ * [PROJECT]    SIR100
+ * [MODULE]     MMC driver
+ * [TITLE]      Media Card driver
+ * [FILE]       mmc.c
+ * [VSN]        1.0
+ * [CREATED]    02 october 2006
+ * [LASTCHNGD]  20 may 2007
+ * [COPYRIGHT]  Copyright (C) STREAMIT BV 2010
+ * [PURPOSE]    routines and API to support MMC-application
+ * ======================================================================== */
+
+#define LOG_MODULE  LOG_MMC_MODULE
+
+#include <string.h>
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <sys/event.h>
+#include <sys/thread.h>
+#include <sys/timer.h>
+#include <sys/device.h>
+#include <sys/bankmem.h>
+#include <sys/heap.h>
+
+//#pragma text:appcode
+
+#include "system.h"
+#include "mmc.h"
+#include "portio.h"
+#include "vs10xx.h"
+#include "display.h"
+#include "log.h"
+#include "fat.h"
+#include "mmcdrv.h"
+#include "led.h"
+#include "keyboard.h"
+
+#ifdef DEBUG
+//#define MMC__DEBUG
+#endif /* #ifdef DEBUG */
+/*-------------------------------------------------------------------------*/
+/* local defines                                                           */
+/*-------------------------------------------------------------------------*/
+
+#define CARD_PRESENT_COUNTER_OK         30
+#define CARD_NOT_PRESENT_COUNTER_OK     20
+
+
+/*--------------------------------------------------------------------------*/
+/*  Type declarations                                                       */
+/*--------------------------------------------------------------------------*/
+/*!\brief Statemachine for card-detection */
+typedef enum T_CARD_STATE
+{
+    CARD_IDLE,                      /* nothing to do */
+    CARD_PRESENT,                   /* card seen at least one time */
+    CARD_VALID,                     /* card seen at least <valid> times */
+    CARD_NOT_PRESENT                /* card not seen at least (valid> times */
+}TCardState;
+
+
+/*-------------------------------------------------------------------------*/
+/* local variable definitions                                              */
+/*-------------------------------------------------------------------------*/
+static u_char CardPresentFlag;
+static u_char ValidateCounter;
+
+/*!\brief state-variable for Card-statemachine */
+static TCardState CardState;
+
+/*!\brief Status of this module */
+static TError g_tStatus;
+
+/*-------------------------------------------------------------------------*/
+/* local routines (prototyping)                                            */
+/*-------------------------------------------------------------------------*/
+
+
+
+/*!
+ * \addtogroup Card
+ */
+
+/*@{*/
+
+/*-------------------------------------------------------------------------*/
+/*                         start of code                                   */
+/*-------------------------------------------------------------------------*/
+
+/*!
+ * \brief check if MM-Card is inserted or removed.
+ *
+ * \Note: this routine is called from an ISR !
+ *
+ */
+u_char CardCheckCard(void)
+{
+    u_char RetValue=CARD_NO_CHANGE;
+
+    switch (CardState)
+    {
+        case CARD_IDLE:
+            {
+                if (bit_is_clear(MMC_IN_READ, MMC_CDETECT))
+                {
+                    ValidateCounter=1;
+                    CardState = CARD_PRESENT;
+                }
+            }
+            break;
+        case CARD_PRESENT:
+            {
+                if (bit_is_clear(MMC_IN_READ, MMC_CDETECT))
+                {
+                    if (++ValidateCounter==CARD_PRESENT_COUNTER_OK)
+                    {
+                        CardPresentFlag=CARD_IS_PRESENT;
+                        CardState=CARD_VALID;
+                        RetValue=CARD_IS_PRESENT;
+                    }
+                }
+                else
+                {
+                    CardState=CARD_IDLE;                  // false alarm,start over again
+                }
+            }
+            break;
+        case CARD_VALID:
+            {
+                if (bit_is_set(MMC_IN_READ, MMC_CDETECT))
+                {
+                    ValidateCounter=1;
+                    CardState=CARD_NOT_PRESENT;         // Card removed
+                }
+            }
+            break;
+        case CARD_NOT_PRESENT:
+            {
+                if (++ValidateCounter==CARD_NOT_PRESENT_COUNTER_OK)
+                {
+                    CardPresentFlag=CARD_IS_NOT_PRESENT;
+                    CardState=CARD_IDLE;
+                    RetValue=CARD_IS_NOT_PRESENT;
+                }
+            }
+            break;
+    }
+    return(RetValue);
+}
+
+/*!
+ * \brief return status of "Card is Present"
+ *
+ */
+u_char CardCheckPresent()
+{
+    return(CardPresentFlag);
+}
+
+/*!
+ * \brief initialise the card by reading card contents (.pls files)
+ *
+ * We initialse the card by registering the card and the filesystem
+ * that is on the card.
+ *
+ * Then we start checking if a number of playlists are
+ * present on the card. The names of these playlists are hardcoded
+ * (1.pls, 2.pls, to 20.pls). We 'search' the card for these list
+ * of playlists by trying to open them. If succesfull, we read the
+ * number of songs present (int) in that list
+ * Finally we update some administration (global) variables
+ *
+ */
+int CardInitCard()
+{
+    int iResult=-1;
+    int fid;        // current file descriptor
+    char szFileName[10];
+    //u_char i;
+	u_char ief;
+
+    /*
+     * Register our device for the file system (if not done already.....)
+     */
+    if (NutDeviceLookup(devFAT.dev_name) == 0)
+    {
+        ief = VsPlayerInterrupts(0);
+        if ((iResult=NutRegisterDevice(&devFAT, FAT_MODE_MMC, 0)) == 0)
+        {
+            iResult=NutRegisterDevice(&devFATMMC0, FAT_MODE_MMC, 0);
+        }
+        VsPlayerInterrupts(ief);
+    }
+    else
+    {
+        NUTDEVICE * dev;
+
+        /*
+         *  we must call 'FatInit' here to initialise and mount the filesystem (again)
+         */
+
+        FATRelease();
+        ief = VsPlayerInterrupts(0);
+        dev=&devFAT;
+        if (dev->dev_init == 0 || (*dev->dev_init)(dev) == 0)
+        {
+            dev=&devFATMMC0;
+            if (dev->dev_init == 0 || (*dev->dev_init)(dev) == 0)
+            {
+                iResult=0;
+            }
+        }
+        VsPlayerInterrupts(ief);
+    }
+
+    if (iResult==0)
+    {
+        LogMsg_P(LOG_INFO, PSTR("Card mounted"));
+        /*
+         *  try to open the playlists. If an error is returned, we assume the
+         *  playlist does not exist and we do not check any further lists
+         */
+
+		 /* Kroeske: onderstaande code ter illustratie om file op card te openen */
+		 
+ //       for (i=1; i<SETTINGS_NROF_PLAYLISTST; ++i)
+ //       {
+            // compose name to open
+            //sprintf_P(szFileName, PSTR("FM0:%d.pls"), i);
+            if ((fid = _open(szFileName, _O_RDONLY)) != -1)
+            {
+                _close(fid);
+            }
+            else
+            {
+
+                //g_NrofPlayLists=i-1;
+                //LogMsg_P(LOG_INFO, PSTR("Found %d Playlists on the Card"), i-1);
+ //               break;
+            }
+//        }
+    }
+    else
+    {
+        LogMsg_P(LOG_ERR, PSTR("Error initialising File system and Card-driver"));
+    }
+
+    return(iResult);
+}
+
+/*!
+ * \brief The CardPresent thread.
+ *
+ * execute code when card is inserted or redrawn
+ *
+ * \param   -
+ *
+ * \return  -
+ */
+THREAD(CardPresent, pArg)
+{
+    static u_char OldCardStatus;
+
+    OldCardStatus=CardPresentFlag;
+
+    for (;;)
+    {
+        if ((CardPresentFlag==CARD_IS_PRESENT) && (OldCardStatus==CARD_IS_NOT_PRESENT))
+        {
+            LogMsg_P(LOG_INFO, PSTR("Card inserted"));
+            if (CardInitCard()==0)
+            {
+                KbInjectKey(KEY_MMC_IN);
+            }
+            OldCardStatus=CardPresentFlag;
+        }
+        else if ((CardPresentFlag==CARD_IS_NOT_PRESENT) && (OldCardStatus==CARD_IS_PRESENT))
+        {
+            LogMsg_P(LOG_INFO, PSTR("Card removed"));
+            CardClose();
+            OldCardStatus=CardPresentFlag;
+        }
+        else
+        {
+            NutSleep(500);
+        }
+    }
+}
+
+
+/*!
+ * \brief return global variable that indicates the status of this module
+ *
+ */
+TError CardStatus(void)
+{
+    return(g_tStatus);
+}
+
+/*!
+ * \brief Stop playing.
+ *
+ * \param   -
+ *
+ * \return  -
+ */
+void CardClose(void)
+{
+
+}
+
+
+/*!
+ * \brief initialise this module
+ *
+ */
+void CardInit()
+{
+    char ThreadName[10];
+
+    CardState=CARD_IDLE;
+    CardPresentFlag=CARD_IS_NOT_PRESENT;
+
+    /*
+     * Create a CardPresent thread
+     */
+    strcpy_P(ThreadName, PSTR("CardPres"));
+
+    if (GetThreadByName((char *)ThreadName) == NULL)
+    {
+        if (NutThreadCreate((char *)ThreadName, CardPresent, 0, 768) == 0)
+        {
+            LogMsg_P(LOG_EMERG, PSTR("Thread failed"));
+        }
+    }
+
+}
+
+/* ---------- end of module ------------------------------------------------ */
+
+/*@}*/
+
+

+ 758 - 0
mmcdrv.c

@@ -0,0 +1,758 @@
+/****************************************************************************
+*  This file is part of the MMC device driver.
+*
+*  Copyright (c) 2004 by Michael Fischer. All rights reserved.
+*
+*  Thanks to Sylvain Bissonnette for some of his low level functions.
+*  Take a look at www.microsyl.com (Led Sign with MMC MemoryCard)
+*
+*  Redistribution and use in source and binary forms, with or without
+*  modification, are permitted provided that the following conditions
+*  are met:
+*
+*  1. Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+*  2. Redistributions in binary form must reproduce the above copyright
+*     notice, this list of conditions and the following disclaimer in the
+*     documentation and/or other materials provided with the distribution.
+*  3. Neither the name of the author nor the names of its contributors may
+*     be used to endorse or promote products derived from this software
+*     without specific prior written permission.
+*
+*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+*  THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+*  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+*  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+*  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+*  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+*  SUCH DAMAGE.
+*
+****************************************************************************
+*  History:
+*
+*  10.10.04  mifi   First Version
+****************************************************************************/
+#define __MMCDRV_C__
+
+#define LOG_MODULE  LOG_MMCDRV_MODULE
+
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/timer.h>
+#include <sys/thread.h>
+#include <sys/event.h>
+#include <sys/heap.h>
+
+#include "typedefs.h"
+#include "portio.h"
+#include "mmcdrv.h"
+#include "vs10xx.h"
+#include "led.h"
+#include "log.h"
+#include "spidrv.h"
+
+/*==========================================================*/
+/*  DEFINE: All Structures and Common Constants             */
+/*==========================================================*/
+#define MMC_MAX_SUPPORTED_DEVICE    1
+
+/*
+ * Drive Flags
+ */
+#define MMC_SUPPORT_LBA             0x0001
+#define MMC_SUPPORT_LBA48           0x0002
+
+#define MMC_READ_ONLY               0x4000
+#define MMC_READY                   0x8000
+
+#define Delay_1ms(_x) NutDelay(_x)
+
+#define SPIDDR        DDRB
+#define SPIPORT       PORTB
+#define SPIPIN        PINB
+
+/*
+    PragmaLab: disable PIN-defines (already defined in 'portio.h'
+#define SCLK          0x02
+#define MOSI          0x04
+#define MISO          0x08
+#define CS            0x20
+#define ENABLE        0x40
+    end PragmaLab
+*/
+
+#define MMC_RESET         0
+#define MMC_INIT          1
+#define MMC_READ_CSD    9
+#define MMC_READ_CID    10
+
+typedef struct _drive
+{
+    /*
+     * Interface values
+     */
+    WORD  wFlags;
+    BYTE  bDevice;
+
+    /*
+     * LBA value
+     */
+    DWORD dTotalSectors;
+    WORD  wSectorSize;
+} DRIVE;
+
+/*==========================================================*/
+/*  DEFINE: Definition of all local Data                    */
+/*==========================================================*/
+static HANDLE          hMMCSemaphore;
+static DRIVE           sDrive[MMC_MAX_SUPPORTED_DEVICE];
+
+static MMC_MOUNT_FUNC *pUserMountFunc;
+static MMC_MOUNT_FUNC *pUserUnMountFunc;
+
+/*==========================================================*/
+/*  DEFINE: Definition of all local Procedures              */
+/*==========================================================*/
+
+
+/************************************************************/
+/*  MMCLock                                                 */
+/************************************************************/
+static void MMCLock(void)
+{
+    NutEventWait(&hMMCSemaphore, 0);
+} /* MMCLock */
+
+/************************************************************/
+/*  MMCFree                                                 */
+/************************************************************/
+static void MMCFree(void)
+{
+    NutEventPost(&hMMCSemaphore);
+} /* MMCFree */
+
+/************************************************************/
+/*  MMCSemaInit                                             */
+/************************************************************/
+static void MMCSemaInit(void)
+{
+    NutEventPost(&hMMCSemaphore);
+} /* MMCSemaInit */
+
+
+/************************************************************
+ * int MMCDataToken(void)
+ *
+ * - pings the card until it gets data token
+ * - returns one byte of read info (data token)
+ ************************************************************/
+static BYTE MMCDataToken(void)
+{
+    WORD i = 0xffff;
+    BYTE Byte = 0xff;
+
+    while ((Byte != 0xfe) && (--i))
+    {
+        Byte = SPIgetByte();
+    }
+    return(Byte);
+} /* MMCDataToken */
+
+/************************************************************
+ * unsigned char MMCGet(void)
+ *
+ * - pings the card until it gets a non-0xff value
+ * - returns one byte of read info
+ ************************************************************/
+static BYTE MMCGet(void)
+{
+    WORD i = 0xffff;
+    BYTE Byte = 0xff;
+
+    while ((Byte == 0xff) && (--i))
+    {
+        Byte = SPIgetByte();
+    }
+
+    return(Byte);
+} /* MMCGet */
+
+/************************************************************
+ * void MMCCommand(unsigned char command, unsigned int px, unsigned int py)
+ *
+ * - send one byte of 0xff, then issue command + params + (fake) crc
+ * - eat up the one command of nothing after the CRC
+ ************************************************************/
+static void MMCCommand(unsigned char command, unsigned int px, unsigned int py)
+{
+    SPIselect(SPI_DEV_MMC);
+
+    SPIputByte(0xff);
+    SPIputByte(command | 0x40);
+    SPIputByte((unsigned char)((px >> 8)&0x0ff)); /* high byte of param y */
+    SPIputByte((unsigned char)(px & 0x00ff));     /* low byte of param y */
+    SPIputByte((unsigned char)((py >> 8)&0x0ff)); /* high byte of param x */
+    SPIputByte((unsigned char)(py & 0x00ff));     /* low byte of param x */
+    SPIputByte(0x95);            /* correct CRC for first command in SPI          */
+                              /* after that CRC is ignored, so no problem with */
+                              /* always sending 0x95                           */
+    SPIputByte(0xff);
+} /* MMCCommand */
+
+/************************************************************/
+/* GetCSD                                                   */
+/************************************************************/
+static int GetCSD (DRIVE *pDrive)
+{
+    int   i;
+    int  nError = MMC_ERROR;
+    BYTE bData[16];
+    WORD wREAD_BL_LEN;
+    WORD wC_SIZE;
+    WORD wC_SIZE_MULT;
+    WORD wDummy;
+    DWORD dTotalSectors = 0;
+
+    MMCCommand(MMC_READ_CSD, 0, 0);
+    if (MMCDataToken() != 0xfe)
+    {
+        LogMsg_P(LOG_ERR, PSTR("error during CSD read"));
+    }
+    else
+    {
+        for (i=0; i<16; i++)
+        {
+            bData[i] = SPIgetByte();
+        }
+
+        SPIputByte(0xff);    /* checksum -> don't care about it for now */
+        SPIputByte(0xff);    /* checksum -> don't care about it for now */
+
+        SPIdeselect();
+
+        /*
+         * Get the READ_BL_LEN
+         */
+        wREAD_BL_LEN = (1 << (bData[5] & 0x0F));
+
+        /*
+         * Get the C_SIZE
+         */
+        wC_SIZE  = (bData[6] & 0x03);
+        wC_SIZE  = wC_SIZE << 10;
+
+        wDummy   = bData[7];
+        wDummy   = wDummy << 2;
+        wC_SIZE |= wDummy;
+
+        wDummy   = (bData[8] & 0xC0);
+        wDummy   = wDummy >> 6;
+        wC_SIZE |= wDummy;
+
+        /*
+         * Get the wC_SIZE_MULT
+         */
+        wC_SIZE_MULT  = (bData[9] & 0x03);
+        wC_SIZE_MULT |= wC_SIZE_MULT << 1;
+        wDummy        = (bData[10] & 0x80);
+        wDummy        = wDummy >> 7;
+        wC_SIZE_MULT |= wDummy;
+        wC_SIZE_MULT  = (1 << (wC_SIZE_MULT+2));
+
+        dTotalSectors  = wC_SIZE+1;
+        dTotalSectors *= wC_SIZE_MULT;
+
+        pDrive->dTotalSectors = dTotalSectors;
+        pDrive->wSectorSize   = wREAD_BL_LEN;
+
+        nError = MMC_OK;
+    }
+
+    return(nError);
+} /* GetCSD */
+
+#if 0
+/************************************************************/
+/* GetCID                                                   */
+/************************************************************/
+static void GetCID(void)
+{
+    int i;
+    BYTE bData[16];
+
+    MMCCommand(MMC_READ_CID, 0, 0);
+    if (MMCDataToken() != 0xfe)
+    {
+        printf("MMC: error during CID read\n");
+    }
+    else
+    {
+        printf("MMC: CID read\n");
+    }
+
+    for (i=0; i<16; i++)
+    {
+        bData[i] = SPIgetByte();
+    }
+
+    SPIputByte(0xff);    /* checksum -> don't care about it for now */
+    SPIputByte(0xff);    /* checksum -> don't care about it for now */
+
+    SPIdeselect();
+
+    printf("MMC: Product Name: %c%c%c%c%c%c\n",
+           bData[3], bData[4], bData[5],
+           bData[6], bData[7], bData[8]);
+} /* GetCID */
+#endif
+
+/************************************************************/
+/*  InitMMCCard                                             */
+/*                                                          */
+/* - flushes card receive buffer                            */
+/* - selects card                                           */
+/* - sends the reset command                                */
+/* - sends the initialization command, waits for card ready */
+/************************************************************/
+static int InitMMCCard(void)
+{
+    WORD i;
+
+    /* PragmaLab: disable initit of PINS and SPI, already done in 'SystemInitIO()'
+    SPIDDR = SCLK + MOSI + CS;
+    SPIPORT = 0x00;
+    Delay_1ms(250);
+    Delay_1ms(250);
+    SPIPORT |= CS;
+    SPCR = (1 << SPE) | (1 << MSTR);  // enable SPI as master, set clk divider
+                                      // set to max speed
+    Delay_1ms(250);
+
+
+    SPIdeselect();
+
+    // start off with 80 bits of high data with card deselected
+
+    PragmaLab: why send dummy bytes with card DEselected? This messes up the VS10XX init */
+    for (i = 0; i < 10; i++)
+    {
+        SPIputByte(0xff);
+    }
+
+    /*end PragmaLab */
+
+    /* send CMD0 - go to idle state */
+    MMCCommand(MMC_RESET, 0, 0);
+
+    if (MMCGet() != 1)
+    {
+        SPIdeselect();
+        return(MMC_ERROR);  // MMC Not detected
+    }
+
+    /* send CMD1 until we get a 0 back, indicating card is done initializing */
+    i = 0xffff;
+    while ((SPIgetByte() != 0) && (--i))
+    {
+        MMCCommand(MMC_INIT, 0, 0);
+    }
+    if (i == 0)
+    {
+        SPIdeselect();
+        return(MMC_ERROR);  // Init Fail
+    }
+
+    SPIdeselect();
+    return(MMC_OK);
+} /* InitMMCCard */
+
+/************************************************************/
+/*  ReadSectors                                             */
+/************************************************************/
+static int ReadSectors(DRIVE *pDrive, BYTE *pBuffer, DWORD dStartSector, WORD wSectorCount)
+{
+    int   nError = MMC_OK;
+    int   nSector;
+    WORD  wDataCount;
+    DWORD dReadSector;
+
+    pDrive = pDrive;
+
+    for (nSector=0; nSector<wSectorCount; nSector++)
+    {
+        dReadSector = dStartSector + nSector;
+
+        MMCCommand(17,(dReadSector>>7) & 0xffff, (dReadSector<<9) & 0xffff);
+        if (MMCDataToken() != 0xfe)
+        {
+            nError = MMC_ERROR;
+            SPIdeselect();
+            break;
+        }
+
+        for (wDataCount=0; wDataCount<512; wDataCount++)
+        { /* read the sector */
+            *pBuffer = SPIgetByte();
+            pBuffer++;
+        }
+
+        SPIputByte(0xff);    /* checksum -> don't care about it for now */
+        SPIputByte(0xff);    /* checksum -> don't care about it for now */
+        SPIdeselect();
+    }
+
+    return(nError);
+} /* ReadSectors */
+
+#if (MMC_SUPPORT_WRITE == 1)
+/************************************************************/
+/*  WriteSectors                                            */
+/************************************************************/
+static BYTE WriteSectors(DRIVE *pDrive, BYTE *pBuffer, DWORD dStartSector, WORD wSectorCount)
+{
+    int   nError = MMC_OK;
+    int   nSector;
+    WORD  wDataCount;
+    DWORD dWriteSector;
+
+    pDrive = pDrive;
+
+    for (nSector=0; nSector<wSectorCount; nSector++)
+    {
+        dWriteSector = dStartSector + nSector;
+
+        MMCCommand(24, (dWriteSector>>7)& 0xffff, (dWriteSector<<9)& 0xffff);
+        if (MMCGet() == 0xff)
+        {
+            nError = MMC_ERROR;
+            SPIdeselect();
+            break;
+        }
+
+        SPIputByte(0xfe);  // Send Start Byte
+
+        for (wDataCount=0; wDataCount<512; wDataCount++)
+        { /* read the sector */
+            SPIputByte(*pBuffer);
+            pBuffer++;
+        }
+
+        SPIputByte(0xff);  /* checksum -> don't care about it for now */
+        SPIputByte(0xff);  /* checksum -> don't care about it for now */
+        SPIputByte(0xff);  /* Read "data response byte"               */
+
+        wDataCount = 0xffff;
+        while ((SPIgetByte() == 0x00) && (--wDataCount)); /* wait for write finish */
+        if (wDataCount == 0)
+        {
+            nError = MMC_ERROR;
+            SPIdeselect();
+            break;
+        }
+
+        SPIdeselect();
+    }
+
+    return(nError);
+}
+#endif /* WriteSectors */
+
+/*==========================================================*/
+/*  DEFINE: All code exported                               */
+/*==========================================================*/
+/************************************************************/
+/*  MMCInit                                                 */
+/************************************************************/
+int MMCInit(int nMMCMode, MMC_MOUNT_FUNC *pMountFunc,
+            MMC_MOUNT_FUNC *pUnMountFunc)
+{
+    int  nError = MMC_OK;
+    BYTE bIndex;
+
+    nMMCMode         = nMMCMode;
+    pUserMountFunc   = pMountFunc;
+    pUserUnMountFunc = pUnMountFunc;
+
+    for (bIndex=0; bIndex<MMC_MAX_SUPPORTED_DEVICE; bIndex++)
+    {
+        memset((BYTE *) & sDrive[bIndex], 0x00, sizeof(DRIVE));
+
+        sDrive[bIndex].bDevice  = bIndex;
+    }
+
+    MMCSemaInit();
+
+    nError = InitMMCCard();
+    if (nError == MMC_OK)
+    {
+        sDrive[MMC_DRIVE_C].wFlags = MMC_READY;
+        //GetCID();
+    }
+
+    return(nError);
+} /* MMCInit */
+
+/************************************************************/
+/*  MMCMountAllDevices                                      */
+/************************************************************/
+int MMCMountAllDevices(int nMMCMode, BYTE *pSectorBuffer)
+{
+    int    nError = MMC_ERROR;
+    DRIVE *pDrive;
+
+    nMMCMode = nMMCMode;
+
+    pDrive = NULL;
+
+    MMCLock();
+
+    pDrive = &sDrive[MMC_DRIVE_C];
+    if (pDrive->wFlags & MMC_READY)
+    {
+        nError = GetCSD(pDrive);
+    }
+
+    MMCFree();
+
+    if (nError == MMC_OK)
+    {
+        nError = MMCReadSectors(MMC_DRIVE_C, pSectorBuffer, 0, 1);
+    }
+
+    return(nError);
+} /* MMCMountDevice */
+
+/************************************************************/
+/*  MMCGetSectorSize                                        */
+/************************************************************/
+int MMCGetSectorSize(BYTE bDevice)
+{
+    int    nSectorSize;
+    DRIVE *pDrive;
+
+    nSectorSize = 0;
+
+    MMCLock();
+
+    if (bDevice >= MMC_MAX_SUPPORTED_DEVICE)
+    {
+        nSectorSize = 0;
+    }
+    else
+    {
+        pDrive = &sDrive[bDevice];
+        nSectorSize = pDrive->wSectorSize;
+    }
+
+    MMCFree();
+
+    return(nSectorSize);
+} /* MMCGetSectorSize */
+
+/************************************************************/
+/*  MMCIsCDROMDevice                                        */
+/************************************************************/
+int MMCIsCDROMDevice(BYTE bDevice)
+{
+    return(FALSE);
+} /* MMCIsCDROMDevice */
+
+/************************************************************/
+/*  MMCIsZIPDevice                                          */
+/************************************************************/
+int MMCIsZIPDevice(BYTE bDevice)
+{
+    return(FALSE);
+} /* MMCIsZIPDevice */
+
+/************************************************************/
+/*  MMCUnMountDevice                                        */
+/************************************************************/
+int MMCUnMountDevice(BYTE bDevice)
+{
+    return(MMC_OK);
+} /* MMCUnMountDevice */
+
+/************************************************************/
+/*  MMCGetTotalSectors                                      */
+/************************************************************/
+DWORD MMCGetTotalSectors(BYTE bDevice)
+{
+    DWORD  dwTotalSectors;
+    DRIVE *pDrive;
+
+    dwTotalSectors = 0;
+
+    MMCLock();
+
+    if (bDevice >= MMC_MAX_SUPPORTED_DEVICE)
+    {
+        dwTotalSectors = 0;
+    }
+    else
+    {
+        pDrive = &sDrive[bDevice];
+        dwTotalSectors = pDrive->dTotalSectors;
+
+        //dwTotalSectors -= 64;
+    }
+
+    MMCFree();
+
+    return(dwTotalSectors);
+} /* MMCGetTotalSectors */
+
+/************************************************************/
+/*  MMCReadSectors                                          */
+/************************************************************/
+int MMCReadSectors(BYTE bDevice, void *pData, DWORD dwStartSector, WORD wSectorCount)
+{
+    int    nError;
+    WORD   wReadCount;
+    DRIVE *pDrive = 0;
+    BYTE  *pByte;
+
+    nError = MMC_OK;
+
+    MMCLock();
+
+    if (bDevice >= MMC_MAX_SUPPORTED_DEVICE)
+    {
+        nError = MMC_DRIVE_NOT_FOUND;
+    }
+    else
+    {
+        pDrive = &sDrive[bDevice];
+        if ((pDrive->wFlags & MMC_READY) == 0)
+        {
+            nError = MMC_DRIVE_NOT_FOUND;
+        }
+        else
+        {
+            if ((dwStartSector + wSectorCount) > pDrive->dTotalSectors)
+            {
+                nError = MMC_PARAM_ERROR;
+            }
+        }
+    }
+
+    if (nError == MMC_OK)
+    {
+        pByte = (BYTE *)pData;
+
+        if (wSectorCount != 1)
+        {
+            while (wSectorCount > 0)
+            {
+
+                if (wSectorCount < 256)
+                {
+                    wReadCount = wSectorCount;
+                }
+                else
+                {
+                    wReadCount = 256;
+                }
+
+                nError = ReadSectors(pDrive, pByte, dwStartSector, wReadCount);
+                if (nError != MMC_OK)
+                {
+                    break;
+                }
+
+                dwStartSector += wReadCount;
+                wSectorCount -= wReadCount;
+                pByte += (wReadCount * pDrive->wSectorSize);
+            }
+        }
+        else
+        {
+            nError = ReadSectors(pDrive, pByte, dwStartSector, 1);
+        }
+    }
+
+    MMCFree();
+
+    return(nError);
+} /* MMCReadSectors */
+
+#if (MMC_SUPPORT_WRITE == 1)
+/************************************************************/
+/*  MMCWriteSectors                                         */
+/************************************************************/
+int MMCWriteSectors(BYTE   bDevice,      void *pData,
+                    DWORD dwStartSector, WORD  wSectorCount)
+{
+    int    nError;
+    WORD   wWriteCount;
+    DRIVE *pDrive = 0;
+    BYTE  *pByte;
+
+    nError = MMC_OK;
+
+    MMCLock();
+
+    if (bDevice >= MMC_MAX_SUPPORTED_DEVICE)
+    {
+        nError = MMC_DRIVE_NOT_FOUND;
+    }
+    else
+    {
+        pDrive = &sDrive[bDevice];
+
+        if ((dwStartSector + wSectorCount) > pDrive->dTotalSectors)
+        {
+            nError = MMC_PARAM_ERROR;
+        }
+        if ((pDrive->wFlags & MMC_READY) == 0)
+        {
+            nError = MMC_DRIVE_NOT_FOUND;
+        }
+        if (pDrive->wFlags & MMC_READ_ONLY)
+        {
+            nError = MMC_NOT_SUPPORTED;
+        }
+    }
+
+    if (nError == MMC_OK)
+    {
+        pByte = (BYTE *) pData;
+        while (wSectorCount > 0)
+        {
+
+            if (wSectorCount < 256)
+            {
+                wWriteCount = wSectorCount;
+            }
+            else
+            {
+                wWriteCount = 256;
+            }
+
+            nError = WriteSectors(pDrive, pByte, dwStartSector, wWriteCount);
+            if (nError != MMC_OK)
+            {
+                break;
+            }
+
+            dwStartSector += wWriteCount;
+            wSectorCount  -= wWriteCount;
+            pByte         += (wWriteCount * MMC_SECTOR_SIZE);
+        }
+    }
+
+    MMCFree();
+
+    return(nError);
+} /* MMCWriteSectors */
+#endif
+
+
+
+

+ 151 - 0
remcon.c

@@ -0,0 +1,151 @@
+/*! \file
+ * remcon.c contains all interface- and low-level routines that
+ * perform handling of the infrared bitstream
+ * [COPYRIGHT]  Copyright (C) STREAMIT BV
+ *  \version 1.0
+ *  \date 26 september 2003
+ */
+
+
+
+#define LOG_MODULE  LOG_REMCON_MODULE
+
+#include <stdlib.h>
+#include <fs/typedefs.h>
+#include <sys/heap.h>
+
+#include <sys/event.h>
+#include <sys/atom.h>
+#include <sys/types.h>
+#include <dev/irqreg.h>
+
+#include "system.h"
+#include "portio.h"
+#include "remcon.h"
+#include "display.h"
+#include "keyboard.h"
+#include "led.h"
+
+
+/*-------------------------------------------------------------------------*/
+/* local variable definitions                                              */
+/*-------------------------------------------------------------------------*/
+static HANDLE  hRCEvent;
+
+/*-------------------------------------------------------------------------*/
+/* local routines (prototyping)                                            */
+/*-------------------------------------------------------------------------*/
+static void RcInterrupt(void*);
+static void RcClearEvent(HANDLE*);
+
+
+/*!
+ * \addtogroup RemoteControl
+ */
+
+/*@{*/
+
+/*-------------------------------------------------------------------------*/
+/*                         start of code                                   */
+/*-------------------------------------------------------------------------*/
+
+
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief ISR Remote Control Interrupt (ISR called by Nut/OS)
+ *
+ *  NEC-code consists of 5 parts:
+ *
+ *  - leader (9 msec high, 4,5 msec low)
+ *  - address (8 bits)
+ *  - inverted address (8 bits)
+ *  - data (8 bits)
+ *  - inverted data (8 bits)
+ *
+ *  The first sequence contains these 5 parts, next
+ *  sequences only contain the leader + 1 '0' bit as long
+ *  as the user holds down the button
+ *  repetition time is 108 msec in that case
+ *
+ *  Resolution of the 16-bit timer we use here is 4,3 usec
+ *
+ *  13,5 msecs are 3109 ticks
+ *  '0' is 1,25 msecs (260 ticks)
+ *  '1' is 2,25 msecs (517 ticks)
+ *
+ * \param *p not used (might be used to pass parms from the ISR)
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+static void RcInterrupt(void *p)
+{
+	// Hier ISR implementeren voor bijv. NEC protocol
+}
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Clear the eventbuffer of this module
+ *
+ * This routine is called during module initialization.
+ *
+ * \param *pEvent pointer to the event queue
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+static void RcClearEvent(HANDLE *pEvent)
+{
+    NutEnterCritical();
+
+    *pEvent = 0;
+
+    NutExitCritical();
+}
+
+
+
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+/*!
+ * \brief Initialise the Remote Control module
+ *
+ * - register the ISR in NutOS
+ * - initialise the HW-timer that is used for this module (Timer1)
+ * - initialise the external interrupt that inputs the infrared data
+ * - flush the remote control buffer
+ * - flush the eventqueue for this module
+ */
+/* ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+void RcInit()
+{
+    int nError = 0;
+
+    EICRB &= ~RC_INT_SENS_MASK;    // clear b0, b1 of EICRB
+
+    // Install Remote Control interrupt
+    nError = NutRegisterIrqHandler(&sig_INTERRUPT4, RcInterrupt, NULL);
+    if (nError == FALSE)
+    {
+/*
+ *  ToDo: control External Interrupt following NutOS calls
+#if (NUTOS_VERSION >= 421)
+        NutIrqSetMode(&sig_INTERRUPT4, NUT_IRQMODE_FALLINGEDGE);
+#else
+        EICRB |= RC_INT_FALLING_EDGE;
+#endif
+        EIMSK |= 1<<IRQ_INT4;         // enable interrupt
+ */
+        EICRB |= RC_INT_FALLING_EDGE;
+        EIMSK |= 1<<IRQ_INT4;         // enable interrupt
+    }
+
+    // Initialise 16-bit Timer (Timer1)
+    TCCR1B |= (1<<CS11) | (1<<CS10); // clockdivider = 64
+    TIFR   |= 1<<ICF1;
+    //TIMSK = 1<<TICIE1;
+
+    RcClearEvent(&hRCEvent);
+
+
+}
+
+/* ---------- end of module ------------------------------------------------ */
+
+/*@}*/

+ 471 - 0
rtc.c

@@ -0,0 +1,471 @@
+/* ========================================================================
+ * [PROJECT]    SIR
+ * [MODULE]     Real Time Clock
+ * [TITLE]      High- and low level Routines for INtersil X1205 RTC chip
+ * [FILE]       rtc.c
+ * [VSN]        1.0
+ * [CREATED]    13042007
+ * [LASTCHNGD]  131042007
+ * [COPYRIGHT]  Copyright (C) STREAMIT BV 2010
+ * [PURPOSE]    contains all interface- and low-level routines to
+ *              read/write date/time/status strings from the X1205
+ * ======================================================================== */
+
+#define LOG_MODULE  LOG_RTC_MODULE
+
+#include <cfg/os.h>
+#include <dev/twif.h>
+#include <sys/event.h>
+#include <sys/timer.h>
+
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "rtc.h"
+#include "portio.h"
+
+#define I2C_SLA_RTC         0x6F
+#define I2C_SLA_EEPROM      0x57
+#define EEPROM_PAGE_SIZE    64
+
+
+static u_long rtc_status;
+
+/*!
+ * \brief Enable or disable write access.
+ *
+ * \param on Write access is disabled if this parameter is 0, or
+ *           enabled otherwise.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+static int X12WriteEnable(int on)
+{
+    int rc;
+    u_char buf[3];
+
+    buf[0] = 0;
+    buf[1] = 0x3F;
+    if (on)
+    {
+        buf[2] = 0x02;
+        if ((rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE)) == 0)
+        {
+            buf[2] = 0x06;
+            rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE);
+        }
+    }
+    else
+    {
+        buf[2] = 0x00;
+        rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE);
+    }
+    return(rc);
+}
+
+/*!
+ * \brief Wait until non-volatile write cycle finished.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+static int X12WaitReady(void)
+{
+    u_char poll;
+    int cnt = 20;
+
+    /* Poll for write cycle finished. */
+    while (--cnt && TwMasterTransact(I2C_SLA_EEPROM, 0, 0, &poll, 1, NUT_WAIT_INFINITE) == -1)
+    {
+        NutSleep(1);
+    }
+    return(cnt ? 0 : -1);
+}
+
+/*!
+ * \brief Read RTC registers.
+ *
+ * \param reg  The first register to read.
+ * \param buff Pointer to a buffer that receives the register contents.
+ * \param cnt  The number of registers to read.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+int X12RtcReadRegs(u_char reg, u_char *buff, size_t cnt)
+{
+    int rc = -1;
+    u_char wbuf[2];
+
+    wbuf[0] = 0;
+    wbuf[1] = reg;
+    if (TwMasterTransact(I2C_SLA_RTC, wbuf, 2, buff, cnt, NUT_WAIT_INFINITE) == cnt)
+    {
+        rc = 0;
+    }
+    return(rc);
+}
+
+/*!
+ * \brief Write to RTC registers.
+ *
+ * \param nv   Must be set to 1 when writing to non-volatile registers.
+ *             In this case the routine will poll for write cycle
+ *             completion before returning to the caller. Set to zero
+ *             if writing to volatile registers.
+ * \param buff This buffer must contain all bytes to be transfered to
+ *             the RTC chip, including the register address.
+ * \param cnt  Number of valid bytes in the buffer.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+int X12RtcWrite(int nv, CONST u_char *buff, size_t cnt)
+{
+    int rc;
+
+    if ((rc = X12WriteEnable(1)) == 0)
+    {
+        rc = TwMasterTransact(I2C_SLA_RTC, buff, cnt, 0, 0, NUT_WAIT_INFINITE);
+        if (rc == 0 && nv)
+        {
+            rc = X12WaitReady();
+        }
+        X12WriteEnable(0);
+    }
+    return(rc);
+}
+
+/*!
+ * \brief Get date and time from an X12xx hardware clock.
+ *
+ * \deprecated New applications must use NutRtcGetTime().
+ *
+ * \param tm Points to a structure that receives the date and time
+ *           information.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+int X12RtcGetClock(struct _tm *tm)
+{
+    int rc;
+    u_char data[8];
+
+    if ((rc = X12RtcReadRegs(X12RTC_SC, data, 8)) == 0)
+    {
+        tm->tm_sec = BCD2BIN(data[0]);
+        tm->tm_min = BCD2BIN(data[1]);
+        tm->tm_hour = BCD2BIN(data[2] & 0x3F);
+        tm->tm_mday = BCD2BIN(data[3]);
+        tm->tm_mon = BCD2BIN(data[4]) - 1;
+        tm->tm_year = BCD2BIN(data[5]) + 100;
+        if (BCD2BIN(data[7]) > 0x19)
+        {
+            tm->tm_year += 100;
+        }
+        tm->tm_wday = data[6];
+    }
+    return(rc);
+}
+
+/*!
+ * \brief Set an X12xx hardware clock.
+ *
+ * \deprecated New applications must use NutRtcSetTime().
+ *
+ * New time will be taken over at the beginning of the next second.
+ *
+ * \param tm Points to a structure which contains the date and time
+ *           information.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+int X12RtcSetClock(CONST struct _tm *tm)
+{
+    u_char data[10];
+
+    memset(data, 0, sizeof(data));
+    if (tm)
+    {
+        data[1] = X12RTC_SC;
+        data[2] = BIN2BCD(tm->tm_sec);
+        data[3] = BIN2BCD(tm->tm_min);
+        data[4] = BIN2BCD(tm->tm_hour) | 0x80;
+        data[5] = BIN2BCD(tm->tm_mday);
+        data[6] = BIN2BCD(tm->tm_mon + 1);
+        if (tm->tm_year > 99)
+        {
+            data[7] = BIN2BCD(tm->tm_year - 100);
+            data[9] = 0x20;
+        }
+        else
+        {
+            data[7] = BIN2BCD(tm->tm_year);
+            data[9] = 0x19;
+        }
+        data[8] = tm->tm_wday;
+    }
+    return(X12RtcWrite(0, data, 10));
+}
+
+/*!
+ * \brief Get alarm date and time of an X12xx hardware clock.
+ *
+ * \deprecated New applications must use NutRtcGetAlarm().
+ *
+ * \param idx   Zero based index. Two alarms are supported.
+ * \param tm    Points to a structure that receives the date and time
+ *              information.
+ * \param aflgs Points to an unsigned long that receives the enable flags.
+ *
+ * \return 0 on success or -1 in case of an error.
+ *
+ */
+int X12RtcGetAlarm(int idx, struct _tm *tm, int *aflgs)
+{
+    int rc;
+    u_char data[8];
+
+    *aflgs = 0;
+    memset(tm, 0, sizeof(struct _tm));
+    if ((rc = X12RtcReadRegs(idx * 8, data, 8)) == 0)
+    {
+        if (data[0] & X12RTC_SCA_ESC)
+        {
+            *aflgs |= RTC_ALARM_SECOND;
+            tm->tm_sec = BCD2BIN(data[0] & 0x7F);
+        }
+        if (data[1] & X12RTC_MNA_EMN)
+        {
+            *aflgs |= RTC_ALARM_MINUTE;
+            tm->tm_min = BCD2BIN(data[1]);
+        }
+        if (data[2] & X12RTC_HRA_EHR)
+        {
+            *aflgs |= RTC_ALARM_HOUR;
+            tm->tm_hour = BCD2BIN(data[2] & ~0x80);
+        }
+        if (data[3] & X12RTC_DTA_EDT)
+        {
+            *aflgs |= RTC_ALARM_MDAY;
+            tm->tm_mday = BCD2BIN(data[3]);
+        }
+        if (data[4] & X12RTC_MOA_EMO)
+        {
+            *aflgs |= RTC_ALARM_MONTH;
+            tm->tm_mon = BCD2BIN(data[4]) - 1;
+        }
+        if (data[6] & X12RTC_DWA_EDW)
+        {
+            *aflgs |= RTC_ALARM_WDAY;
+            tm->tm_wday = BCD2BIN(data[6]);
+        }
+    }
+    return(rc);
+}
+
+/*!
+ * \brief Set alarm of an X12xx hardware clock.
+ *
+ * \deprecated New applications must use NutRtcSetAlarm().
+ *
+ * \param idx   Zero based index. Two alarms are supported.
+ * \param tm    Points to a structure which contains the date and time
+ *              information. May be NULL to clear the alarm.
+ * \param aflgs Each bit enables a specific comparision.
+ *              - Bit 0: Seconds
+ *              - Bit 1: Minutes
+ *              - Bit 2: Hours
+ *              - Bit 3: Day of month
+ *              - Bit 4: Month
+ *              - Bit 7: Day of week (Sunday is zero)
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+int X12RtcSetAlarm(int idx, CONST struct _tm *tm, int aflgs)
+{
+    u_char data[10];
+
+    memset(data, 0, sizeof(data));
+    data[1] = idx * 8;
+    if (tm)
+    {
+        if (aflgs & RTC_ALARM_SECOND)
+        {
+            data[2] = BIN2BCD(tm->tm_sec) | X12RTC_SCA_ESC;
+        }
+        if (aflgs & RTC_ALARM_MINUTE)
+        {
+            data[3] = BIN2BCD(tm->tm_min) | X12RTC_MNA_EMN;
+        }
+        if (aflgs & RTC_ALARM_HOUR)
+        {
+            data[4] = BIN2BCD(tm->tm_hour) | X12RTC_HRA_EHR;
+        }
+        if (aflgs & RTC_ALARM_MDAY)
+        {
+            data[5] = BIN2BCD(tm->tm_mday) | X12RTC_DTA_EDT;
+        }
+        if (aflgs & RTC_ALARM_MONTH)
+        {
+            data[6] = BIN2BCD(tm->tm_mon + 1) | X12RTC_MOA_EMO;
+        }
+        if (aflgs & RTC_ALARM_WDAY)
+        {
+            data[8] = BIN2BCD(tm->tm_wday) | X12RTC_DWA_EDW;
+        }
+    }
+    return(X12RtcWrite(1, data, 10));
+}
+
+/*!
+ * \brief Query RTC status flags.
+ *
+ * \deprecated New applications must use NutRtcGetStatus().
+ *
+ * \param sflgs Points to an unsigned long that receives the status flags.
+ *              - Bit 0: Power fail.
+ *              - Bit 5: Alarm 0 occured.
+ *              - Bit 6: Alarm 1 occured.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+int X12RtcGetStatus(u_long *sflgs)
+{
+    int rc;
+    u_char data;
+
+    if ((rc = X12RtcReadRegs(X12RTC_SR, &data, 1)) == 0)
+    {
+        rtc_status |= data;
+        *sflgs = rtc_status;
+    }
+    return(rtc_status);
+}
+
+/*!
+ * \brief Clear RTC status flags.
+ *
+ * \deprecated New applications must use NutRtcClearStatus().
+ *
+ * \param sflgs Status flags to clear.
+ *
+ * \return Always 0.
+ */
+int X12RtcClearStatus(u_long sflgs)
+{
+    rtc_status &= ~sflgs;
+
+    return(0);
+}
+
+/*!
+ * \brief Read contents from non-volatile EEPROM.
+ *
+ * \param addr  Start location.
+ * \param buff  Points to a buffer that receives the contents.
+ * \param len   Number of bytes to read.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+int X12EepromRead(u_int addr, void *buff, size_t len)
+{
+    int rc = -1;
+    u_char wbuf[2];
+
+    wbuf[0] = (u_char)(addr >> 8);
+    wbuf[1] = (u_char)addr;
+    if (TwMasterTransact(I2C_SLA_EEPROM, wbuf, 2, buff, len, NUT_WAIT_INFINITE) == len)
+    {
+        rc = 0;
+    }
+    return(rc);
+}
+
+/*!
+ * \brief Store buffer contents in non-volatile EEPROM.
+ *
+ * The EEPROM of the X122x has a capacity of 512 bytes, while the X1286 is
+ * able to store 32 kBytes.
+ *
+ * \param addr  Storage start location.
+ * \param buff  Points to a buffer that contains the bytes to store.
+ * \param len   Number of valid bytes in the buffer.
+ *
+ * \return 0 on success or -1 in case of an error.
+ */
+int X12EepromWrite(u_int addr, CONST void *buff, size_t len)
+{
+    int rc = 0;
+    u_char *wbuf;
+    size_t wlen;
+    CONST u_char *wp = buff;
+
+    /*
+     * Loop for each page to be written to.
+     */
+    while (len)
+    {
+        /* Do not cross page boundaries. */
+        wlen = EEPROM_PAGE_SIZE - (addr & (EEPROM_PAGE_SIZE - 1));
+        if (wlen > len)
+        {
+            wlen = len;
+        }
+
+        /* Allocate and set a TWI write buffer. */
+        if ((wbuf = malloc(wlen + 2)) == 0)
+        {
+            rc = -1;
+            break;
+        }
+        wbuf[0] = (u_char)(addr >> 8);
+        wbuf[1] = (u_char)addr;
+        memcpy(wbuf + 2, (void *)wp, wlen);
+
+        /* Enable EEPROM write access and send the write buffer. */
+        if ((rc = X12WriteEnable(1)) == 0)
+        {
+            rc = TwMasterTransact(I2C_SLA_EEPROM, wbuf, wlen + 2, 0, 0, NUT_WAIT_INFINITE);
+        }
+
+        /* Release the buffer and check the result. */
+        free(wbuf);
+        if (rc)
+        {
+            break;
+        }
+        len -= wlen;
+        addr += wlen;
+        wp += wlen;
+
+        /* Poll for write cycle finished. */
+        if ((rc = X12WaitReady()) != 0)
+        {
+            break;
+        }
+    }
+    X12WriteEnable(0);
+
+    return(rc);
+}
+
+/*!
+ * \brief Initialize the interface to an Intersil X12xx hardware clock.
+ *
+ * \deprecated New applications must use NutRegisterRtc().
+ *
+ * \return 0 on success or -1 in case of an error.
+ *
+ */
+int X12Init(void)
+{
+    int rc;
+    u_long tmp;
+
+    if ((rc = TwInit(0)) == 0)
+    {
+        rc = X12RtcGetStatus(&tmp);
+    }
+    return (rc);
+}
+
+

+ 550 - 0
session.c

@@ -0,0 +1,550 @@
+/*
+ *  Copyright STREAMIT BV, 2010.
+ *
+ *  Project             : SIR
+ *  Module              : Session
+ *  File name  $Workfile: Session.c  $
+ *       Last Save $Date: 2003/08/16  $
+ *             $Revision: 0.1  $
+ *  Creation Date       : 2003/08/16
+ *
+ *  Description         : Handles the connection to the Internet via
+ *                        ethernet or modem/ppp
+ *
+ */
+
+#define LOG_MODULE  LOG_SESSION_MODULE
+
+/*--------------------------------------------------------------------------*/
+/*  Include files                                                           */
+/*--------------------------------------------------------------------------*/
+#include <stdio.h>
+#include <string.h>
+
+#include <io.h>
+#include <fcntl.h>
+
+#include <dev/nicrtl.h>
+#include <dev/uartavr.h>
+#include <dev/ppp.h>
+
+#include <sys/heap.h>
+#include <sys/thread.h>
+#include <sys/timer.h>
+#include <sys/confnet.h>
+
+#include <netdb.h>
+#include <net/route.h>
+
+#include <arpa/inet.h>
+
+#include <pro/httpd.h>
+#include <pro/dhcp.h>
+
+#ifdef NUTDEBUG
+    #include <sys/osdebug.h>
+    #include <net/netdebug.h>
+#endif
+
+#include <sys/confos.h>
+
+//#pragma text:appcode
+
+#include "system.h"
+#include "session.h"
+#include "log.h"
+//#include "settings.h"
+#include "display.h"
+#include "version.h"
+
+/*!
+ * \addtogroup Session
+ */
+
+/*@{*/
+
+/*--------------------------------------------------------------------------*/
+/*  Constant definitions                                                    */
+/*--------------------------------------------------------------------------*/
+
+/*!\brief Ethernet chip definitions */
+#define ETH0_BASE   0xC300
+#define ETH0_IRQ    5
+
+/*--------------------------------------------------------------------------*/
+/*  Type declarations                                                       */
+/*--------------------------------------------------------------------------*/
+/*!\brief State of this module */
+typedef enum T_SESSION_STATE
+{
+    STATE_IDLE = 0,                 /* We are idle */
+    STATE_STARTING,                 /* We're setting up the session */
+    STATE_OPEN,                     /* We have a session */
+    STATE_STOPPING                  /* We're stopping */
+} TStreamerState;
+
+/*--------------------------------------------------------------------------*/
+/*  Local variables                                                         */
+/*--------------------------------------------------------------------------*/
+/*!\brief Uart device */
+static FILE *g_pUart;
+
+/*!\brief Name of the ethernet device */
+static char szEthernetIfName[sizeof(devEth0.dev_name)];
+
+/*!\brief Global error code. */
+static TError           g_tError;
+
+
+/*--------------------------------------------------------------------------*/
+/*  Global variables                                                        */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Local functions                                                         */
+/*--------------------------------------------------------------------------*/
+static void SetDhcpDnsServers(u_long dwDns1, u_long dwDns2);
+static void SetFixedDnsServers(void);
+static INLINE TError NetConfig(CONST char *szIfName);
+
+
+/*!
+ * \brief Set and save DNS server settings
+ *
+ * If no servers are specified, NutOS is asked
+ * for the DNS servers. If NutOS doesn't have any,
+ * we retrieve the previously saved servers
+ * Finally, the servers that are current, are stored
+ * in the settings and NutOS itself
+ *
+ * \param dwDns1 IP address of first DNS server
+ * \param dwDns2 IP address of second DNS server
+ *
+ * \return -
+ */
+static void SetDhcpDnsServers(u_long dwDns1, u_long dwDns2)
+{
+    // //char szDns[sizeof(SETTINGS_POINTER->Isp.szDns1)];
+
+    // /*
+     // * If not specified get current DNS' from NutOs
+     // */
+    // if ((dwDns1 == 0) && (dwDns2 == 0))
+    // {
+        // NutGetDnsServers(&dwDns1, &dwDns2);
+    // }
+
+    // /*
+     // * If still no DNS servers, get previously saved config
+     // */
+    // if ((dwDns1 == 0) && (dwDns2 == 0))
+    // {
+        // //SettingsGet(szDns, &SETTINGS_POINTER->Isp.szDns1, sizeof(szDns));
+        // if ((dwDns1 = inet_addr(szDns)) == (u_long)-1)
+        // {
+            // dwDns1 = 0;
+        // }
+        // //SettingsGet(szDns, &SETTINGS_POINTER->Isp.szDns2, sizeof(szDns));
+        // if ((dwDns2 = inet_addr(szDns)) == (u_long)-1)
+        // {
+            // dwDns2 = 0;
+        // }
+    // }
+
+    // /*
+     // * Save DNS servers and let NutOs use them
+     // */
+    // if ((dwDns1 != 0) || (dwDns2 != 0))
+    // {
+        // strcpy(szDns, inet_ntoa(dwDns1));
+        // //SettingsSet(szDns, &SETTINGS_POINTER->Isp.szDns1, sizeof(szDns));
+        // strcpy(szDns, inet_ntoa(dwDns2));
+        // //SettingsSet(szDns, &SETTINGS_POINTER->Isp.szDns2, sizeof(szDns));
+
+        // NutDnsConfig2(0, 0, dwDns1, dwDns2);
+    // }
+}
+
+static void SetFixedDnsServers(void)
+{
+    // u_long dwDns1, dwDns2;
+    // char szDns[sizeof(SETTINGS_POINTER->Isp.szDns1)];
+
+    // //SettingsGet(szDns, &SETTINGS_POINTER->Isp.szDns1, sizeof(szDns));
+    // if ((dwDns1 = inet_addr(szDns)) == (u_long)-1)
+    // {
+        // dwDns1 = 0;
+    // }
+
+    // //SettingsGet(szDns, &SETTINGS_POINTER->Isp.szDns2, sizeof(szDns));
+    // if ((dwDns2 = inet_addr(szDns)) == (u_long)-1)
+    // {
+        // dwDns2 = 0;
+    // }
+
+    // NutDnsConfig2(0, 0, dwDns1, dwDns2);
+}
+
+
+static void TryGetDhcp(u_long timeout)
+{
+    /*
+     * Make sure DHCP is started by setting the
+     * *CONFIGURED* IP address to zero.
+     * AND erase the previous IP address, just in case
+     * our MAC address changed.
+     */
+    confnet.cdn_cip_addr = 0;
+    confnet.cdn_ip_addr = 0;
+    confnet.cdn_ip_mask = 0;
+
+#ifdef NUTDEBUG
+    NutTraceTcp(stdout, 1);
+    NutTraceHeap(stdout, 0); // doesn't function !!
+    NutTraceOs(stdout, 0); // doesn't function !!
+#endif
+
+    /*
+     * Start DHCP, and wait for the answer (or timeout)
+     */
+    LogMsg_P(LOG_DEBUG, PSTR("DHCP client started"));
+    if (NutDhcpIfConfig(szEthernetIfName, confnet.cdn_mac, timeout))
+    {
+        LogMsg_P(LOG_DEBUG, PSTR("No DHCP address retrieved"));
+    }
+    else
+    {
+        LogMsg_P(LOG_INFO, PSTR("Ethernet interface %s ready"), inet_ntoa(confnet.cdn_ip_addr));
+
+#ifdef NUTDEBUG
+        NutTraceTcp(stdout, 0);
+        NutTraceHeap(stdout, 0);
+        NutTraceOs(stdout, 0);
+#endif
+    }
+}
+
+/*!
+ * \brief Configures the ethernet interface
+ *
+ * Send out a DHCP request.
+ * An error is returned if no response from a DHCP server
+ * was received
+ *
+ * \param szIfName Name of the device.
+ *
+ * \return OK if success, TError otherwise
+ */
+static INLINE TError NetConfig(CONST char *szIfName)
+{
+    // u_long ulMac;
+    // u_long ulSerialNumber;
+    // u_long ulIpAddress;
+    // u_char byTempValue;
+    // char szIp[sizeof(SETTINGS_POINTER->Isp.szIp)];
+
+    // LogMsg_P(LOG_DEBUG, PSTR("Configuring ethernet %s"), szIfName);
+
+    // /*
+     // * LAN configuration using EEPROM values or DHCP/ARP method.
+     // * If it fails, use fixed values.
+     // */
+    // if (NutNetLoadConfig(szIfName) != 0)
+    // {
+        // /*
+         // * No previous config, ignore
+         // */
+    // }
+
+    // /*
+     // * Override any previously used MAC address by
+     // * the one from our own setup
+     // *
+     // * The MAC address is 00:xx:xx:0y:yy:yy
+     // * where x = 4 digits from the IEEE assigned adres
+     // *       y = 5 digits from our serial number
+     // */
+    // ulMac = SettingsGetMacIeee();
+    // ulMac = __byte_swap4(ulMac) >> 8;
+    // memcpy(confnet.cdn_mac, &ulMac, sizeof(confnet.cdn_mac)/2);
+
+    // //ulSerialNumber = SettingsGetSerialnumber();
+    // ulSerialNumber = __byte_swap4(ulSerialNumber) >> 8;
+    // memcpy(&confnet.cdn_mac[sizeof(confnet.cdn_mac)/2], &ulSerialNumber, sizeof(confnet.cdn_mac)/2);
+
+    // LogMsg_P(LOG_INFO, PSTR("MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x"),
+             // confnet.cdn_mac[0],
+             // confnet.cdn_mac[1],
+             // confnet.cdn_mac[2],
+             // confnet.cdn_mac[3],
+             // confnet.cdn_mac[4],
+             // confnet.cdn_mac[5]);
+
+    // /*
+     // * Save the new MAC address
+     // */
+    // NutNetSaveConfig();
+
+    // /*
+     // * Bring up the network.
+     // * Use fixed settings if DHCP is disabled.
+     // * If the fixed IP address is invalid use DHCP anyway.
+     // */
+    // //SettingsGet(szIp, &SETTINGS_POINTER->Isp.szIp, sizeof(szIp));
+    // ulIpAddress = inet_addr(szIp);
+    // if (ulIpAddress == -1)
+    // {
+        // ulIpAddress = 0;
+    // }
+
+    // //SettingsGet(&byTempValue, &SETTINGS_POINTER->Isp.bDhcp, sizeof(byTempValue));
+    // if ((byTempValue == 0) &&
+        // (ulIpAddress != 0))
+    // {
+        // /*
+         // * Use fixed settings.
+         // */
+        // LogMsg_P(LOG_INFO, PSTR("Fixed IP address used"));
+        // confnet.cdn_cip_addr = inet_addr(szIp);
+
+        // //SettingsGet(szIp, &SETTINGS_POINTER->Isp.szGateway, sizeof(szIp));
+        // confnet.cdn_gateway = inet_addr(szIp);
+        // if (confnet.cdn_gateway == -1)
+        // {
+            // confnet.cdn_gateway = 0;
+        // }
+        // //SettingsGet(szIp, &SETTINGS_POINTER->Isp.szNetmask, sizeof(szIp));
+        // confnet.cdn_ip_mask = inet_addr(szIp);
+        // if (confnet.cdn_ip_mask == -1)
+        // {
+            // confnet.cdn_ip_mask = 0;
+        // }
+
+        // if (NutNetIfConfig(szIfName, confnet.cdn_mac, confnet.cdn_cip_addr, confnet.cdn_ip_mask) == 0)
+        // {
+            // NUTDEVICE *dev;
+
+            // /*
+             // * Add the default route
+             // */
+            // if ((dev = NutDeviceLookup(szIfName)) != 0 && dev->dev_type == IFTYP_NET)
+            // {
+                // NutIpRouteAdd(0, 0, confnet.cdn_gateway, dev);
+            // }
+
+            // LogMsg_P(LOG_INFO, PSTR("Ethernet interface %s ready"), inet_ntoa(confnet.cdn_ip_addr));
+        // }
+        // else
+        // {
+            // LogMsg_P(LOG_ERR, PSTR("Incorrect static Ip settings"));
+        // }
+        // SetFixedDnsServers();
+    // }
+    // else
+    // {
+        // /*
+         // * Use DHCP.
+         // */
+        // TryGetDhcp(20000L);
+        // SetDhcpDnsServers(0,0);
+    // }
+    return(OK);
+}
+
+/*!
+ * \brief Starts the ethernet interface
+ *
+ * Waits for DHCP to finish configuring the interface
+ * If that doesn't happen the interface is configured
+ * using the previous settings.
+ *
+ * \return OK if success, TError otherwise
+ */
+static INLINE TError StartNet(void)
+{
+    TError tError = OK;
+    u_char byTempValue = 0;
+
+    /*
+     * Check if we use DHCP
+     */
+    //SettingsGet(&byTempValue, &SETTINGS_POINTER->Isp.bDhcp, sizeof(byTempValue));
+    if (byTempValue != 0)
+    {
+        if (NutDhcpIsConfigured() == 0)
+        {
+            if (NutNetLoadConfig(szEthernetIfName) ||
+                NutNetIfConfig(szEthernetIfName, confnet.cdn_mac, confnet.cdn_ip_addr, confnet.cdn_ip_mask))
+            {
+                LogMsg_P(LOG_ERR, PSTR("No usable network"));
+                tError = SESSION_NODHCP_NOEEPROM;
+            }
+            else
+            {
+                NUTDEVICE *dev;
+
+                /*
+                 * Add the default route
+                 */
+                if ((dev = NutDeviceLookup(szEthernetIfName)) != 0 && dev->dev_type == IFTYP_NET)
+                {
+                    NutIpRouteAdd(0, 0, confnet.cdn_gateway, dev);
+                }
+
+                LogMsg_P(LOG_WARNING, PSTR("No DHCP response, trying previous network settings..."));
+            }
+            /*
+             * Set current DNS servers
+             */
+            SetDhcpDnsServers(0, 0);
+        }
+    }
+
+
+    /*
+     * Stop all debugging
+     */
+#ifdef NUTDEBUG
+    NutTraceTcp(stdout, 0);
+    NutTraceHeap(stdout, 0);
+    NutTraceOs(stdout, 0);
+#endif
+
+    return(tError);
+}
+
+/*--------------------------------------------------------------------------*/
+/*  Global functions                                                        */
+/*--------------------------------------------------------------------------*/
+
+/*!
+ * \brief Initialises this module
+ *
+ * \note With NutOS 3.2.1 it is not possible
+ *       to have both a ppp and eth0 device
+ *       registered at the same time and still
+ *       have DHCP working.
+ *       So switching interface is currently
+ *       a reason for reboot!
+ *
+ * \return OK if success, TError otherwise
+ */
+TError SessionInit(void)
+{
+    TError tError = OK;
+
+    /*
+     * Initialise globals
+     */
+    g_pUart = 0;
+    g_tError = OK;
+
+    strncpy_P(szEthernetIfName, PSTR("eth0"), sizeof(szEthernetIfName));
+
+    /*
+     * Create a unique hostname from our serial number
+     * Save the hostname in NutOS
+     */
+    //sprintf_P(confos.hostname, PSTR("%.10s%5.5lX"), VersionGetAppProductName(), SettingsGetSerialnumber());
+    NutSaveConfig();
+
+    /*
+     * Try to bring up the selected
+     * interface. Error if none configured
+     */
+    /*
+     * Register Realtek controller
+     */
+    if (NutRegisterDevice(&devEth0, ETH0_BASE, ETH0_IRQ))
+    {
+        LogMsg_P(LOG_EMERG, PSTR("Registering ethernet failed"));
+        tError = SESSION_NODEVICE;
+    }
+    else
+    {
+        /* Let the chip settle down from init */
+        NutSleep(1000);
+    }
+
+    /*
+     * If we have an ethernet interface start it on init
+     */
+    if (tError == OK)
+    {
+        tError = NetConfig(szEthernetIfName);
+    }
+
+    g_tError = tError;
+
+    return(tError);
+}
+
+/*!
+ * \brief Opens a session
+ *
+ * If this succeeds you can start communicating
+ * to the Internet
+ *
+ * \return OK if success, TError otherwise
+ */
+TError SessionOpen(void)
+{
+    TError tError = OK;
+
+    g_tError = OK;
+
+    /*
+     * Try to bring up the selected
+     * interface. Error if none configured
+     */
+    tError = StartNet();
+
+    /*
+     * Display our network settings
+     */
+    if (tError == OK)
+    {
+        u_long ulPrimaryDNS;
+        u_long ulSecondaryDNS;
+
+        /*
+         * Display our IP settings.
+         */
+        LogMsg_P(LOG_INFO, PSTR("  Local IP: %s"), inet_ntoa(confnet.cdn_ip_addr));
+        LogMsg_P(LOG_INFO, PSTR("Gateway IP: %s"), inet_ntoa(confnet.cdn_gateway));
+
+        NutGetDnsServers(&ulPrimaryDNS, &ulSecondaryDNS);
+        LogMsg_P(LOG_INFO, PSTR("  Pri. DNS: %s"), inet_ntoa(ulPrimaryDNS));
+        LogMsg_P(LOG_INFO, PSTR("  Sec. DNS: %s"), inet_ntoa(ulSecondaryDNS));
+    }
+
+    g_tError = tError;
+
+    return(tError);
+}
+
+/*!
+ * \brief Return the session status
+ *
+ * Call to check the status of a session.
+ *
+ * \return see above
+ */
+TError SessionStatus(void)
+{
+    return(g_tError);
+}
+
+/*!
+ * \brief Stop the session
+ *
+ * \return OK if success, TError otherwise
+ */
+TError SessionClose(void)
+{
+    g_tError = USER_ABORT;
+
+    return(OK);
+}
+
+/*@}*/

+ 228 - 0
spidrv.c

@@ -0,0 +1,228 @@
+/* ========================================================================
+ * [PROJECT]    SIR100
+ * [MODULE]     SPI
+ * [TITLE]      SPI source file
+ * [FILE]       spi.c
+ * [VSN]        1.0
+ * [CREATED]    06102006
+ * [LASTCHNGD]  06102006
+ * [COPYRIGHT]  Copyright (C) STREAMIT BV 2010
+ * [PURPOSE]    contains all interface- and low-level routines to
+ *              control audiofunctions of the VS1003//AT45DBXX/MMC-SD(HC)card
+ * ======================================================================== */
+
+#define LOG_MODULE  LOG_SPIDRV_MODULE
+
+/*-------------------------------------------------------------------------*/
+/* includes                                                                */
+/*-------------------------------------------------------------------------*/
+#include <stdio.h>
+
+#include "system.h"
+#include "spidrv.h"
+#include "portio.h"
+#include "log.h"
+
+#include "vs10xx.h"
+
+#include <sys/timer.h>
+
+/*-------------------------------------------------------------------------*/
+/* local defines                                                           */
+/*-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/* typedefs & structs                                                      */
+/*-------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/* local variable definitions                                              */
+/*-------------------------------------------------------------------------*/
+static u_char g_Speedmode;
+
+/*-------------------------------------------------------------------------*/
+/* local routines (prototyping)                                            */
+/*-------------------------------------------------------------------------*/
+
+/*!
+ * \addtogroup Drivers
+ */
+
+/*@{*/
+
+/*-------------------------------------------------------------------------*/
+/*                         start of code                                   */
+/*-------------------------------------------------------------------------*/
+/*!
+ * \brief enable SPI-logic for given device
+ *
+ * SPI bits:
+ * SPSR:  SPI2X         double speed as set in SPR0, SPR1
+ * SPCR:  SPIE:         SPI interrupt enable
+ *        SPE:          SPI enable
+ *        SPR1, SPR0:   clockrate
+ *        MSTR:         set to master
+ *
+ * SPI2X SPR1 SPR0 for a ATMEGA @  14.7456 MHz
+ *
+ * 1     0    0:  fosc/2  = 136 ns  -> 7.37 MHz
+ * 0     0    0:  fosc/4  = 271 ns  -> 3.6864 MHz
+ * 1     0    1:  fosc/8  = 542 ns  -> 1.8432 MHz
+ * 0     0    1:  fosc/16 = 1085 ns -> 0.9216 MHz
+ *
+ */
+
+void SPIselect(TSPIDevice Device)
+{
+
+    // set SPI-speed for selected device
+    if (Device==SPI_DEV_VS10XX)
+    {
+        if (g_Speedmode==SPEED_SLOW)
+        {
+            // set speed to Fosc/8
+            outb(SPSR, BV(SPI2X));
+            outb(SPCR, BV(MSTR) | BV(SPE) | BV(SPR0));
+        }
+        else if (g_Speedmode==SPEED_FAST)
+        {
+            // set speed to Fosc/4
+            outb(SPSR, 0);
+            outb(SPCR, BV(MSTR) | BV(SPE));
+        }
+        else if (g_Speedmode==SPEED_ULTRA_FAST)
+        {
+            // set speed to Fosc/2
+            outb(SPSR, BV(SPI2X));
+            outb(SPCR, BV(MSTR) | BV(SPE));
+        }
+        else
+        {
+            LogMsg_P(LOG_ERR,PSTR("invalid Speed"));
+        }
+    }
+    else if (Device==SPI_DEV_FLASH)
+    {
+        // set speed for flash to Fosc/2
+        outb(SPSR, BV(SPI2X));
+        outb(SPCR, BV(MSTR) | BV(SPE));
+    }
+    else
+    {
+        // set speed for flash to Fosc/2
+//        outb(SPSR, BV(SPI2X));
+//        outb(SPCR, BV(MSTR) | BV(SPE));
+
+        // set speed for card to Fosc/4
+        outb(SPSR, 0);
+        outb(SPCR, BV(MSTR) | BV(SPE));
+
+        // set speed for card to Fosc/8
+//        outb(SPSR, BV(SPI2X));
+//        outb(SPCR, BV(MSTR) | BV(SPE) | BV(SPR0));
+
+        // set speed for card to Fosc/16
+//        outb(SPSR, 0);
+//        outb(SPCR, BV(MSTR) | BV(SPE) | BV(SPR0));
+    }
+
+    // enable selected device
+    switch (Device)
+    {
+        case SPI_DEV_VS10XX:
+            {
+                sbi(FLASH_OUT_WRITE, FLASH_ENABLE);    // disable serial Flash
+                sbi(MMCVS_OUT_WRITE, MMC_ENABLE);      // disable MMC/SDHC
+                sbi(MMCVS_OUT_WRITE, VS_ENABLE);       // enable VS10XX
+                break;
+            }
+        case SPI_DEV_FLASH:
+            {
+                sbi(MMCVS_OUT_WRITE, MMC_ENABLE);      // disable MMC/SDHC
+                cbi(MMCVS_OUT_WRITE, VS_ENABLE);       // disable VS10XX
+                cbi(FLASH_OUT_WRITE, FLASH_ENABLE);    // enable serial Flash
+                break;
+            }
+        case SPI_DEV_MMC:
+            {
+                sbi(FLASH_OUT_WRITE, FLASH_ENABLE);    // disable serial Flash
+                cbi(MMCVS_OUT_WRITE, VS_ENABLE);       // disable VS10XX
+                cbi(MMCVS_OUT_WRITE, MMC_ENABLE);      // enable MMC/SDHC
+                break;
+            }
+        default: break;
+    }
+}
+
+/*!
+ * \brief disable SPI-logic for ALL devices
+ *
+ */
+void SPIdeselect()
+{
+    sbi(FLASH_OUT_WRITE, FLASH_ENABLE);    // disable serial Flash
+    cbi(MMCVS_OUT_WRITE, VS_ENABLE);       // disable VS10XX
+    sbi(MMCVS_OUT_WRITE, MMC_ENABLE);      // disable MMC/SDHC
+}
+
+/*!
+ * \brief not all devices can operate always on maximum speed. This routine determines the several speed modes.
+ *
+ */
+void SPImode(u_char data)
+{
+    g_Speedmode = data;
+}
+
+u_char SPIgetmode(void)
+{
+    return(g_Speedmode);
+}
+/*!
+ * \brief send a byte using SPI, ignore result
+ *
+ */
+void SPIputByte(u_char data)
+{
+    SPDR = data;
+    while (!(SPSR & (1<<SPIF)));     // wait for completion
+}
+
+/*!
+ * \brief read byte using SPI, don't use any input
+ *
+ */
+u_char SPIgetByte()
+{
+    SPDR = 0xFF;                     // dummy
+    while (!(SPSR & (1<<SPIF)));     // wait for completion
+    return(SPDR);                    // return with byte shifted in from receiver
+}
+
+/*!
+ * \brief send byte using SPI, return result
+ *
+ */
+u_char SPItransferByte(u_char data)
+{
+    SPDR = data;
+    while (!(SPSR & (1<<SPIF)));     // wait for completion
+    return(SPDR);                    // return with byte shifted in from receiver
+}
+
+/*!
+ * \brief Initialise SPI registers (speed)
+ *
+ *  Note that the IO-lines (SCK, SI, SO) are already set in 'SysInitIO()'
+ *  in the main-module
+ *
+ */
+void SPIinit()
+{
+    sbi(FLASH_OUT_WRITE, FLASH_ENABLE);    // disable serial Flash
+    cbi(MMCVS_OUT_WRITE, VS_ENABLE);       // disable VS10XX
+    sbi(MMCVS_OUT_WRITE, MMC_ENABLE);      // disable MMC/SDHC
+}
+
+
+/*  ÍÍÍÍ  End Of File  ÍÍÍÍÍÍÍÍ ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ */
+
+

+ 151 - 0
uart0driver.c

@@ -0,0 +1,151 @@
+/*!
+ * Copyright (C) 2003 by Streamit All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgement:
+ *
+  *    This product includes software developed by Streamit
+ *    and its contributors.
+ *
+ * THIS SOFTWARE IS PROVIDED BY STREAMIT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CALL DIRECT
+ * CELLULAR SOLUTIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * For additional information see http://www.streamit.nl/
+ */
+
+/*!
+ *
+ * COPYRIGHT STREAMIT BV 2010
+ * Tested except for the DTR code. This will be done when the final hardware is available
+ *
+ *
+ * [Note] this UART0 is used for serial communication with LTP (or a PC in general).
+ */
+
+
+#define LOG_MODULE  LOG_UART0DRIVER_MODULE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <io.h>
+
+#include <dev/uartavr.h>
+//#include <sys/heap.h>
+#include <sys/thread.h>
+#include <sys/timer.h>
+
+//#pragma text:appcode
+
+#include "system.h"
+#include "uart0driver.h"
+
+
+//----------------------------------------------------------
+
+static FILE *stream=NULL;
+
+//----------------------------------------------------------
+
+/*!
+ * \brief Handle input.
+ */
+// THREAD(Uart0KeyEvents, arg)
+// {
+    // NutThreadSetPriority(254);  // low prio
+    // for (;;)
+    // {
+        // if (stream==NULL)
+        // {
+            // NutSleep(2000);           //Mhe
+            // continue;
+        // }
+
+       // CommandHandler(stream);
+    // }
+// }
+
+//----------------------------------------------------------
+
+/*!
+ * \brief return stream that is connected to terminal program (serial or TCP/IP)
+ */
+FILE *Uart0DriverGetStream(void)
+{
+    return(stream);
+}
+
+/*!
+ * \brief Uart0 process initialisation. Uses stdout in combination with Uart0.
+ */
+void Uart0DriverInit(void)
+{
+    stream = NULL;
+
+    // register Uart0
+    NutRegisterDevice(&devUart0, 0, 0);
+}
+
+/*!
+ * \brief Creates a thread to handle incoming data from User.
+ */
+void Uart0DriverStart(void)
+{
+    u_long baud = 115200;
+    char DeviceNameBuffer[6];
+    char FileModeBuffer[3];
+
+    strcpy_P(DeviceNameBuffer, PSTR("uart0"));
+    strcpy_P(FileModeBuffer, PSTR("w"));
+
+    /* Open the stream, connect to stdout */
+    if ((stream=freopen(DeviceNameBuffer, FileModeBuffer, stdout)) != NULL)
+    {
+        _ioctl(_fileno(stream), UART_SETSPEED, &baud);
+    }
+
+    // if (stream != NULL)
+    // {
+        // if (GetThreadByName(DeviceNameBuffer) == NULL)
+        // {
+            // NutThreadCreate(DeviceNameBuffer, Uart0KeyEvents, 0, 512);
+        // }
+    // }
+}
+
+void Uart0DriverSetCookedMode(u_long CookedMode)
+{
+    _ioctl(_fileno(stream), UART_SETCOOKEDMODE, &CookedMode);
+}
+
+/*!
+ * \brief Close the stream.
+ */
+void Uart0DriverStop(void)
+{
+    if (stream)
+    {
+        // needed? (void)_close(_fileno(stream));
+        (void)fclose(stream);
+        stream = NULL;
+    }
+}

+ 267 - 0
util.c

@@ -0,0 +1,267 @@
+/*
+ *  Copyright STREAMIT BV, 2010.
+ *
+ *  Project             : SIR
+ *  Module              : Util
+ *  File name  $Workfile: Util.c  $
+ *       Last Save $Date: 2006/05/11 9:53:22  $
+ *             $Revision: 0.1  $
+ *  Creation Date       : 2006/05/11 9:53:22
+ *
+ *  Description         : Utility functions for the SIR project
+ *
+ */
+
+#define LOG_MODULE  LOG_UTIL_MODULE
+
+/*--------------------------------------------------------------------------*/
+/*  Include files                                                           */
+/*--------------------------------------------------------------------------*/
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/heap.h>
+
+//#pragma text:appcode
+
+#include "system.h"
+#include "log.h"
+
+#include "util.h"
+
+/*--------------------------------------------------------------------------*/
+/*  Constant definitions                                                    */
+/*--------------------------------------------------------------------------*/
+#ifdef DEBUG
+//#define UTIL_DEBUG
+#endif /* #ifdef DEBUG */
+
+/*--------------------------------------------------------------------------*/
+/*  Type declarations                                                       */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Local variables                                                         */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Global variables                                                        */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Local functions                                                         */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Global functions                                                        */
+/*--------------------------------------------------------------------------*/
+
+/*!
+ * \brief Allocate memory.
+ *
+ * \param   unSize [in] Amount of memory to allocate.
+ *
+ * \return  pointer to the allocated memory.
+ *          NULL if there is no memory left or you requested 0 size.
+ */
+void *MyMalloc(unsigned int unSize)
+{
+    void *pResult = NULL;
+
+    if ((unSize != 0) &&
+        ((pResult = NutHeapAlloc(unSize)) == NULL))
+    {
+        LogMsg_P(LOG_ERR, PSTR("No memory [%u]"), unSize);
+    }
+    return (pResult);
+}
+
+/*!
+ * \brief Create a copy of a string.
+ *
+ * Allocates sufficient memory from heap for a copy of the string
+ * and does the copy.
+ *
+ * \param   str [in] Pointer to the string to copy.
+ *
+ * \return  A pointer to the new string.
+ *          NULL if allocating memory failed.
+ */
+char *strdup(CONST char *str)
+{
+    char *copy = NULL;
+
+    if (str != NULL)
+    {
+        size_t siz = strlen(str) + 1;
+
+        if ((copy = MyMalloc(siz)) != NULL)
+        {
+            memcpy(copy, str, siz);
+        }
+    }
+    return (copy);
+}
+
+/*!
+ * \brief Allocate new memory if needed.
+ *
+ * Checks if a memory block is large enough to hold additional data.
+ * If it is not, the buffer is reallocated so it can hold the additional data.
+ *
+ * \param   ppcBuf [in,out] Address of a pointer to a memory block.
+ * \param   punBufSize [in,out] The currently allocated size, [out] the new blocksize
+ * \param   unBufInUse [in] Currently in use.
+ * \param   unSizeNeeded [in] Size of the data to add.
+ *
+ * \return  0 when the buffer is large enough to add the data.
+ *          -1 if no new memory could be allocated.
+ */
+int BufferMakeRoom(char **ppcBuf, unsigned int *punBufSize, unsigned int unBufInUse, unsigned int unSizeNeeded)
+{
+#ifdef UTIL_DEBUG
+    LogMsg_P(LOG_DEBUG, PSTR("Have %u,Need %u"), (*punBufSize - unBufInUse), unSizeNeeded);
+#endif /* #ifdef UTIL_DEBUG */
+
+    if (unSizeNeeded > (*punBufSize - unBufInUse))
+    {
+        unsigned int unBlockSize = 256;
+        char *pNewBuf = NULL;
+
+        if (unBlockSize < unSizeNeeded)
+        {
+            unBlockSize = unSizeNeeded;
+        }
+
+        pNewBuf = MyMalloc(*punBufSize + unBlockSize);
+        if (pNewBuf == NULL)
+        {
+            return (-1);
+        }
+        else
+        {
+            *punBufSize += unBlockSize;
+
+#ifdef UTIL_DEBUG
+            LogMsg_P(LOG_DEBUG, PSTR("MemBlock is %u now"), *punBufSize);
+#endif /* #ifdef UTIL_DEBUG */
+
+            if (*ppcBuf != NULL)
+            {
+                memcpy(pNewBuf, *ppcBuf, unBufInUse);
+            }
+            MyFree(*ppcBuf);
+            *ppcBuf = pNewBuf;
+        }
+    }
+    return (0);
+}
+
+/*!
+ * \brief Add a string to a memory block.
+ *
+ * \param   ppcBuf [in] Address of a pointer to a memory block.
+ * \param   punBufSize [in,out] The currently allocated size, [out] the new blocksize
+ * \param   unBufInUse [in,out] Currently in use, [out] in use after adding the string
+ * \param   pszString [in] String to add.
+ *
+ * \return  0 when the string was successfully added.
+ *          -1 on errors.
+ */
+int BufferAddString(char **ppcBuf, unsigned int *punBufSize, unsigned int *punBufInUse, CONST char *pszString)
+{
+    unsigned int unStringLen = 0;
+
+    if (pszString == NULL)
+    {
+        return (-1);
+    }
+
+    /*
+     * Add the line to the response buffer
+     */
+    unStringLen = strlen(pszString);
+    if (unStringLen > 0)
+    {
+        unStringLen += 1;   /* Correct for \0 */
+        if (BufferMakeRoom(ppcBuf, punBufSize, *punBufInUse, unStringLen) < 0)
+        {
+            return (-1);
+        }
+        else
+        {
+            /* Only count one \0 (so, in the InUse counter, only count the \0 the very first time) */
+            if (*punBufInUse != 0)
+            {
+                *punBufInUse -= 1;
+            }
+            memcpy(&(*ppcBuf)[*punBufInUse], pszString, unStringLen);
+            *punBufInUse += unStringLen;
+        }
+    }
+    return (0);
+}
+
+/*!
+ * \brief Find a descriptor for a piece of text.
+ *
+ * A LookUp Table (LUT) is searched for matching text.
+ * The row in which the match was found is returned.
+ * If no match was found, the descriptor of the last entry
+ * is returned. As a result, the table should always contain
+ * at least one entry. And that last one should be the
+ * default/empty or error value, depending on your needs.
+ *
+ * \note    The compare used is not case sensitive.
+ * \note    If byLen is 0, only the first part of pcText
+ *          needs to match.
+ *          E.g. "foo" in the LUT will match "foo",
+ *          E.g. "foo" in the LUT will match "foobar"
+ *          By passing the length of pcText (not including the
+ *          \0 character), will force an exact match.
+ *          E.g. "foo" in the LUT will match "foo",
+ *          E.g. "foo" in the LUT will not match "foobar",
+ *
+ * \param   tLookupTable [in] The lookup table.
+ * \param   pcText [in] The text to find the value for
+ *          this does not need to be 0 terminated.
+ * \param   byLen [in] See notes above.
+ *
+ * \return  The descriptor of the row that matched.
+ *          Or the descriptor of the last row if no match.
+ */
+void *LutSearch(CONST tLut tLookupTable[],
+                CONST char *pcText,
+                unsigned char byLen)
+{
+    unsigned char byRow = 0;
+
+    for (byRow = 0; byRow < (unsigned char)(-1); byRow++)
+    {
+        unsigned char byTagLen = 0;
+
+        if (tLookupTable[byRow].pszTag == NULL)
+        {
+            break;
+        }
+
+        byTagLen = strlen_P(tLookupTable[byRow].pszTag);
+
+        if ((byLen != 0) && (byTagLen != byLen))
+        {
+            continue;   /* not the same size; keep looking */
+        }
+
+        /* case-insensitive compare */
+        if (strncasecmp_P(pcText, tLookupTable[byRow].pszTag, byTagLen) == 0)
+        {
+            break;
+        }
+    }
+
+#ifdef UTIL_DEBUG
+    LogMsg_P(LOG_DEBUG, PSTR("Match %d"), byRow);
+#endif /* #ifdef UTIL_DEBUG */
+
+    return (tLookupTable[byRow].pDesc);
+}

+ 965 - 0
vs10xx.c

@@ -0,0 +1,965 @@
+/*
+ * Copyright (C) 2003 by Pavel Chromy. All rights reserved.
+ * Copyright (C) 2001-2003 by egnite Software GmbH. All rights reserved.
+ * Copyright (C) 2003-2004 by Streamit. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
+ * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * For additional information see http://www.ethernut.de/
+ * -
+ *
+ * This software has been inspired by all the valuable work done by
+ * Jesper Hansen <jesperh@telia.com>. Many thanks for all his help.
+ */
+
+/*
+ * [COPYRIGHT]  Copyright (C) STREAMIT BV
+ *
+ *
+ */
+#define LOG_MODULE  LOG_VS10XX_MODULE
+
+#include <stdlib.h>
+
+#include <sys/atom.h>
+#include <sys/event.h>
+#include <sys/timer.h>
+#include <sys/heap.h>
+
+#include <dev/irqreg.h>
+
+#include <sys/bankmem.h>
+
+#if (NUTOS_VERSION >= 433)
+    #include <cpu_load.h>
+#endif
+
+
+#include "system.h"
+#include "vs10xx.h"
+#include "platform.h"
+#include "log.h"
+#include "portio.h"    // for debug purposes only
+#include "spidrv.h"    // for debug purposes only
+#include "watchdog.h"
+
+
+/*-------------------------------------------------------------------------*/
+/* global variable definitions                                             */
+/*-------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/* local variable definitions                                              */
+/*-------------------------------------------------------------------------*/
+
+#define LOW         0
+#define HIGH        1
+
+#define MONO        0
+#define STEREO      1
+
+#define VsDeselectVs()  SPIdeselect()
+#define VsSelectVs()    SPIselect(SPI_DEV_VS10XX)
+
+
+/*-------------------------------------------------------------------------*/
+/* local variable definitions                                              */
+/*-------------------------------------------------------------------------*/
+static volatile u_char vs_status = VS_STATUS_STOPPED;
+static u_short g_vs_type;
+static u_char VsPlayMode;
+
+
+static void VsLoadProgramCode(void);
+
+/*-------------------------------------------------------------------------*/
+/* local routines (prototyping)                                            */
+/*-------------------------------------------------------------------------*/
+
+#define CODE_SIZE 437
+static prog_char atab[CODE_SIZE] = { /* Register addresses */
+    7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+    6, 6, 6, 6, 6
+};
+
+prog_int dtab[CODE_SIZE] = { /* Data to write */
+    0x8030, 0x0030, 0x0717, 0xb080, 0x3c17, 0x0006, 0x5017, 0x3f00,
+    0x0024, 0x0006, 0x2016, 0x0012, 0x578f, 0x0000, 0x10ce, 0x2912,
+    0x9900, 0x0000, 0x004d, 0x4080, 0x184c, 0x0006, 0x96d7, 0x2800,
+    0x0d55, 0x0000, 0x0d48, 0x0006, 0x5b50, 0x3009, 0x0042, 0xb080,
+    0x8001, 0x4214, 0xbc40, 0x2818, 0xc740, 0x3613, 0x3c42, 0x3e00,
+    0xb803, 0x0014, 0x1b03, 0x0015, 0x59c2, 0x6fd6, 0x0024, 0x3600,
+    0x9803, 0x2812, 0x57d5, 0x0000, 0x004d, 0x2800, 0x2b40, 0x36f3,
+    0x0024, 0x804c, 0x3e10, 0x3814, 0x3e10, 0x780a, 0x3e13, 0xb80d,
+    0x3e03, 0xf805, 0x0006, 0x5595, 0x3009, 0x1415, 0x001b, 0xffd4,
+    0x0003, 0xffce, 0x0001, 0x000a, 0x2400, 0x16ce, 0xb58a, 0x0024,
+    0xf292, 0x9400, 0x6152, 0x0024, 0xfe02, 0x0024, 0x48b2, 0x0024,
+    0x454a, 0xb601, 0x36f3, 0xd805, 0x36f3, 0x980d, 0x36f0, 0x580a,
+    0x2000, 0x0000, 0x36f0, 0x1814, 0x8061, 0x3613, 0x0024, 0x3e12,
+    0xb817, 0x3e12, 0x3815, 0x3e05, 0xb814, 0x3625, 0x0024, 0x0000,
+    0x800a, 0x3e10, 0xb803, 0x4194, 0xb805, 0x3e11, 0x0024, 0x3e11,
+    0xb807, 0x3e14, 0x7812, 0x3e14, 0xf80d, 0x3e03, 0xf80e, 0x0006,
+    0x0051, 0x2800, 0x24d5, 0x0000, 0x0024, 0xb888, 0x0012, 0x6404,
+    0x0405, 0x0000, 0x0024, 0x2800, 0x2158, 0x4094, 0x0024, 0x2400,
+    0x2102, 0x0000, 0x0024, 0x6498, 0x0803, 0xfe56, 0x0024, 0x48b6,
+    0x0024, 0x4dd6, 0x0024, 0x3a10, 0xc024, 0x32f0, 0xc024, 0xfe56,
+    0x0024, 0x48b6, 0x0024, 0x4dd6, 0x0024, 0x4384, 0x4483, 0x6396,
+    0x888c, 0xf400, 0x40d5, 0x3d00, 0x8024, 0x0006, 0x0091, 0x003f,
+    0xfec3, 0x0006, 0x0053, 0x3101, 0x8024, 0xfe60, 0x0024, 0x48be,
+    0x0024, 0xa634, 0x0c03, 0x4324, 0x0024, 0x4284, 0x2c02, 0x0006,
+    0x0011, 0x2800, 0x24d8, 0x3100, 0x8024, 0x0006, 0x5011, 0x3900,
+    0x8024, 0x0006, 0x0011, 0x3100, 0x984c, 0x4284, 0x904c, 0xf400,
+    0x4088, 0x2800, 0x2845, 0x0000, 0x0024, 0x3cf0, 0x3840, 0x3009,
+    0x3841, 0x3009, 0x3810, 0x2000, 0x0000, 0x0000, 0x2788, 0x3009,
+    0x1bd0, 0x2800, 0x2880, 0x3009, 0x1b81, 0x34f3, 0x1bcc, 0x36f3,
+    0xd80e, 0x36f4, 0xd80d, 0x36f4, 0x5812, 0x36f1, 0x9807, 0x36f1,
+    0x1805, 0x36f0, 0x9803, 0x3405, 0x9014, 0x36f3, 0x0024, 0x36f2,
+    0x1815, 0x2000, 0x0000, 0x36f2, 0x9817, 0x80ad, 0x3e12, 0xb817,
+    0x3e12, 0x3815, 0x3e05, 0xb814, 0x3615, 0x0024, 0x0000, 0x800a,
+    0x3e10, 0x7802, 0x3e10, 0xf804, 0x3e11, 0x7810, 0x3e14, 0x7812,
+    0x2913, 0xc980, 0x3e14, 0xc024, 0x2913, 0xc980, 0x4088, 0x184c,
+    0xf400, 0x4005, 0x0000, 0x18c0, 0x6400, 0x0024, 0x0000, 0x1bc0,
+    0x2800, 0x3095, 0x0030, 0x0310, 0x2800, 0x3f80, 0x3801, 0x4024,
+    0x6400, 0x0024, 0x0000, 0x1a40, 0x2800, 0x3755, 0x0006, 0x55d0,
+    0x0000, 0x7d03, 0xb884, 0x184c, 0x3009, 0x3805, 0x3009, 0x0000,
+    0xff8a, 0x0024, 0x291d, 0x7b00, 0x48b2, 0x0024, 0x0000, 0x1841,
+    0x0006, 0x5010, 0x408a, 0xb844, 0x2900, 0x1300, 0x4088, 0x0024,
+    0x3000, 0x1bcc, 0x6014, 0x0024, 0x0030, 0x0351, 0x2800, 0x36d5,
+    0x0000, 0x0024, 0x0006, 0x0011, 0x3100, 0x0024, 0x0030, 0x0351,
+    0x3800, 0x0024, 0x2800, 0x3f80, 0x3901, 0x4024, 0x6400, 0x0024,
+    0x0030, 0x03d0, 0x2800, 0x3f55, 0x0000, 0x7d03, 0x0006, 0x55d0,
+    0xb884, 0x184c, 0x3009, 0x3805, 0x3009, 0x0000, 0xff8a, 0x0024,
+    0x291d, 0x7b00, 0x48b2, 0x0024, 0x408a, 0x9bcc, 0x0000, 0x1841,
+    0x2800, 0x3b55, 0x0006, 0x5010, 0x689a, 0x0024, 0x3000, 0x0024,
+    0x6014, 0x0024, 0x0030, 0x0392, 0x2800, 0x3e85, 0x0006, 0x0091,
+    0x0006, 0x0011, 0x0000, 0x1852, 0x0006, 0x0053, 0xb880, 0x2400,
+    0x0006, 0x0091, 0x3804, 0x8024, 0x0030, 0x0392, 0x3b00, 0x0024,
+    0x3901, 0x4024, 0x2800, 0x3f80, 0x3a01, 0x4024, 0x3801, 0x4024,
+    0xb880, 0x1bd3, 0x36f4, 0x5812, 0x36f1, 0x5810, 0x36f0, 0xd804,
+    0x36f0, 0x5802, 0x3405, 0x9014, 0x36f3, 0x0024, 0x36f2, 0x1815,
+    0x2000, 0x0000, 0x36f2, 0x9817, 0x0030
+};
+
+/*!
+ * \addtogroup VS1003B
+ */
+
+/*@{*/
+
+/*-------------------------------------------------------------------------*/
+/*                         start of code                                   */
+/*-------------------------------------------------------------------------*/
+
+
+/*!
+ * \brief Write a specified number of bytes to the VS10XX data interface.
+ *
+ * Decoder interrupts must have been disabled before calling this function.
+ */
+static void VsSdiWrite(CONST u_char * data, u_short len)
+{
+    VsSelectVs();
+
+    while (len--)
+    {
+        SPIputByte(*data);
+        data++;
+    }
+
+    VsDeselectVs();
+    return;
+}
+
+/*!
+ * \brief Write a specified number of bytes from program space to the
+ *        VS10XX data interface.
+ *
+ * This function is similar to VsSdiWrite() except that the data is
+ * located in program space.
+ */
+static void VsSdiWrite_P(PGM_P data, u_short len)
+{
+    VsSelectVs();
+
+    while (len--)
+    {
+        SPIputByte(PRG_RDB(data));
+        data++;
+    }
+
+    VsDeselectVs();
+    return;
+}
+
+
+/*!
+ * \brief Write to a decoder register.
+ *
+ * Decoder interrupts must have been disabled before calling this function.
+ */
+void VsRegWrite(u_char reg, u_short data)
+{
+    u_char spimode;
+
+    spimode = SPIgetmode();
+    SPImode(SPEED_SLOW);
+
+    VsSelectVs();
+
+    cbi(VS_XCS_PORT, VS_XCS_BIT);
+
+    SPIputByte(VS_OPCODE_WRITE);
+    SPIputByte(reg);
+    SPIputByte((u_char) (data >> 8));
+    SPIputByte((u_char) data);
+
+    sbi(VS_XCS_PORT, VS_XCS_BIT);
+
+    VsDeselectVs();
+
+    SPImode(spimode);
+
+    return;
+}
+
+/*!
+ * \brief determine if the stream is valid. If true, returns value; if false returns 0
+ *
+ */
+u_short VsStreamValid(void)
+{
+    u_short value;
+    u_short result;
+
+    value = VsRegInfo(VS_HDAT1_REG);
+
+    if (value > 0xFFE0)
+    {
+        value = 0xFFE0;
+    }
+
+    switch (value)
+    {
+        case 0x7665: /* WAV */
+        case 0x4154: /* AAC DTS */
+        case 0x4144: /* AAC ADIF */
+        case 0x4D34: /* AAC .mp4 / .m4a */
+        case 0x574D: /* WMA without broadcast patch*/
+            {
+                result= (g_vs_type==VS_VS1003? 0 : value);
+            }
+            break;
+
+        case 0x576d: /* WMA with broadcast patch*/
+        case 0x4D54: /* MIDI */
+        case 0xFFE0: /* MP3 */
+            {
+                result=value;
+            }
+            break;
+
+        default:
+            {
+                result=0;
+            }
+    }
+
+    return(result);
+}
+/*
+ * \brief Read from a register.
+ *
+ * Decoder interrupts must have been disabled before calling this function.
+ *
+ * \return Register contents.
+ */
+static u_short VsRegRead(u_char reg)
+{
+    u_short data;
+    u_char spimode;
+
+    spimode = SPIgetmode();
+    SPImode(SPEED_SLOW);
+
+    VsSelectVs();
+
+    cbi(VS_XCS_PORT, VS_XCS_BIT);
+
+    SPIputByte(VS_OPCODE_READ);
+    SPIputByte(reg);
+
+    data=SPIgetByte()<<8;           // get MSB
+    data |= SPIgetByte();           // get LSB
+
+    sbi(VS_XCS_PORT, VS_XCS_BIT);
+
+    VsDeselectVs();
+    SPImode(spimode);
+
+    return(data);
+}
+
+/*!
+ * \brief read data from a specified register from the VS10XX
+ *
+ */
+u_short VsRegInfo(u_char reg)
+{
+    u_char ief;
+    u_short value;
+
+    ief = VsPlayerInterrupts(0);
+    value = VsRegRead(reg);
+    VsPlayerInterrupts(ief);
+
+    return(value);
+}
+
+
+/*!
+ * \brief Enable or disable player interrupts.
+ *
+ * This routine is typically used by applications when dealing with
+ * unprotected buffers.
+ *
+ * \param enable Disables interrupts when zero. Otherwise interrupts
+ *               are enabled.
+ *
+ * \return Zero if interrupts were disabled before this call.
+ */
+u_char VsPlayerInterrupts(u_char enable)
+{
+    u_char rc;
+
+    NutEnterCritical();
+    rc = (inb(EIMSK) & _BV(VS_DREQ_BIT)) != 0;
+    if (enable)
+    {
+        sbi(EIMSK, VS_DREQ_BIT);
+    }
+    else
+    {
+        cbi(EIMSK, VS_DREQ_BIT);
+    }
+    NutExitCritical();
+
+    return(rc);
+}
+
+/*
+ * \brief Feed the decoder with data.
+ *
+ * This function serves two purposes:
+ * - It is called by VsPlayerKick() to initially fill the decoder buffer.
+ * - It is used as an interrupt handler for the decoder.
+ *
+ * Note that although this routine is an ISR, it is called from 'VsPlayerKick' as well
+ */
+static void VsPlayerFeed(void *arg)
+{
+    u_short j = 32;
+    u_char ief;
+
+    char *bp;
+    size_t consumed;
+    size_t available;
+
+    // leave if not running.
+    if ((vs_status != VS_STATUS_RUNNING) || (bit_is_clear(VS_DREQ_PIN, VS_DREQ_BIT)))
+    {
+        return;
+    }
+
+    /*
+     * We are hanging around here some time and may block other important
+     * interrupts. Disable decoder interrupts and enable global interrupts.
+     */
+    ief = VsPlayerInterrupts(0);
+
+    sei();
+
+    bp = 0;
+    consumed = 0;
+    available = 0;
+
+    /*
+     * Feed the decoder with j bytes or we ran out of data.
+     */
+    VsSelectVs();
+
+    do
+    {
+        if (consumed >= available)
+        {
+            // Commit previously consumed bytes.
+            if (consumed)
+            {
+                NutSegBufReadCommit(consumed);
+                consumed = 0;
+            }
+            // All bytes consumed, request new.
+            bp = NutSegBufReadRequest(&available);
+            if (available == 0)
+            {
+                /* End of stream. */
+                vs_status = VS_STATUS_EOF;
+                break;
+            }
+        }
+        if (available != 0) // We have some data in the buffer, feed it.
+        {
+            SPIputByte(*bp);
+            bp++;
+            consumed++;
+        }
+        /*
+         * appearantly DREQ goes low when less then 32 byte are available
+         * in the internal buffer (2048 bytes)
+         */
+
+        /* Allow 32 bytes to be sent as long as DREQ is set. This includes the one in progress */
+        if (bit_is_set(VS_DREQ_PIN, VS_DREQ_BIT))
+        {
+            j = 32;
+        }
+    } while (--j);          // bug solved: j-- counts one too many....
+
+    VsDeselectVs();
+
+    /* Finally re-enable the producer buffer. */
+    NutSegBufReadLast(consumed);
+    VsPlayerInterrupts(ief);
+}
+
+
+/*!
+ * \brief Start playback.
+ *
+ * This routine will send the first MP3 data bytes to the
+ * decoder. The data buffer
+ * should have been filled before calling this routine.
+ *
+ * Decoder interrupts will be enabled.
+ *
+ * \return 0 on success, -1 otherwise.
+ */
+int VsPlayerKick(void)
+{
+    /*
+     * Start feeding the decoder with data.
+     */
+    if (vs_status != VS_STATUS_RUNNING)
+    {
+        VsPlayerInterrupts(0);
+        /*
+         *  for the VS1003 we need an extra reset
+         *  here before we start playing a stream...
+         */
+//        VsPlayerSetMode(VS_SM_RESET);
+//        NutDelay(10);
+//        LogMsg_P(LOG_DEBUG,PSTR("Kick: CLOCKF = [0x%02X]"),VsRegRead(VS_CLOCKF_REG));
+//        LogMsg_P(LOG_DEBUG,PSTR("Kick: CLOCKF = [0x%02X]"),VsRegRead(VS_CLOCKF_REG));
+//        LogMsg_P(LOG_DEBUG,PSTR("Kick: CLOCKF = [0x%02X]"),VsRegRead(VS_CLOCKF_REG));
+//        LogMsg_P(LOG_DEBUG,PSTR("Kick: CLOCKF = [0x%02X]"),VsRegRead(VS_CLOCKF_REG));
+//        LogMsg_P(LOG_DEBUG,PSTR("Kick: CLOCKF = [0x%02X]"),VsRegRead(VS_CLOCKF_REG));
+
+        VsLoadProgramCode();
+        vs_status = VS_STATUS_RUNNING;
+        VsPlayerFeed(NULL);
+        VsPlayerInterrupts(1);
+    }
+    return(0);
+}
+
+/*!
+ * \brief Stops the playback.
+ *
+ * This routine will stops the MP3 playback, VsPlayerKick() may be used
+ * to resume the playback.
+ *
+ * \return 0 on success, -1 otherwise.
+ */
+int VsPlayerStop(void)
+{
+    u_char ief;
+
+    ief = VsPlayerInterrupts(0);
+    /* Check whether we need to stop at all to not overwrite other than running status */
+    if (vs_status == VS_STATUS_RUNNING)
+        vs_status = VS_STATUS_STOPPED;
+    VsPlayerInterrupts(ief);
+
+    return(0);
+}
+
+
+/*!
+ * \brief Initialize the VS10xx hardware interface.
+ *
+ * \return 0 on success, -1 otherwise.
+ */
+int VsPlayerInit(void)
+{
+
+    /* Disable decoder interrupts. */
+
+    VsPlayerInterrupts(0);
+
+    /* Keep decoder in reset state. */
+    cbi(VS_RESET_PORT, VS_RESET_BIT);
+
+    /* Set VS10XX chip select output inactive for SCI bus (high) */
+    sbi(VS_XCS_PORT, VS_XCS_BIT);
+
+    /* Set SCK output low. */
+    cbi(VS_SCK_PORT, VS_SCK_BIT);
+    sbi(VS_SCK_DDR, VS_SCK_BIT);
+
+
+    /*
+     * Init SPI mode to no interrupts, enabled, MSB first, master mode,
+     * rising clock and fosc/8 clock speed. Send an initial byte to
+     * make sure SPIF is set. Note, that the decoder reset line is still
+     * active.
+     */
+    NutDelay(4);
+
+    SPImode(SPEED_SLOW);
+
+    vs_status = VS_STATUS_STOPPED;
+
+    /* Release decoder reset line. */
+    sbi(VS_RESET_PORT, VS_RESET_BIT);
+
+    /* Wait until DREQ is active
+     * Write 0x9800 to SCI_CLOCKF
+     * do another register write
+     * wait at least 11000 clockcycles
+     */
+    NutDelay(4);
+
+    /* Read the status register to determine the VS type. */
+    g_vs_type = (VsRegRead(VS_STATUS_REG) >> 4) & 7;
+
+    /* Force frequency change (see datasheet). */
+    switch (g_vs_type)
+    {
+        case VS_VS1003:
+            {
+                VsRegWrite(VS_CLOCKF_REG, 0xE000); // 4.5x
+                break;
+            }
+        default:
+            {
+                VsRegWrite(VS_CLOCKF_REG, 0x9800); // 2x
+                break;
+            }
+    }
+
+    NutDelay(50);
+
+    // Datasheet requires 2 write instructions before speeding up SPI interface
+    VsPlayerSetMode(0);
+    VsSetVolume(0,0);
+
+    NutDelay(50);
+
+    // now switch to new speed...
+    switch (g_vs_type)
+    {
+        case VS_VS1003:
+        case VS_VS1053:
+            {
+                SPImode(SPEED_ULTRA_FAST);
+                break;
+            }
+        case VS_VS1011e:
+        default:
+            {
+                SPImode(SPEED_FAST);
+                break;
+            }
+    }
+
+    /* Register the interrupt routine */
+    NutRegisterIrqHandler(&sig_INTERRUPT6, VsPlayerFeed, NULL);
+
+    /* Rising edge will generate interrupts. */
+    NutIrqSetMode(&sig_INTERRUPT6, NUT_IRQMODE_RISINGEDGE);
+
+    /* Clear any spurious interrupt. */
+    outp(BV(VS_DREQ_BIT), EIFR);
+
+    return(0);
+}
+
+/*!
+ * \brief Software reset the decoder.
+ *
+ * This function is typically called after VsPlayerInit() and at the end
+ * of each track.
+ *
+ * \param mode Any of the following flags may be or'ed
+ * - VS_SM_DIFF Left channel inverted.
+ * - VS_SM_FFWD Fast forward.
+ * - VS_SM_RESET Force hardware reset.
+ * - VS_SM_PDOWN Switch to power down mode.
+ * - VS_SM_BASS Bass/treble enhancer.
+ *
+ * \return 0 on success, -1 otherwise.
+ */
+int VsPlayerReset(u_short mode)
+{
+    /* Disable decoder interrupts and feeding. */
+    VsPlayerInterrupts(0);
+    vs_status = VS_STATUS_STOPPED;
+
+    /* Software reset, set modes of decoder. */
+    VsPlayerSetMode(VS_SM_RESET | mode);
+    NutDelay(10);
+
+    /* Clear any spurious interrupts. */
+    outp(BV(VS_DREQ_BIT), EIFR);
+
+    return(0);
+}
+
+/*!
+ * \brief Set mode register of the decoder.
+ *
+ * \param mode Any of the following flags may be or'ed
+ * - VS_SM_DIFF Left channel inverted.
+ * - VS_SM_FFWD Fast forward.
+ * - VS_SM_RESET Software reset.
+ * - VS_SM_PDOWN Switch to power down mode.
+ * - VS_SM_BASS Bass/treble enhancer.
+ *
+ * \return 0 on success, -1 otherwise.
+ */
+int VsPlayerSetMode(u_short mode)
+{
+    u_char ief;
+
+    ief = VsPlayerInterrupts(0);
+    /*
+     *  We need to be sure that the way of interfacing
+     *  is not corrupted by setting some new mode
+     *  We need to have SM_SDINEW & SM_SDISHARE set to '1'
+     *  at all times
+     */
+    mode |= (VS_SM_SDISHARE | VS_SM_SDINEW);
+    VsRegWrite(VS_MODE_REG, mode);
+    VsPlayerInterrupts(ief);
+
+    return(0);
+}
+
+/*!
+ * \brief Returns status of the player.
+ *
+ * \return Any of the following value:
+ * - VS_STATUS_STOPPED Player is ready to be started by VsPlayerKick().
+ * - VS_STATUS_RUNNING Player is running.
+ * - VS_STATUS_EOF Player has reached the end of a stream after VsPlayerFlush() has been called.
+ * - VS_STATUS_EMPTY Player runs out of data. VsPlayerKick() will restart it.
+ */
+u_char VsGetStatus(void)
+{
+    return(vs_status);
+}
+
+
+/*!
+ * \brief Initialize decoder memory test and return result.
+ *
+ * \return Memory test result.
+ * - Bit(s) Mask            Meaning
+            VS1011e/ VS1053
+            VS1003
+ * - 15     0x8000  0x8000  Test finished
+ * - 14:7   Unused
+ * - 14:10          Unused
+ * - 9:             0x0200  Mux test succeeded
+ * - 8:             0x0100  Good MAC RAM
+ * - 7:             0x0080  Good I RAM
+ * - 6:     0x0040          Mux test succeeded
+ * - 6:             0x0040  Good Y RAM
+ * - 5:     0x0020          Good I RAM
+ * - 5:             0x0020  Good X RAM
+ * - 4:     0x0010          Good Y RAM
+ * - 4:             0x0010  Good I ROM 1
+ * - 3:     0x0008          Good X RAM
+ * - 3:             0x0008  Good I ROM 2
+ * - 2:     0x0004          Good I ROM
+ * - 2:             0x0004  Good Y ROM
+ * - 1:     0x0002          Good Y ROM
+ * - 1:             0x0002  Good X ROM 1
+ * - 0:     0x0001          Good X ROM
+ * - 0:             0x0001  Good X ROM 2
+ *          0x807F  0x83FF  All ok
+ *
+ * - return 0 on succes; rc otherwise
+ */
+u_short VsMemoryTest(void)
+{
+    u_short rc = -1;
+    u_char ief;
+    static prog_char mtcmd[] = { 0x4D, 0xEA, 0x6D, 0x54, 0x00, 0x00, 0x00, 0x00};
+
+    VsPlayerReset(0);
+    VsPlayerSetMode(VS_SM_TESTS);
+
+    ief = VsPlayerInterrupts(0);
+
+    VsSdiWrite_P(mtcmd, sizeof(mtcmd));
+    NutDelay(40);
+
+    rc = VsRegRead(VS_HDAT0_REG);
+
+    VsPlayerInterrupts(ief);
+
+    if ((VsGetType()==VS_VS1003) && (rc == 0x807F))
+    {
+        rc=0;
+    }
+
+    return(rc);
+}
+
+/*!
+ * \brief Set volume.
+ *
+ * \param left  Left channel volume.
+ * \param right Right channel volume.
+ *
+ * \return 0 on success, -1 otherwise.
+ */
+int VsSetVolume(u_char left, u_char right)
+{
+    u_char ief;
+
+    ief = VsPlayerInterrupts(0);
+
+    VsRegWrite(VS_VOL_REG, (((u_short) left) << 8) | (u_short) right);
+
+    VsPlayerInterrupts(ief);
+
+    return(0);
+}
+
+
+/*!
+ * \brief Get volume.
+ *
+ * \return u_short Volume.
+ */
+u_short VsGetVolume()
+{
+    u_char ief;
+    u_short vol;
+    ief = VsPlayerInterrupts(0);
+    vol=VsRegRead(VS_VOL_REG);
+    VsPlayerInterrupts(ief);
+    return(vol);
+}
+
+/*!
+ * \brief Return the number of the VS10xx chip.
+ *
+ * \return  The actual type number as defined
+ */
+u_short VsGetType(void)
+{
+    return(g_vs_type);
+}
+
+
+/*!
+ * \brief Return the number of the VS10xx chip.
+ *
+ * \return  The actual type number coded in HEX!
+ */
+u_short VsGetTypeHex(void)
+{
+    switch (g_vs_type)
+    {
+        case VS_VS1003:  return(0x03);
+        default:         break;         /* Other codes use the actual number. */
+    }
+
+    return(g_vs_type);
+}
+
+/*!
+ * \brief Sine wave beep.
+ *
+ * \param fsin Frequency.
+ * \param ms   Duration.
+ *
+ * \return 0 on success, -1 otherwise.
+ */
+int VsBeep(u_char fsin, u_short ms)
+{
+    u_char ief;
+    static prog_char on[] = { 0x53, 0xEF, 0x6E};
+    static prog_char off[] = { 0x45, 0x78, 0x69, 0x74};
+    static prog_char end[] = { 0x00, 0x00, 0x00, 0x00};
+
+    /* Disable decoder interrupts. */
+    ief = VsPlayerInterrupts(0);
+
+    VsPlayerSetMode(VS_SM_TESTS);
+
+    fsin = (fsin* 16) + 56;
+    VsSdiWrite_P(on, sizeof(on));
+    VsSdiWrite(&fsin, 1);
+    VsSdiWrite_P(end, sizeof(end));
+    NutDelay(ms);
+    VsSdiWrite_P(off, sizeof(off));
+    VsSdiWrite_P(end, sizeof(end));
+
+    /* Enable decoder interrupts. */
+    VsPlayerInterrupts(ief);
+
+    return(0);
+}
+
+/*!
+ * \brief Sine wave beep start.
+ *
+ * \param fsin Frequency.
+ *
+ * \note this routine uses a fixed sample rate (12.000Hz) and
+ *       therefor the possible frequencies are limited.
+ *       Use 'VsBeepStartRaw' to generate a sinus with
+ *       an arbitrary freqeuncy
+ *
+ * \return 0 on success, -1 otherwise.
+ */
+int VsBeepStart(u_char fsin)
+{
+    return(VsBeepStartRaw(56 + (fsin & 7) * 9));
+}
+
+/*!
+ * \brief Sine wave beep start in Raw mode.
+ *
+ * \param Raw: b7..b5 determines samplerate
+ *        while b4..b0 determines finepitch
+ *
+ * \note use the VS1003/VS1011 datasheet to figure out
+ *       which value to use for Fs (b7..b5) and
+ *       which value to use for S (b4..b0)
+ *
+ *
+ * \return 0 on success, -1 otherwise.
+ */
+int VsBeepStartRaw(u_char Raw)
+{
+    u_char ief;
+    static prog_char on[] = { 0x53, 0xEF, 0x6E};
+    static prog_char end[] = { 0x00, 0x00, 0x00, 0x00};
+
+    /* Disable decoder interrupts. */
+    ief = VsPlayerInterrupts(0);
+
+    VsPlayerSetMode(VS_SM_TESTS);
+
+    VsSdiWrite_P(on, sizeof(on));
+    VsSdiWrite(&Raw, 1);
+    VsSdiWrite_P(end, sizeof(end));
+
+    /* Enable decoder interrupts. */
+    VsPlayerInterrupts(ief);
+
+    return(0);
+}
+
+/*!
+ * \brief Sine wave beep stop.
+ *
+ * \return 0 on success, -1 otherwise.
+ */
+int VsBeepStop()
+{
+    u_char ief;
+    static prog_char off[] = { 0x45, 0x78, 0x69, 0x74};
+    static prog_char end[] = { 0x00, 0x00, 0x00, 0x00};
+
+    /* Disable decoder interrupts. */
+    ief = VsPlayerInterrupts(0);
+
+    VsSdiWrite_P(off, sizeof(off));
+    VsSdiWrite_P(end, sizeof(end));
+
+    /* Enable decoder interrupts. */
+    VsPlayerInterrupts(ief);
+
+    return(0);
+}
+
+static void VsLoadProgramCode(void)
+{
+    int i;
+
+    for (i=0;i<CODE_SIZE;i++)
+    {
+        VsRegWrite(PRG_RDB(&atab[i]), PRG_RDW(&dtab[i]));
+        // kick watchdog on a regular base
+        if(i%500==0)
+        {
+            WatchDogRestart();
+        }
+    }
+}
+/*@}*/

+ 169 - 0
watchdog.c

@@ -0,0 +1,169 @@
+/*
+ *  Copyright STREAMIT BV, 2010.
+ *
+ *  Project             : SIR
+ *  Module              : watchdog
+ *  File name  $Workfile: watchdog.c  $
+ *       Last Save $Date: 2007/01/16 11:17:24  $
+ *             $Revision: 0.1  $
+ *  Creation Date       : 2007/01/16 11:17:25
+ *
+ *  Description         : Watchdog routines for the ATMega128/256
+ *                        Inspired by the arch/xxx/dev/watchdog routines in
+ *                        NutOS 4.2.1 and higher.
+ *
+ */
+
+#define LOG_MODULE  LOG_WATCHDOG_MODULE
+/*--------------------------------------------------------------------------*/
+/*  Include files                                                           */
+/*--------------------------------------------------------------------------*/
+#include <stdio.h>
+
+#ifdef __AVR__
+#ifdef __GNUC__
+    #include <avr/wdt.h>
+#endif /* #ifdef __GNUC__ */
+#endif /* #ifdef __AVR__ */
+
+#include "watchdog.h"
+
+/*--------------------------------------------------------------------------*/
+/*  Constant definitions                                                    */
+/*--------------------------------------------------------------------------*/
+/*!
+ * \brief Watchdog oscillator frequency.
+ */
+#ifndef NUT_WDT_FREQ
+#define NUT_WDT_FREQ    128000UL
+#endif
+
+#ifndef WDTO_15MS
+#define WDTO_15MS   0x00
+#define WDTO_30MS   0x01
+#define WDTO_60MS   0x02
+#define WDTO_120MS  0x03
+#define WDTO_250MS  0x04
+#define WDTO_500MS  0x05
+#define WDTO_1S     0x06
+#define WDTO_2S     0x07
+#define WDTO_4S     0x20
+#define WDTO_8S     0x21
+#endif /* #ifndef WDTO_15MS */
+
+#define WD_CONTROL      WDTCSR
+
+/*--------------------------------------------------------------------------*/
+/*  Type declarations                                                       */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Local variables                                                         */
+/*--------------------------------------------------------------------------*/
+/*!\brief Counts nested calls */
+static unsigned char g_byNested;
+
+/*!\brief Watchdog Timeout constant */
+static unsigned char g_byWdtDivider;
+
+/*--------------------------------------------------------------------------*/
+/*  Global variables                                                        */
+/*--------------------------------------------------------------------------*/
+
+/*--------------------------------------------------------------------------*/
+/*  Local functions                                                         */
+/*--------------------------------------------------------------------------*/
+
+#ifndef wdt_enable
+#define wdt_enable(tmo) \
+{ \
+    register u_char s = _BV(WDCE) | _BV(WDE); \
+    register u_char r = tmo | _BV(WDE); \
+    asm("in R0, 0x3F\n"     \
+        "cli\n"             \
+        "wdr\n"             \
+        "sts 0x60, %s\n"    \
+        "sts 0x60, %r\n"    \
+        "out 0x3F, R0\n");  \
+}
+#endif /* #ifndef wdt_enable */
+
+#ifndef wdt_disable
+#define wdt_disable() \
+{ \
+    asm("in R0, $3F\n"      \
+        "cli\n"                 \
+        "wdr\n"                 \
+        "in      r24,0x34\n"    \
+        "andi    r24,0xF7\n"    \
+        "out     0x34,r24\n"    \
+        "lds     r24,0x0060\n"  \
+        "ori     r24,0x18\n"    \
+        "sts     0x0060,r24\n"  \
+        "clr     r2\n"          \
+        "sts     0x0060,r2\n"   \
+        "out 0x3F, R0\n");      \
+}
+#endif /* #ifndef wdt_disable */
+
+#ifndef wdt_reset
+#define wdt_reset() \
+{ \
+    _WDR(); \
+}
+#endif /* #ifndef wdt_reset */
+
+/*--------------------------------------------------------------------------*/
+/*  Global functions                                                        */
+/*--------------------------------------------------------------------------*/
+
+void WatchDogStart(unsigned long ms)
+{
+    const unsigned long unPrescaleFactor = 2048UL;       // PragmaLab: check datasheet Mega2561
+
+    unsigned char byPrescale = 1;
+
+    wdt_reset();
+
+    /*
+     * Calculate the needed prescaling for the timer ticks
+     */
+    while (((unPrescaleFactor << byPrescale) / (NUT_WDT_FREQ / 1000UL)) < ms)
+    {
+        byPrescale++;
+    }
+
+    g_byWdtDivider = byPrescale;
+
+    if (g_byWdtDivider > WDTO_8S)
+    {
+        // restrict timeout since HW only can handle 8 seconds for Mega256
+        g_byWdtDivider &= WDTO_8S;
+    }
+
+    wdt_enable(g_byWdtDivider);
+
+    g_byNested = 1;
+}
+
+void WatchDogRestart(void)
+{
+    wdt_reset();
+}
+
+void WatchDogDisable(void)
+{
+    if (g_byNested != 0)
+    {
+        g_byNested++;
+    }
+    wdt_disable();
+}
+
+void WatchDogEnable(void)
+{
+    if (g_byNested > 1 && --g_byNested == 1)
+    {
+        wdt_enable(g_byWdtDivider);
+    }
+}