うおーるぼっとをiPhoneでコントロールするプログラムです。 iPhoneとはBTLEで接続しています。

Dependencies:   FatFileSystem HighSpeedAnalogIn TB6612FNG2 mbed

Files at this revision

API Documentation at this revision

Comitter:
jksoft
Date:
Fri May 10 11:48:07 2013 +0000
Commit message:
?????????

Changed in this revision

FatFileSystem.lib Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/FATDirHandle.cpp Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/FATDirHandle.h Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/FATFileHandle.cpp Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/FATFileHandle.h Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/FATFileSystem.cpp Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/FATFileSystem.h Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/MemFileSystem.h Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/diskio.c Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/diskio.h Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/ff.c Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/ff.h Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/ffconf.h Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/integer.h Show annotated file Show diff for this revision Revisions of this file
FatFileSystem/option/ccsbcs.c Show annotated file Show diff for this revision Revisions of this file
HighSpeedAnalogIn.lib Show annotated file Show diff for this revision Revisions of this file
HighSpeedAnalogIn/HighSpeedAnalogIn.cpp Show annotated file Show diff for this revision Revisions of this file
HighSpeedAnalogIn/HighSpeedAnalogIn.h Show annotated file Show diff for this revision Revisions of this file
TB6612FNG2.lib Show annotated file Show diff for this revision Revisions of this file
TB6612FNG2/TB6612.cpp Show annotated file Show diff for this revision Revisions of this file
TB6612FNG2/TB6612.h Show annotated file Show diff for this revision Revisions of this file
btstack/att.c Show annotated file Show diff for this revision Revisions of this file
btstack/att.h Show annotated file Show diff for this revision Revisions of this file
btstack/bt_control.h Show annotated file Show diff for this revision Revisions of this file
btstack/btstack.h Show annotated file Show diff for this revision Revisions of this file
btstack/btstack_memory.c Show annotated file Show diff for this revision Revisions of this file
btstack/btstack_memory.h Show annotated file Show diff for this revision Revisions of this file
btstack/config.h Show annotated file Show diff for this revision Revisions of this file
btstack/debug.h Show annotated file Show diff for this revision Revisions of this file
btstack/hal_cpu.c Show annotated file Show diff for this revision Revisions of this file
btstack/hal_cpu.h Show annotated file Show diff for this revision Revisions of this file
btstack/hal_tick.c Show annotated file Show diff for this revision Revisions of this file
btstack/hal_tick.h Show annotated file Show diff for this revision Revisions of this file
btstack/hci.c Show annotated file Show diff for this revision Revisions of this file
btstack/hci.h Show annotated file Show diff for this revision Revisions of this file
btstack/hci_cmds.c Show annotated file Show diff for this revision Revisions of this file
btstack/hci_cmds.h Show annotated file Show diff for this revision Revisions of this file
btstack/hci_dump.c Show annotated file Show diff for this revision Revisions of this file
btstack/hci_dump.h Show annotated file Show diff for this revision Revisions of this file
btstack/hci_transport.h Show annotated file Show diff for this revision Revisions of this file
btstack/hci_transport_usb.c Show annotated file Show diff for this revision Revisions of this file
btstack/l2cap.c Show annotated file Show diff for this revision Revisions of this file
btstack/l2cap.h Show annotated file Show diff for this revision Revisions of this file
btstack/l2cap_signaling.h Show annotated file Show diff for this revision Revisions of this file
btstack/linked_list.c Show annotated file Show diff for this revision Revisions of this file
btstack/linked_list.h Show annotated file Show diff for this revision Revisions of this file
btstack/memory_pool.c Show annotated file Show diff for this revision Revisions of this file
btstack/memory_pool.h Show annotated file Show diff for this revision Revisions of this file
btstack/remote_device_db.h Show annotated file Show diff for this revision Revisions of this file
btstack/remote_device_db_memory.c Show annotated file Show diff for this revision Revisions of this file
btstack/rfcomm.h Show annotated file Show diff for this revision Revisions of this file
btstack/run_loop.c Show annotated file Show diff for this revision Revisions of this file
btstack/run_loop.h Show annotated file Show diff for this revision Revisions of this file
btstack/run_loop_embedded.c Show annotated file Show diff for this revision Revisions of this file
btstack/run_loop_private.h Show annotated file Show diff for this revision Revisions of this file
btstack/sdp.h Show annotated file Show diff for this revision Revisions of this file
btstack/sdp_util.c Show annotated file Show diff for this revision Revisions of this file
btstack/sdp_util.h Show annotated file Show diff for this revision Revisions of this file
btstack/utils.c Show annotated file Show diff for this revision Revisions of this file
btstack/utils.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
profile.h Show annotated file Show diff for this revision Revisions of this file
usb/UsbBaseClass.cpp Show annotated file Show diff for this revision Revisions of this file
usb/UsbBaseClass.h Show annotated file Show diff for this revision Revisions of this file
usb/UsbDevice.cpp Show annotated file Show diff for this revision Revisions of this file
usb/UsbDevice.h Show annotated file Show diff for this revision Revisions of this file
usb/UsbDevice2.cpp Show annotated file Show diff for this revision Revisions of this file
usb/UsbEndpoint.cpp Show annotated file Show diff for this revision Revisions of this file
usb/UsbEndpoint.h Show annotated file Show diff for this revision Revisions of this file
usb/UsbEndpoint2.cpp Show annotated file Show diff for this revision Revisions of this file
usb/UsbHostMgr.cpp Show annotated file Show diff for this revision Revisions of this file
usb/UsbHostMgr.h Show annotated file Show diff for this revision Revisions of this file
usb/UsbHostMgr2.cpp Show annotated file Show diff for this revision Revisions of this file
usb/UsbInc.h Show annotated file Show diff for this revision Revisions of this file
usb/Usb_td.cpp Show annotated file Show diff for this revision Revisions of this file
usb/Usb_td.h Show annotated file Show diff for this revision Revisions of this file
usb/Utils.cpp Show annotated file Show diff for this revision Revisions of this file
usb/Utils.h Show annotated file Show diff for this revision Revisions of this file
usb/mydbg.h Show annotated file Show diff for this revision Revisions of this file
usb/netCfg.h Show annotated file Show diff for this revision Revisions of this file
usb/usb_mem.c Show annotated file Show diff for this revision Revisions of this file
usb/usb_mem.h Show annotated file Show diff for this revision Revisions of this file
usbbt/bd_addr.cpp Show annotated file Show diff for this revision Revisions of this file
usbbt/bd_addr.h Show annotated file Show diff for this revision Revisions of this file
usbbt/usbbt.cpp Show annotated file Show diff for this revision Revisions of this file
usbbt/usbbt.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem.lib	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/SomeRandomBloke/code/FatFileSystem/#5baba5d5b728
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/FATDirHandle.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,66 @@
+/* mbed Microcontroller Library - FATDirHandle
+ * Copyright (c) 2008, sford
+ */
+ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ff.h"
+#include "FATDirHandle.h"
+#include "FATFileSystem.h"
+
+namespace mbed {
+
+FATDirHandle::FATDirHandle(const FATFS_DIR &the_dir) {
+    dir = the_dir;
+}
+
+int FATDirHandle::closedir() {
+    delete this;
+    return 0;
+}
+
+struct dirent *FATDirHandle::readdir() {
+    FILINFO finfo;
+
+#if _USE_LFN
+    finfo.lfname = cur_entry.d_name;
+    finfo.lfsize = sizeof(cur_entry.d_name);
+#endif // _USE_LFN
+
+    FRESULT res = f_readdir(&dir, &finfo);
+
+#if _USE_LFN
+    if(res != 0 || finfo.fname[0]==0) {
+        return NULL;
+    } else {
+        if(cur_entry.d_name[0]==0) {
+            // No long filename so use short filename.
+            memcpy(cur_entry.d_name, finfo.fname, sizeof(finfo.fname));
+        }
+        return &cur_entry;
+    }
+#else
+    if(res != 0 || finfo.fname[0]==0) {
+        return NULL;
+    } else {
+        memcpy(cur_entry.d_name, finfo.fname, sizeof(finfo.fname));
+        return &cur_entry;
+    }
+#endif /* _USE_LFN */
+}
+
+void FATDirHandle::rewinddir() {
+    dir.index = 0;
+}
+
+off_t FATDirHandle::telldir() {
+    return dir.index;
+}
+
+void FATDirHandle::seekdir(off_t location) {
+    dir.index = location;
+}
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/FATDirHandle.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,31 @@
+/* mbed Microcontroller Library - FATDirHandle
+ * Copyright (c) 2008, sford
+ */
+
+#ifndef MBED_FATDIRHANDLE_H
+#define MBED_FATDIRHANDLE_H
+
+#include "DirHandle.h"
+#include "ff.h"
+
+namespace mbed {
+
+class FATDirHandle : public DirHandle {
+
+ public:
+    FATDirHandle(const FATFS_DIR &the_dir);
+    virtual int closedir();
+    virtual struct dirent *readdir();
+    virtual void rewinddir();
+    virtual off_t telldir();
+    virtual void seekdir(off_t location);
+
+ private:
+    FATFS_DIR dir;
+    struct dirent cur_entry;
+
+};
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/FATFileHandle.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,102 @@
+/* mbed Microcontroller Library - FATFileHandle
+ * Copyright (c) 2008, sford
+ */
+
+#include "FATFileHandle.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "ff.h"
+#include "FATFileSystem.h"
+
+namespace mbed {
+
+#if FFSDEBUG_ENABLED
+static const char *FR_ERRORS[] = {
+    "FR_OK = 0", 
+    "FR_NOT_READY",          
+    "FR_NO_FILE",              
+    "FR_NO_PATH",             
+    "FR_INVALID_NAME",     
+    "FR_INVALID_DRIVE",      
+    "FR_DENIED",              
+    "FR_EXIST",              
+    "FR_RW_ERROR",          
+    "FR_WRITE_PROTECTED", 
+    "FR_NOT_ENABLED",    
+    "FR_NO_FILESYSTEM",    
+    "FR_INVALID_OBJECT",    
+    "FR_MKFS_ABORTED"    
+};
+#endif
+
+FATFileHandle::FATFileHandle(FIL fh) { 
+    _fh = fh;
+}
+    
+int FATFileHandle::close() {
+    FFSDEBUG("close\n");
+    int retval = f_close(&_fh);
+    delete this;
+    return retval;
+}
+
+ssize_t FATFileHandle::write(const void* buffer, size_t length) {
+    FFSDEBUG("write(%d)\n", length);
+    UINT n;
+    FRESULT res = f_write(&_fh, buffer, length, &n);
+    if(res) { 
+        FFSDEBUG("f_write() failed (%d, %s)", res, FR_ERRORS[res]);
+        return -1;
+    }
+    return n;
+}
+        
+ssize_t FATFileHandle::read(void* buffer, size_t length) {
+    FFSDEBUG("read(%d)\n", length);
+    UINT n;
+    FRESULT res = f_read(&_fh, buffer, length, &n);
+    if(res) {
+        FFSDEBUG("f_read() failed (%d, %s)\n", res, FR_ERRORS[res]);
+        return -1;
+    }
+    return n;
+}
+        
+int FATFileHandle::isatty() {
+    return 0;
+}
+        
+off_t FATFileHandle::lseek(off_t position, int whence) {
+    FFSDEBUG("lseek(%i,%i)\n",position,whence);
+    if(whence == SEEK_END) {
+        position += _fh.fsize;
+    } else if(whence==SEEK_CUR) {
+        position += _fh.fptr;
+    }
+    FRESULT res = f_lseek(&_fh, position);
+    if(res) {
+        FFSDEBUG("lseek failed (%d, %s)\n", res, FR_ERRORS[res]);
+        return -1;
+    } else {
+        FFSDEBUG("lseek OK, returning %i\n", _fh.fptr);
+        return _fh.fptr;
+    }
+}
+        
+int FATFileHandle::fsync() {
+    FFSDEBUG("fsync()\n");
+    FRESULT res = f_sync(&_fh);
+    if (res) {
+        FFSDEBUG("f_sync() failed (%d, %s)\n", res, FR_ERRORS[res]);
+        return -1;
+    }
+    return 0;
+}
+
+off_t FATFileHandle::flen() {
+    FFSDEBUG("flen\n");
+    return _fh.fsize;
+}
+
+} // namespace mbed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/FATFileHandle.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,33 @@
+/* mbed Microcontroller Library - FATFileHandle
+ * Copyright (c) 2008, sford
+ */
+
+#ifndef MBED_FATFILEHANDLE_H
+#define MBED_FATFILEHANDLE_H
+
+#include "FileHandle.h"
+#include "ff.h"
+
+namespace mbed {
+
+class FATFileHandle : public FileHandle {
+public:
+
+    FATFileHandle(FIL fh);
+    virtual int close();
+    virtual ssize_t write(const void* buffer, size_t length);
+    virtual ssize_t read(void* buffer, size_t length);
+    virtual int isatty();
+    virtual off_t lseek(off_t position, int whence);
+    virtual int fsync();
+    virtual off_t flen();
+
+protected:
+
+    FIL _fh;
+
+};
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/FATFileSystem.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,176 @@
+/* mbed Microcontroller Library - FATFileSystem
+ * Copyright (c) 2008, sford
+ */
+
+#include "FATFileSystem.h"
+
+#include "mbed.h"
+
+#include "FileSystemLike.h"
+#include "FATFileHandle.h"
+#include "FATDirHandle.h"
+#include "ff.h"
+//#include "Debug.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+/*
+Currnet time is returned with packed into a DWORD value. The bit field is as follows:
+bit31:25
+Year from 1980 (0..127)
+bit24:21
+Month (1..12)
+bit20:16
+Day in month(1..31)
+bit15:11
+Hour (0..23)
+bit10:5
+Minute (0..59)
+bit4:0
+Second / 2 (0..29)
+
+
+int tm_sec;
+int tm_min;
+int tm_hour;
+int tm_mday;
+int tm_mon;
+int tm_year;
+int tm_wday;
+int tm_yday;
+int tm_isdst;
+
+*/
+
+DWORD get_fattime (void) {
+  time_t rawtime;
+  struct tm *ptm;
+  time ( &rawtime );
+  ptm = localtime ( &rawtime );
+  FFSDEBUG("DTM: %d/%d/%d %d:%d:%d\n",ptm->tm_year,ptm->tm_mon,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec);
+  DWORD fattime = (DWORD)(ptm->tm_year - 80) << 25
+                | (DWORD)(ptm->tm_mon + 1) << 21
+                | (DWORD)(ptm->tm_mday) << 16
+                | (DWORD)(ptm->tm_hour) << 11
+                | (DWORD)(ptm->tm_min) << 5
+                | (DWORD)(ptm->tm_sec/2);
+ 
+  FFSDEBUG("Converted: %x\n",fattime);
+  return fattime;
+}
+
+namespace mbed {
+
+#if FFSDEBUG_ENABLED
+static const char *FR_ERRORS[] = {
+    "FR_OK = 0", 
+    "FR_NOT_READY",          
+    "FR_NO_FILE",              
+    "FR_NO_PATH",             
+    "FR_INVALID_NAME",     
+    "FR_INVALID_DRIVE",      
+    "FR_DENIED",              
+    "FR_EXIST",              
+    "FR_RW_ERROR",          
+    "FR_WRITE_PROTECTED", 
+    "FR_NOT_ENABLED",    
+    "FR_NO_FILESYSTEM",    
+    "FR_INVALID_OBJECT",    
+    "FR_MKFS_ABORTED"    
+};
+#endif
+
+FATFileSystem *FATFileSystem::_ffs[_VOLUMES] = {0};
+
+FATFileSystem::FATFileSystem(const char* n) : FileSystemLike(n) {
+    FFSDEBUG("FATFileSystem(%s)\n", n);
+    for(int i=0; i<_VOLUMES; i++) {
+        if(_ffs[i] == 0) {
+            _ffs[i] = this;
+            _fsid = i;
+            FFSDEBUG("Mounting [%s] on ffs drive [%d]\n", _name, _fsid);
+            f_mount(i, &_fs);
+            return;
+        }
+    }
+    error("Couldn't create %s in FATFileSystem::FATFileSystem\n",n);
+}
+    
+FATFileSystem::~FATFileSystem() {
+    for(int i=0; i<_VOLUMES; i++) {
+        if(_ffs[i] == this) {
+            _ffs[i] = 0;
+            f_mount(i, NULL);
+        }
+    }
+}
+    
+FileHandle *FATFileSystem::open(const char* name, int flags) {
+    FFSDEBUG("open(%s) on filesystem [%s], drv [%d]\n", name, _name, _fsid);
+    char n[64];
+    sprintf(n, "%d:/%s", _fsid, name);
+
+    /* POSIX flags -> FatFS open mode */
+    BYTE openmode;
+    if(flags & O_RDWR) {
+        openmode = FA_READ|FA_WRITE;
+    } else if(flags & O_WRONLY) {
+        openmode = FA_WRITE;
+    } else {
+        openmode = FA_READ;
+    }
+    if(flags & O_CREAT) {
+        if(flags & O_TRUNC) {
+            openmode |= FA_CREATE_ALWAYS;
+        } else {
+            openmode |= FA_OPEN_ALWAYS;
+        }
+    }
+
+    FIL fh;
+    FRESULT res = f_open(&fh, n, openmode);
+    if(res) { 
+        FFSDEBUG("f_open('w') failed (%d, %s)\n", res, FR_ERRORS[res]);
+        return NULL;
+    }
+    if(flags & O_APPEND) {
+        f_lseek(&fh, fh.fsize);
+    }
+    return new FATFileHandle(fh);
+}
+    
+int FATFileSystem::remove(const char *filename) {
+    FRESULT res = f_unlink(filename);
+    if(res) { 
+        FFSDEBUG("f_unlink() failed (%d, %s)\n", res, FR_ERRORS[res]);
+        return -1;
+    }
+    return 0;
+}
+
+int FATFileSystem::format() {
+    FFSDEBUG("format()\n");
+    FRESULT res = f_mkfs(_fsid, 0, 512); // Logical drive number, Partitioning rule, Allocation unit size (bytes per cluster)
+    if(res) {
+        FFSDEBUG("f_mkfs() failed (%d, %s)\n", res, FR_ERRORS[res]);
+        return -1;
+    }
+    return 0;
+}
+
+DirHandle *FATFileSystem::opendir(const char *name) {
+    FATFS_DIR dir;
+    FRESULT res = f_opendir(&dir, name);
+    if(res != 0) {
+        return NULL;
+    }
+    return new FATDirHandle(dir);
+}
+
+int FATFileSystem::mkdir(const char *name, mode_t mode) {
+    FRESULT res = f_mkdir(name);
+    return res == 0 ? 0 : -1;
+}
+
+} // namespace mbed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/FATFileSystem.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,61 @@
+/* mbed Microcontroller Library - FATFileSystem
+ * Copyright (c) 2008, sford
+ */
+
+/* Library: FATFileSystem.h
+ * A library of stuff to make a fat filesystem on top of a block device
+ */
+
+#ifndef MBED_FATFILESYSTEM_H
+#define MBED_FATFILESYSTEM_H
+
+#ifndef FFSDEBUG_ENABLED
+#define FFSDEBUG_ENABLED 0
+#endif
+
+#if FFSDEBUG_ENABLED
+#define FFSDEBUG(FMT, ...) printf(FMT, ##__VA_ARGS__)
+#else
+#define FFSDEBUG(FMT, ...)
+#endif
+
+#include "FileSystemLike.h"
+#include "FileHandle.h"
+#include "ff.h"
+#include "diskio.h"
+
+namespace mbed {
+/* Class: FATFileSystem
+ * The class itself
+ */
+class FATFileSystem : public FileSystemLike {
+public:
+
+    FATFileSystem(const char* n);
+    virtual ~FATFileSystem();
+    
+    /* Function: open
+       * open a file on the filesystem. never called directly
+       */
+    virtual FileHandle *open(const char* name, int flags);
+    virtual int remove(const char *filename);
+    virtual int format();
+        virtual DirHandle *opendir(const char *name);
+        virtual int mkdir(const char *name, mode_t mode);
+    
+    FATFS _fs;                                // Work area (file system object) for logical drive    
+    static FATFileSystem *_ffs[_VOLUMES];    // FATFileSystem objects, as parallel to FatFs drives array
+    int _fsid;
+    
+    virtual int disk_initialize() { return 0; }
+    virtual int disk_status() { return 0; }
+    virtual int disk_read(char *buffer, int sector) = 0;
+    virtual int disk_write(const char *buffer, int sector) = 0;
+    virtual int disk_sync() { return 0; }
+    virtual int disk_sectors() = 0;
+     
+};
+    
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/MemFileSystem.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,75 @@
+/* mbed Microcontroller Library - MemFileSystem
+ * Copyright (c) 2008, sford
+ */
+
+
+#ifndef MBED_MEMFILESYSTEM_H
+#define MBED_MEMFILESYSTEM_H
+
+#include "FATFileSystem.h"
+
+namespace mbed {
+
+class MemFileSystem : public FATFileSystem {
+public:
+
+    // 2000 sectors, each 512 bytes (malloced as required)
+    char *sectors[2000];
+
+    MemFileSystem(const char* name) : FATFileSystem(name) {
+        memset(sectors, 0, sizeof(sectors));
+    }
+
+    virtual ~MemFileSystem() {
+        for(int i = 0; i < 2000; i++) {
+            if(sectors[i]) {
+                free(sectors[i]);
+            }
+        }
+    }
+
+    // read a sector in to the buffer, return 0 if ok
+    virtual int disk_read(char *buffer, int sector) {
+        if(sectors[sector] == 0) {
+            // nothing allocated means sector is empty
+            memset(buffer, 0, 512);
+        } else {
+            memcpy(buffer, sectors[sector], 512);
+        }
+        return 0;
+    }
+
+    // write a sector from the buffer, return 0 if ok
+    virtual int disk_write(const char *buffer, int sector) {
+        // if buffer is zero deallocate sector
+        char zero[512];
+        memset(zero, 0, 512);
+        if(memcmp(zero, buffer, 512)==0) {
+            if(sectors[sector] != 0) {
+                free(sectors[sector]);
+                sectors[sector] = 0;
+            }
+            return 0;
+        }
+        // else allocate a sector if needed, and write
+        if(sectors[sector] == 0) {
+            char *sec = (char*)malloc(512);
+            if(sec==0) {
+                return 1; // out of memory
+            }
+            sectors[sector] = sec;
+        }
+        memcpy(sectors[sector], buffer, 512);
+        return 0;
+    }
+        
+    // return the number of sectors
+    virtual int disk_sectors() {
+        return sizeof(sectors)/sizeof(sectors[0]);
+    }
+
+};
+    
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/diskio.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,105 @@
+/*-----------------------------------------------------------------------*/
+/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2007        */
+/*-----------------------------------------------------------------------*/
+/* This is a stub disk I/O module that acts as front end of the existing */
+/* disk I/O modules and attach it to FatFs module with common interface. */
+/*-----------------------------------------------------------------------*/
+
+#include "diskio.h"
+#include <stdio.h>
+#include <string.h>
+#include "FATFileSystem.h"
+
+#include "mbed.h"
+
+DSTATUS disk_initialize (
+    BYTE drv                /* Physical drive nmuber (0..) */
+) 
+{
+    FFSDEBUG("disk_initialize on drv [%d]\n", drv);
+    return (DSTATUS)FATFileSystem::_ffs[drv]->disk_initialize();
+}
+
+DSTATUS disk_status (
+    BYTE drv        /* Physical drive nmuber (0..) */
+) 
+{
+    FFSDEBUG("disk_status on drv [%d]\n", drv);
+    return (DSTATUS)FATFileSystem::_ffs[drv]->disk_status();
+}
+
+DRESULT disk_read (
+    BYTE drv,        /* Physical drive nmuber (0..) */
+    BYTE *buff,        /* Data buffer to store read data */
+    DWORD sector,    /* Sector address (LBA) */
+    BYTE count        /* Number of sectors to read (1..255) */
+)
+{
+    FFSDEBUG("disk_read(sector %d, count %d) on drv [%d]\n", sector, count, drv);
+    for(DWORD s=sector; s<sector+count; s++) {
+        FFSDEBUG(" disk_read(sector %d)\n", s);
+        int res = FATFileSystem::_ffs[drv]->disk_read((char*)buff, s);
+        if(res) {
+            return RES_PARERR;
+        }
+        buff += 512;
+    }
+    return RES_OK;
+}
+
+#if _READONLY == 0
+DRESULT disk_write (
+    BYTE drv,            /* Physical drive nmuber (0..) */
+    const BYTE *buff,    /* Data to be written */
+    DWORD sector,        /* Sector address (LBA) */
+    BYTE count            /* Number of sectors to write (1..255) */
+)
+{
+    FFSDEBUG("disk_write(sector %d, count %d) on drv [%d]\n", sector, count, drv);
+    for(DWORD s=sector; s<sector+count; s++) {
+        FFSDEBUG(" disk_write(sector %d)\n", s);
+        int res = FATFileSystem::_ffs[drv]->disk_write((char*)buff, s);
+        if(res) {
+            return RES_PARERR;
+        }
+        buff += 512;
+    }
+    return RES_OK;
+}
+#endif /* _READONLY */
+
+DRESULT disk_ioctl (
+    BYTE drv,        /* Physical drive nmuber (0..) */
+    BYTE ctrl,        /* Control code */
+    void *buff        /* Buffer to send/receive control data */
+)
+{
+    FFSDEBUG("disk_ioctl(%d)\n", ctrl);
+    switch(ctrl) {
+        case CTRL_SYNC:
+            if(FATFileSystem::_ffs[drv] == NULL) {
+                return RES_NOTRDY;
+            } else if(FATFileSystem::_ffs[drv]->disk_sync()) {
+                return RES_ERROR;
+            } 
+            return RES_OK;
+        case GET_SECTOR_COUNT:
+            if(FATFileSystem::_ffs[drv] == NULL) {
+                return RES_NOTRDY;
+            } else {
+                int res = FATFileSystem::_ffs[drv]->disk_sectors();
+                if(res > 0) {
+                    *((DWORD*)buff) = res; // minimum allowed
+                    return RES_OK;
+                } else {
+                    return RES_ERROR;
+                }
+            }
+        case GET_BLOCK_SIZE:
+            *((DWORD*)buff) = 1; // default when not known
+            return RES_OK;
+
+    }
+    return RES_PARERR;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/diskio.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,78 @@
+/*-----------------------------------------------------------------------
+/  Low level disk interface modlue include file
+/-----------------------------------------------------------------------*/
+
+#ifndef _DISKIO
+
+#define _READONLY    0    /* 1: Remove write functions */
+#define _USE_IOCTL    1    /* 1: Use disk_ioctl fucntion */
+
+#include "integer.h"
+
+
+/* Status of Disk Functions */
+typedef BYTE    DSTATUS;
+
+/* Results of Disk Functions */
+typedef enum {
+    RES_OK = 0,        /* 0: Successful */
+    RES_ERROR,        /* 1: R/W Error */
+    RES_WRPRT,        /* 2: Write Protected */
+    RES_NOTRDY,        /* 3: Not Ready */
+    RES_PARERR        /* 4: Invalid Parameter */
+} DRESULT;
+
+
+/*---------------------------------------*/
+/* Prototypes for disk control functions */
+
+int assign_drives (int, int);
+DSTATUS disk_initialize (BYTE);
+DSTATUS disk_status (BYTE);
+DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);
+#if    _READONLY == 0
+DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);
+#endif
+DRESULT disk_ioctl (BYTE, BYTE, void*);
+
+
+
+/* Disk Status Bits (DSTATUS) */
+
+#define STA_NOINIT        0x01    /* Drive not initialized */
+#define STA_NODISK        0x02    /* No medium in the drive */
+#define STA_PROTECT        0x04    /* Write protected */
+
+
+/* Command code for disk_ioctrl fucntion */
+
+/* Generic command (defined for FatFs) */
+#define CTRL_SYNC            0    /* Flush disk cache (for write functions) */
+#define GET_SECTOR_COUNT    1    /* Get media size (for only f_mkfs()) */
+#define GET_SECTOR_SIZE        2    /* Get sector size (for multiple sector size (_MAX_SS >= 1024)) */
+#define GET_BLOCK_SIZE        3    /* Get erase block size (for only f_mkfs()) */
+#define CTRL_ERASE_SECTOR    4    /* Force erased a block of sectors (for only _USE_ERASE) */
+
+/* Generic command */
+#define CTRL_POWER            5    /* Get/Set power status */
+#define CTRL_LOCK            6    /* Lock/Unlock media removal */
+#define CTRL_EJECT            7    /* Eject media */
+
+/* MMC/SDC specific ioctl command */
+#define MMC_GET_TYPE        10    /* Get card type */
+#define MMC_GET_CSD            11    /* Get CSD */
+#define MMC_GET_CID            12    /* Get CID */
+#define MMC_GET_OCR            13    /* Get OCR */
+#define MMC_GET_SDSTAT        14    /* Get SD status */
+
+/* ATA/CF specific ioctl command */
+#define ATA_GET_REV            20    /* Get F/W revision */
+#define ATA_GET_MODEL        21    /* Get model name */
+#define ATA_GET_SN            22    /* Get serial number */
+
+/* NAND specific ioctl command */
+#define NAND_FORMAT            30    /* Create physical format */
+
+
+#define _DISKIO
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/ff.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,4077 @@
+/*----------------------------------------------------------------------------/
+/  FatFs - FAT file system module  R0.09                  (C)ChaN, 2011
+/-----------------------------------------------------------------------------/
+/ FatFs module is a generic FAT file system module for small embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following terms.
+/
+/  Copyright (C) 2011, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/-----------------------------------------------------------------------------/
+/ Feb 26,'06 R0.00  Prototype.
+/
+/ Apr 29,'06 R0.01  First stable version.
+/
+/ Jun 01,'06 R0.02  Added FAT12 support.
+/                   Removed unbuffered mode.
+/                   Fixed a problem on small (<32M) partition.
+/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
+/
+/ Sep 22,'06 R0.03  Added f_rename().
+/                   Changed option _FS_MINIMUM to _FS_MINIMIZE.
+/ Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast.
+/                   Fixed f_mkdir() creates incorrect directory on FAT32.
+/
+/ Feb 04,'07 R0.04  Supported multiple drive system.
+/                   Changed some interfaces for multiple drive system.
+/                   Changed f_mountdrv() to f_mount().
+/                   Added f_mkfs().
+/ Apr 01,'07 R0.04a Supported multiple partitions on a physical drive.
+/                   Added a capability of extending file size to f_lseek().
+/                   Added minimization level 3.
+/                   Fixed an endian sensitive code in f_mkfs().
+/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
+/                   Added FSInfo support.
+/                   Fixed DBCS name can result FR_INVALID_NAME.
+/                   Fixed short seek (<= csize) collapses the file object.
+/
+/ Aug 25,'07 R0.05  Changed arguments of f_read(), f_write() and f_mkfs().
+/                   Fixed f_mkfs() on FAT32 creates incorrect FSInfo.
+/                   Fixed f_mkdir() on FAT32 creates incorrect directory.
+/ Feb 03,'08 R0.05a Added f_truncate() and f_utime().
+/                   Fixed off by one error at FAT sub-type determination.
+/                   Fixed btr in f_read() can be mistruncated.
+/                   Fixed cached sector is not flushed when create and close without write.
+/
+/ Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets().
+/                   Improved performance of f_lseek() on moving to the same or following cluster.
+/
+/ Apr 01,'09 R0.07  Merged Tiny-FatFs as a configuration option. (_FS_TINY)
+/                   Added long file name feature.
+/                   Added multiple code page feature.
+/                   Added re-entrancy for multitask operation.
+/                   Added auto cluster size selection to f_mkfs().
+/                   Added rewind option to f_readdir().
+/                   Changed result code of critical errors.
+/                   Renamed string functions to avoid name collision.
+/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
+/                   Added multiple sector size feature.
+/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.
+/                   Fixed wrong cache control in f_lseek().
+/                   Added relative path feature.
+/                   Added f_chdir() and f_chdrive().
+/                   Added proper case conversion to extended char.
+/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.
+/                   Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH.
+/                   Fixed name matching error on the 13 char boundary.
+/                   Added a configuration option, _LFN_UNICODE.
+/                   Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
+/
+/ May 15,'10 R0.08  Added a memory configuration option. (_USE_LFN = 3)
+/                   Added file lock feature. (_FS_SHARE)
+/                   Added fast seek feature. (_USE_FASTSEEK)
+/                   Changed some types on the API, XCHAR->TCHAR.
+/                   Changed fname member in the FILINFO structure on Unicode cfg.
+/                   String functions support UTF-8 encoding files on Unicode cfg.
+/ Aug 16,'10 R0.08a Added f_getcwd(). (_FS_RPATH = 2)
+/                   Added sector erase feature. (_USE_ERASE)
+/                   Moved file lock semaphore table from fs object to the bss.
+/                   Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'.
+/                   Fixed f_mkfs() creates wrong FAT32 volume.
+/ Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write().
+/                   f_lseek() reports required table size on creating CLMP.
+/                   Extended format syntax of f_printf function.
+/                   Ignores duplicated directory separators in given path names.
+/
+/ Sep 06,'11 R0.09  f_mkfs() supports multiple partition to finish the multiple partition feature.
+/                   Added f_fdisk(). (_MULTI_PARTITION = 2)
+/---------------------------------------------------------------------------*/
+
+#include "ff.h"            /* FatFs configurations and declarations */
+#include "diskio.h"        /* Declarations of low level disk I/O functions */
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if _FATFS != 6502    /* Revision ID */
+#error Wrong include file (ff.h).
+#endif
+
+
+/* Definitions on sector size */
+#if _MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096
+#error Wrong sector size.
+#endif
+#if _MAX_SS != 512
+#define    SS(fs)    ((fs)->ssize)    /* Variable sector size */
+#else
+#define    SS(fs)    512U            /* Fixed sector size */
+#endif
+
+
+/* Reentrancy related */
+#if _FS_REENTRANT
+#if _USE_LFN == 1
+#error Static LFN work area must not be used in re-entrant configuration.
+#endif
+#define    ENTER_FF(fs)        { if (!lock_fs(fs)) return FR_TIMEOUT; }
+#define    LEAVE_FF(fs, res)    { unlock_fs(fs, res); return res; }
+#else
+#define    ENTER_FF(fs)
+#define LEAVE_FF(fs, res)    return res
+#endif
+
+#define    ABORT(fs, res)        { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }
+
+
+/* File shareing feature */
+#if _FS_SHARE
+#if _FS_READONLY
+#error _FS_SHARE must be 0 on read-only cfg.
+#endif
+typedef struct {
+    FATFS *fs;                /* File ID 1, volume (NULL:blank entry) */
+    DWORD clu;                /* File ID 2, directory */
+    WORD idx;                /* File ID 3, directory index */
+    WORD ctr;                /* File open counter, 0:none, 0x01..0xFF:read open count, 0x100:write mode */
+} FILESEM;
+#endif
+
+
+/* Misc definitions */
+#define LD_CLUST(dir)    (((DWORD)LD_WORD(dir+DIR_FstClusHI)<<16) | LD_WORD(dir+DIR_FstClusLO))
+#define ST_CLUST(dir,cl) {ST_WORD(dir+DIR_FstClusLO, cl); ST_WORD(dir+DIR_FstClusHI, (DWORD)cl>>16);}
+
+
+/* DBCS code ranges and SBCS extend char conversion table */
+
+#if _CODE_PAGE == 932    /* Japanese Shift-JIS */
+#define _DF1S    0x81    /* DBC 1st byte range 1 start */
+#define _DF1E    0x9F    /* DBC 1st byte range 1 end */
+#define _DF2S    0xE0    /* DBC 1st byte range 2 start */
+#define _DF2E    0xFC    /* DBC 1st byte range 2 end */
+#define _DS1S    0x40    /* DBC 2nd byte range 1 start */
+#define _DS1E    0x7E    /* DBC 2nd byte range 1 end */
+#define _DS2S    0x80    /* DBC 2nd byte range 2 start */
+#define _DS2E    0xFC    /* DBC 2nd byte range 2 end */
+
+#elif _CODE_PAGE == 936    /* Simplified Chinese GBK */
+#define _DF1S    0x81
+#define _DF1E    0xFE
+#define _DS1S    0x40
+#define _DS1E    0x7E
+#define _DS2S    0x80
+#define _DS2E    0xFE
+
+#elif _CODE_PAGE == 949    /* Korean */
+#define _DF1S    0x81
+#define _DF1E    0xFE
+#define _DS1S    0x41
+#define _DS1E    0x5A
+#define _DS2S    0x61
+#define _DS2E    0x7A
+#define _DS3S    0x81
+#define _DS3E    0xFE
+
+#elif _CODE_PAGE == 950    /* Traditional Chinese Big5 */
+#define _DF1S    0x81
+#define _DF1E    0xFE
+#define _DS1S    0x40
+#define _DS1E    0x7E
+#define _DS2S    0xA1
+#define _DS2E    0xFE
+
+#elif _CODE_PAGE == 437    /* U.S. (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 720    /* Arabic (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 737    /* Greek (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
+                0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 775    /* Baltic (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 850    /* Multilingual Latin 1 (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+                0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 852    /* Latin 2 (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \
+                0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
+
+#elif _CODE_PAGE == 855    /* Cyrillic (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
+                0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
+                0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 857    /* Turkish (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
+                0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 858    /* Multilingual Latin 1 + Euro (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+                0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 862    /* Hebrew (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 866    /* Russian (OEM) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 874    /* Thai (OEM, Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
+
+#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
+                0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF}
+
+#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \
+                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
+
+#elif _CODE_PAGE == 1253 /* Greek (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \
+                0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF}
+
+#elif _CODE_PAGE == 1254 /* Turkish (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \
+                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
+
+#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1256 /* Arabic (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1257 /* Baltic (Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
+
+#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */
+#define _DF1S    0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \
+                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F}
+
+#elif _CODE_PAGE == 1    /* ASCII (for only non-LFN cfg) */
+#if _USE_LFN
+#error Cannot use LFN feature without valid code page.
+#endif
+#define _DF1S    0
+
+#else
+#error Unknown code page
+
+#endif
+
+
+/* Character code support macros */
+#define IsUpper(c)    (((c)>='A')&&((c)<='Z'))
+#define IsLower(c)    (((c)>='a')&&((c)<='z'))
+#define IsDigit(c)    (((c)>='0')&&((c)<='9'))
+
+#if _DF1S        /* Code page is DBCS */
+
+#ifdef _DF2S    /* Two 1st byte areas */
+#define IsDBCS1(c)    (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))
+#else            /* One 1st byte area */
+#define IsDBCS1(c)    ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
+#endif
+
+#ifdef _DS3S    /* Three 2nd byte areas */
+#define IsDBCS2(c)    (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))
+#else            /* Two 2nd byte areas */
+#define IsDBCS2(c)    (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))
+#endif
+
+#else            /* Code page is SBCS */
+
+#define IsDBCS1(c)    0
+#define IsDBCS2(c)    0
+
+#endif /* _DF1S */
+
+
+/* Name status flags */
+#define NS            11        /* Index of name status byte in fn[] */
+#define NS_LOSS        0x01    /* Out of 8.3 format */
+#define NS_LFN        0x02    /* Force to create LFN entry */
+#define NS_LAST        0x04    /* Last segment */
+#define NS_BODY        0x08    /* Lower case flag (body) */
+#define NS_EXT        0x10    /* Lower case flag (ext) */
+#define NS_DOT        0x20    /* Dot entry */
+
+
+/* FAT sub-type boundaries */
+/* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */
+#define MIN_FAT16    4086    /* Minimum number of clusters for FAT16 */
+#define    MIN_FAT32    65526    /* Minimum number of clusters for FAT32 */
+
+
+/* FatFs refers the members in the FAT structures as byte array instead of
+/ structure member because the structure is not binary compatible between
+/ different platforms */
+
+#define BS_jmpBoot            0    /* Jump instruction (3) */
+#define BS_OEMName            3    /* OEM name (8) */
+#define BPB_BytsPerSec        11    /* Sector size [byte] (2) */
+#define BPB_SecPerClus        13    /* Cluster size [sector] (1) */
+#define BPB_RsvdSecCnt        14    /* Size of reserved area [sector] (2) */
+#define BPB_NumFATs            16    /* Number of FAT copies (1) */
+#define BPB_RootEntCnt        17    /* Number of root dir entries for FAT12/16 (2) */
+#define BPB_TotSec16        19    /* Volume size [sector] (2) */
+#define BPB_Media            21    /* Media descriptor (1) */
+#define BPB_FATSz16            22    /* FAT size [sector] (2) */
+#define BPB_SecPerTrk        24    /* Track size [sector] (2) */
+#define BPB_NumHeads        26    /* Number of heads (2) */
+#define BPB_HiddSec            28    /* Number of special hidden sectors (4) */
+#define BPB_TotSec32        32    /* Volume size [sector] (4) */
+#define BS_DrvNum            36    /* Physical drive number (2) */
+#define BS_BootSig            38    /* Extended boot signature (1) */
+#define BS_VolID            39    /* Volume serial number (4) */
+#define BS_VolLab            43    /* Volume label (8) */
+#define BS_FilSysType        54    /* File system type (1) */
+#define BPB_FATSz32            36    /* FAT size [sector] (4) */
+#define BPB_ExtFlags        40    /* Extended flags (2) */
+#define BPB_FSVer            42    /* File system version (2) */
+#define BPB_RootClus        44    /* Root dir first cluster (4) */
+#define BPB_FSInfo            48    /* Offset of FSInfo sector (2) */
+#define BPB_BkBootSec        50    /* Offset of backup boot sectot (2) */
+#define BS_DrvNum32            64    /* Physical drive number (2) */
+#define BS_BootSig32        66    /* Extended boot signature (1) */
+#define BS_VolID32            67    /* Volume serial number (4) */
+#define BS_VolLab32            71    /* Volume label (8) */
+#define BS_FilSysType32        82    /* File system type (1) */
+#define    FSI_LeadSig            0    /* FSI: Leading signature (4) */
+#define    FSI_StrucSig        484    /* FSI: Structure signature (4) */
+#define    FSI_Free_Count        488    /* FSI: Number of free clusters (4) */
+#define    FSI_Nxt_Free        492    /* FSI: Last allocated cluster (4) */
+#define MBR_Table            446    /* MBR: Partition table offset (2) */
+#define    SZ_PTE                16    /* MBR: Size of a partition table entry */
+#define BS_55AA                510    /* Boot sector signature (2) */
+
+#define    DIR_Name            0    /* Short file name (11) */
+#define    DIR_Attr            11    /* Attribute (1) */
+#define    DIR_NTres            12    /* NT flag (1) */
+#define    DIR_CrtTime            14    /* Created time (2) */
+#define    DIR_CrtDate            16    /* Created date (2) */
+#define    DIR_FstClusHI        20    /* Higher 16-bit of first cluster (2) */
+#define    DIR_WrtTime            22    /* Modified time (2) */
+#define    DIR_WrtDate            24    /* Modified date (2) */
+#define    DIR_FstClusLO        26    /* Lower 16-bit of first cluster (2) */
+#define    DIR_FileSize        28    /* File size (4) */
+#define    LDIR_Ord            0    /* LFN entry order and LLE flag (1) */
+#define    LDIR_Attr            11    /* LFN attribute (1) */
+#define    LDIR_Type            12    /* LFN type (1) */
+#define    LDIR_Chksum            13    /* Sum of corresponding SFN entry */
+#define    LDIR_FstClusLO        26    /* Filled by zero (0) */
+#define    SZ_DIR                32        /* Size of a directory entry */
+#define    LLE                    0x40    /* Last long entry flag in LDIR_Ord */
+#define    DDE                    0xE5    /* Deleted directory enrty mark in DIR_Name[0] */
+#define    NDDE                0x05    /* Replacement of a character collides with DDE */
+
+
+/*------------------------------------------------------------*/
+/* Module private work area                                   */
+/*------------------------------------------------------------*/
+/* Note that uninitialized variables with static duration are
+/  zeroed/nulled at start-up. If not, the compiler or start-up
+/  routine is out of ANSI-C standard.
+*/
+
+#if _VOLUMES
+static
+FATFS *FatFs[_VOLUMES];    /* Pointer to the file system objects (logical drives) */
+#else
+#error Number of volumes must not be 0.
+#endif
+
+static
+WORD Fsid;                /* File system mount ID */
+
+#if _FS_RPATH
+static
+BYTE CurrVol;            /* Current drive */
+#endif
+
+#if _FS_SHARE
+static
+FILESEM    Files[_FS_SHARE];    /* File lock semaphores */
+#endif
+
+#if _USE_LFN == 0            /* No LFN feature */
+#define    DEF_NAMEBUF            BYTE sfn[12]
+#define INIT_BUF(dobj)        (dobj).fn = sfn
+#define    FREE_BUF()
+
+#elif _USE_LFN == 1            /* LFN feature with static working buffer */
+static WCHAR LfnBuf[_MAX_LFN+1];
+#define    DEF_NAMEBUF            BYTE sfn[12]
+#define INIT_BUF(dobj)        { (dobj).fn = sfn; (dobj).lfn = LfnBuf; }
+#define    FREE_BUF()
+
+#elif _USE_LFN == 2         /* LFN feature with dynamic working buffer on the stack */
+#define    DEF_NAMEBUF            BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1]
+#define INIT_BUF(dobj)        { (dobj).fn = sfn; (dobj).lfn = lbuf; }
+#define    FREE_BUF()
+
+#elif _USE_LFN == 3         /* LFN feature with dynamic working buffer on the heap */
+#define    DEF_NAMEBUF            BYTE sfn[12]; WCHAR *lfn
+#define INIT_BUF(dobj)        { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \
+                              if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \
+                              (dobj).lfn = lfn;    (dobj).fn = sfn; }
+#define    FREE_BUF()            ff_memfree(lfn)
+
+#else
+#error Wrong LFN configuration.
+#endif
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Functions
+
+---------------------------------------------------------------------------*/
+
+
+/*-----------------------------------------------------------------------*/
+/* String functions                                                      */
+/*-----------------------------------------------------------------------*/
+
+/* Copy memory to memory */
+static
+void mem_cpy (void* dst, const void* src, UINT cnt) {
+    BYTE *d = (BYTE*)dst;
+    const BYTE *s = (const BYTE*)src;
+
+#if _WORD_ACCESS == 1
+    while (cnt >= sizeof(int)) {
+        *(int*)d = *(int*)s;
+        d += sizeof(int); s += sizeof(int);
+        cnt -= sizeof(int);
+    }
+#endif
+    while (cnt--)
+        *d++ = *s++;
+}
+
+/* Fill memory */
+static
+void mem_set (void* dst, int val, UINT cnt) {
+    BYTE *d = (BYTE*)dst;
+
+    while (cnt--)
+        *d++ = (BYTE)val;
+}
+
+/* Compare memory to memory */
+static
+int mem_cmp (const void* dst, const void* src, UINT cnt) {
+    const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;
+    int r = 0;
+
+    while (cnt-- && (r = *d++ - *s++) == 0) ;
+    return r;
+}
+
+/* Check if chr is contained in the string */
+static
+int chk_chr (const char* str, int chr) {
+    while (*str && *str != chr) str++;
+    return *str;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Request/Release grant to access the volume                            */
+/*-----------------------------------------------------------------------*/
+#if _FS_REENTRANT
+
+static
+int lock_fs (
+    FATFS *fs        /* File system object */
+)
+{
+    return ff_req_grant(fs->sobj);
+}
+
+
+static
+void unlock_fs (
+    FATFS *fs,        /* File system object */
+    FRESULT res        /* Result code to be returned */
+)
+{
+    if (res != FR_NOT_ENABLED &&
+        res != FR_INVALID_DRIVE &&
+        res != FR_INVALID_OBJECT &&
+        res != FR_TIMEOUT) {
+        ff_rel_grant(fs->sobj);
+    }
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* File shareing control functions                                       */
+/*-----------------------------------------------------------------------*/
+#if _FS_SHARE
+
+static
+FRESULT chk_lock (    /* Check if the file can be accessed */
+    FATFS_DIR* dj,        /* Directory object pointing the file to be checked */
+    int acc            /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
+)
+{
+    UINT i, be;
+
+    /* Search file semaphore table */
+    for (i = be = 0; i < _FS_SHARE; i++) {
+        if (Files[i].fs) {    /* Existing entry */
+            if (Files[i].fs == dj->fs &&         /* Check if the file matched with an open file */
+                Files[i].clu == dj->sclust &&
+                Files[i].idx == dj->index) break;
+        } else {            /* Blank entry */
+            be++;
+        }
+    }
+    if (i == _FS_SHARE)    /* The file is not opened */
+        return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES;    /* Is there a blank entry for new file? */
+
+    /* The file has been opened. Reject any open against writing file and all write mode open */
+    return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
+}
+
+
+static
+int enq_lock (void)    /* Check if an entry is available for a new file */
+{
+    UINT i;
+
+    for (i = 0; i < _FS_SHARE && Files[i].fs; i++) ;
+    return (i == _FS_SHARE) ? 0 : 1;
+}
+
+
+static
+UINT inc_lock (    /* Increment file open counter and returns its index (0:int error) */
+    FATFS_DIR* dj,    /* Directory object pointing the file to register or increment */
+    int acc        /* Desired access mode (0:Read, !0:Write) */
+)
+{
+    UINT i;
+
+
+    for (i = 0; i < _FS_SHARE; i++) {    /* Find the file */
+        if (Files[i].fs == dj->fs &&
+            Files[i].clu == dj->sclust &&
+            Files[i].idx == dj->index) break;
+    }
+
+    if (i == _FS_SHARE) {                /* Not opened. Register it as new. */
+        for (i = 0; i < _FS_SHARE && Files[i].fs; i++) ;
+        if (i == _FS_SHARE) return 0;    /* No space to register (int err) */
+        Files[i].fs = dj->fs;
+        Files[i].clu = dj->sclust;
+        Files[i].idx = dj->index;
+        Files[i].ctr = 0;
+    }
+
+    if (acc && Files[i].ctr) return 0;    /* Access violation (int err) */
+
+    Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1;    /* Set semaphore value */
+
+    return i + 1;
+}
+
+
+static
+FRESULT dec_lock (    /* Decrement file open counter */
+    UINT i            /* Semaphore index */
+)
+{
+    WORD n;
+    FRESULT res;
+
+
+    if (--i < _FS_SHARE) {
+        n = Files[i].ctr;
+        if (n == 0x100) n = 0;
+        if (n) n--;
+        Files[i].ctr = n;
+        if (!n) Files[i].fs = 0;
+        res = FR_OK;
+    } else {
+        res = FR_INT_ERR;
+    }
+    return res;
+}
+
+
+static
+void clear_lock (    /* Clear lock entries of the volume */
+    FATFS *fs
+)
+{
+    UINT i;
+
+    for (i = 0; i < _FS_SHARE; i++) {
+        if (Files[i].fs == fs) Files[i].fs = 0;
+    }
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change window offset                                                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT move_window (
+    FATFS *fs,        /* File system object */
+    DWORD sector    /* Sector number to make appearance in the fs->win[] */
+)                    /* Move to zero only writes back dirty window */
+{
+    DWORD wsect;
+
+
+    wsect = fs->winsect;
+    if (wsect != sector) {    /* Changed current window */
+#if !_FS_READONLY
+        if (fs->wflag) {    /* Write back dirty window if needed */
+            if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK)
+                return FR_DISK_ERR;
+            fs->wflag = 0;
+            if (wsect < (fs->fatbase + fs->fsize)) {    /* In FAT area */
+                BYTE nf;
+                for (nf = fs->n_fats; nf > 1; nf--) {    /* Reflect the change to all FAT copies */
+                    wsect += fs->fsize;
+                    disk_write(fs->drv, fs->win, wsect, 1);
+                }
+            }
+        }
+#endif
+        if (sector) {
+            if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK)
+                return FR_DISK_ERR;
+            fs->winsect = sector;
+        }
+    }
+
+    return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Clean-up cached data                                                  */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT sync (    /* FR_OK: successful, FR_DISK_ERR: failed */
+    FATFS *fs    /* File system object */
+)
+{
+    FRESULT res;
+
+
+    res = move_window(fs, 0);
+    if (res == FR_OK) {
+        /* Update FSInfo sector if needed */
+        if (fs->fs_type == FS_FAT32 && fs->fsi_flag) {
+            fs->winsect = 0;
+            /* Create FSInfo structure */
+            mem_set(fs->win, 0, 512);
+            ST_WORD(fs->win+BS_55AA, 0xAA55);
+            ST_DWORD(fs->win+FSI_LeadSig, 0x41615252);
+            ST_DWORD(fs->win+FSI_StrucSig, 0x61417272);
+            ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust);
+            ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust);
+            /* Write it into the FSInfo sector */
+            disk_write(fs->drv, fs->win, fs->fsi_sector, 1);
+            fs->fsi_flag = 0;
+        }
+        /* Make sure that no pending write process in the physical drive */
+        if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK)
+            res = FR_DISK_ERR;
+    }
+
+    return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get sector# from cluster#                                             */
+/*-----------------------------------------------------------------------*/
+
+
+DWORD clust2sect (    /* !=0: Sector number, 0: Failed - invalid cluster# */
+    FATFS *fs,        /* File system object */
+    DWORD clst        /* Cluster# to be converted */
+)
+{
+    clst -= 2;
+    if (clst >= (fs->n_fatent - 2)) return 0;        /* Invalid cluster# */
+    return clst * fs->csize + fs->database;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Read value of a FAT entry                                */
+/*-----------------------------------------------------------------------*/
+
+
+DWORD get_fat (    /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */
+    FATFS *fs,    /* File system object */
+    DWORD clst    /* Cluster# to get the link information */
+)
+{
+    UINT wc, bc;
+    BYTE *p;
+
+
+    if (clst < 2 || clst >= fs->n_fatent)    /* Chack range */
+        return 1;
+
+    switch (fs->fs_type) {
+    case FS_FAT12 :
+        bc = (UINT)clst; bc += bc / 2;
+        if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
+        wc = fs->win[bc % SS(fs)]; bc++;
+        if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
+        wc |= fs->win[bc % SS(fs)] << 8;
+        return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);
+
+    case FS_FAT16 :
+        if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) break;
+        p = &fs->win[clst * 2 % SS(fs)];
+        return LD_WORD(p);
+
+    case FS_FAT32 :
+        if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) break;
+        p = &fs->win[clst * 4 % SS(fs)];
+        return LD_DWORD(p) & 0x0FFFFFFF;
+    }
+
+    return 0xFFFFFFFF;    /* An error occurred at the disk I/O layer */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Change value of a FAT entry                              */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+
+FRESULT put_fat (
+    FATFS *fs,    /* File system object */
+    DWORD clst,    /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */
+    DWORD val    /* New value to mark the cluster */
+)
+{
+    UINT bc;
+    BYTE *p;
+    FRESULT res;
+
+
+    if (clst < 2 || clst >= fs->n_fatent) {    /* Check range */
+        res = FR_INT_ERR;
+
+    } else {
+        switch (fs->fs_type) {
+        case FS_FAT12 :
+            bc = clst; bc += bc / 2;
+            res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+            if (res != FR_OK) break;
+            p = &fs->win[bc % SS(fs)];
+            *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
+            bc++;
+            fs->wflag = 1;
+            res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+            if (res != FR_OK) break;
+            p = &fs->win[bc % SS(fs)];
+            *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
+            break;
+
+        case FS_FAT16 :
+            res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
+            if (res != FR_OK) break;
+            p = &fs->win[clst * 2 % SS(fs)];
+            ST_WORD(p, (WORD)val);
+            break;
+
+        case FS_FAT32 :
+            res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
+            if (res != FR_OK) break;
+            p = &fs->win[clst * 4 % SS(fs)];
+            val |= LD_DWORD(p) & 0xF0000000;
+            ST_DWORD(p, val);
+            break;
+
+        default :
+            res = FR_INT_ERR;
+        }
+        fs->wflag = 1;
+    }
+
+    return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Remove a cluster chain                                 */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT remove_chain (
+    FATFS *fs,            /* File system object */
+    DWORD clst            /* Cluster# to remove a chain from */
+)
+{
+    FRESULT res;
+    DWORD nxt;
+#if _USE_ERASE
+    DWORD scl = clst, ecl = clst, resion[2];
+#endif
+
+    if (clst < 2 || clst >= fs->n_fatent) {    /* Check range */
+        res = FR_INT_ERR;
+
+    } else {
+        res = FR_OK;
+        while (clst < fs->n_fatent) {            /* Not a last link? */
+            nxt = get_fat(fs, clst);            /* Get cluster status */
+            if (nxt == 0) break;                /* Empty cluster? */
+            if (nxt == 1) { res = FR_INT_ERR; break; }    /* Internal error? */
+            if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }    /* Disk error? */
+            res = put_fat(fs, clst, 0);            /* Mark the cluster "empty" */
+            if (res != FR_OK) break;
+            if (fs->free_clust != 0xFFFFFFFF) {    /* Update FSInfo */
+                fs->free_clust++;
+                fs->fsi_flag = 1;
+            }
+#if _USE_ERASE
+            if (ecl + 1 == nxt) {    /* Next cluster is contiguous */
+                ecl = nxt;
+            } else {                /* End of contiguous clusters */ 
+                resion[0] = clust2sect(fs, scl);                    /* Start sector */
+                resion[1] = clust2sect(fs, ecl) + fs->csize - 1;    /* End sector */
+                disk_ioctl(fs->drv, CTRL_ERASE_SECTOR, resion);        /* Erase the block */
+                scl = ecl = nxt;
+            }
+#endif
+            clst = nxt;    /* Next cluster */
+        }
+    }
+
+    return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Stretch or Create a cluster chain                      */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+DWORD create_chain (    /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
+    FATFS *fs,            /* File system object */
+    DWORD clst            /* Cluster# to stretch. 0 means create a new chain. */
+)
+{
+    DWORD cs, ncl, scl;
+    FRESULT res;
+
+
+    if (clst == 0) {        /* Create a new chain */
+        scl = fs->last_clust;            /* Get suggested start point */
+        if (!scl || scl >= fs->n_fatent) scl = 1;
+    }
+    else {                    /* Stretch the current chain */
+        cs = get_fat(fs, clst);            /* Check the cluster status */
+        if (cs < 2) return 1;            /* It is an invalid cluster */
+        if (cs < fs->n_fatent) return cs;    /* It is already followed by next cluster */
+        scl = clst;
+    }
+
+    ncl = scl;                /* Start cluster */
+    for (;;) {
+        ncl++;                            /* Next cluster */
+        if (ncl >= fs->n_fatent) {        /* Wrap around */
+            ncl = 2;
+            if (ncl > scl) return 0;    /* No free cluster */
+        }
+        cs = get_fat(fs, ncl);            /* Get the cluster status */
+        if (cs == 0) break;                /* Found a free cluster */
+        if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */
+            return cs;
+        if (ncl == scl) return 0;        /* No free cluster */
+    }
+
+    res = put_fat(fs, ncl, 0x0FFFFFFF);    /* Mark the new cluster "last link" */
+    if (res == FR_OK && clst != 0) {
+        res = put_fat(fs, clst, ncl);    /* Link it to the previous one if needed */
+    }
+    if (res == FR_OK) {
+        fs->last_clust = ncl;            /* Update FSINFO */
+        if (fs->free_clust != 0xFFFFFFFF) {
+            fs->free_clust--;
+            fs->fsi_flag = 1;
+        }
+    } else {
+        ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1;
+    }
+
+    return ncl;        /* Return new cluster number or error code */
+}
+#endif /* !_FS_READONLY */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Convert offset into cluster with link map table        */
+/*-----------------------------------------------------------------------*/
+
+#if _USE_FASTSEEK
+static
+DWORD clmt_clust (    /* <2:Error, >=2:Cluster number */
+    FIL* fp,        /* Pointer to the file object */
+    DWORD ofs        /* File offset to be converted to cluster# */
+)
+{
+    DWORD cl, ncl, *tbl;
+
+
+    tbl = fp->cltbl + 1;    /* Top of CLMT */
+    cl = ofs / SS(fp->fs) / fp->fs->csize;    /* Cluster order from top of the file */
+    for (;;) {
+        ncl = *tbl++;            /* Number of cluters in the fragment */
+        if (!ncl) return 0;        /* End of table? (error) */
+        if (cl < ncl) break;    /* In this fragment? */
+        cl -= ncl; tbl++;        /* Next fragment */
+    }
+    return cl + *tbl;    /* Return the cluster number */
+}
+#endif    /* _USE_FASTSEEK */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Set directory index                              */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_sdi (
+    FATFS_DIR *dj,        /* Pointer to directory object */
+    WORD idx        /* Directory index number */
+)
+{
+    DWORD clst;
+    WORD ic;
+
+
+    dj->index = idx;
+    clst = dj->sclust;
+    if (clst == 1 || clst >= dj->fs->n_fatent)    /* Check start cluster range */
+        return FR_INT_ERR;
+    if (!clst && dj->fs->fs_type == FS_FAT32)    /* Replace cluster# 0 with root cluster# if in FAT32 */
+        clst = dj->fs->dirbase;
+
+    if (clst == 0) {    /* Static table (root-dir in FAT12/16) */
+        dj->clust = clst;
+        if (idx >= dj->fs->n_rootdir)        /* Index is out of range */
+            return FR_INT_ERR;
+        dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / SZ_DIR);    /* Sector# */
+    }
+    else {                /* Dynamic table (sub-dirs or root-dir in FAT32) */
+        ic = SS(dj->fs) / SZ_DIR * dj->fs->csize;    /* Entries per cluster */
+        while (idx >= ic) {    /* Follow cluster chain */
+            clst = get_fat(dj->fs, clst);                /* Get next cluster */
+            if (clst == 0xFFFFFFFF) return FR_DISK_ERR;    /* Disk error */
+            if (clst < 2 || clst >= dj->fs->n_fatent)    /* Reached to end of table or int error */
+                return FR_INT_ERR;
+            idx -= ic;
+        }
+        dj->clust = clst;
+        dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / SZ_DIR);    /* Sector# */
+    }
+
+    dj->dir = dj->fs->win + (idx % (SS(dj->fs) / SZ_DIR)) * SZ_DIR;    /* Ptr to the entry in the sector */
+
+    return FR_OK;    /* Seek succeeded */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Move directory index next                        */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_next (    /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */
+    FATFS_DIR *dj,        /* Pointer to directory object */
+    int stretch        /* 0: Do not stretch table, 1: Stretch table if needed */
+)
+{
+    DWORD clst;
+    WORD i;
+
+
+    stretch = stretch;        /* To suppress warning on read-only cfg. */
+    i = dj->index + 1;
+    if (!i || !dj->sect)    /* Report EOT when index has reached 65535 */
+        return FR_NO_FILE;
+
+    if (!(i % (SS(dj->fs) / SZ_DIR))) {    /* Sector changed? */
+        dj->sect++;                    /* Next sector */
+
+        if (dj->clust == 0) {    /* Static table */
+            if (i >= dj->fs->n_rootdir)    /* Report EOT when end of table */
+                return FR_NO_FILE;
+        }
+        else {                    /* Dynamic table */
+            if (((i / (SS(dj->fs) / SZ_DIR)) & (dj->fs->csize - 1)) == 0) {    /* Cluster changed? */
+                clst = get_fat(dj->fs, dj->clust);                /* Get next cluster */
+                if (clst <= 1) return FR_INT_ERR;
+                if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+                if (clst >= dj->fs->n_fatent) {                    /* When it reached end of dynamic table */
+#if !_FS_READONLY
+                    BYTE c;
+                    if (!stretch) return FR_NO_FILE;            /* When do not stretch, report EOT */
+                    clst = create_chain(dj->fs, dj->clust);        /* Stretch cluster chain */
+                    if (clst == 0) return FR_DENIED;            /* No free cluster */
+                    if (clst == 1) return FR_INT_ERR;
+                    if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+                    /* Clean-up stretched table */
+                    if (move_window(dj->fs, 0)) return FR_DISK_ERR;    /* Flush active window */
+                    mem_set(dj->fs->win, 0, SS(dj->fs));            /* Clear window buffer */
+                    dj->fs->winsect = clust2sect(dj->fs, clst);    /* Cluster start sector */
+                    for (c = 0; c < dj->fs->csize; c++) {        /* Fill the new cluster with 0 */
+                        dj->fs->wflag = 1;
+                        if (move_window(dj->fs, 0)) return FR_DISK_ERR;
+                        dj->fs->winsect++;
+                    }
+                    dj->fs->winsect -= c;                        /* Rewind window address */
+#else
+                    return FR_NO_FILE;            /* Report EOT */
+#endif
+                }
+                dj->clust = clst;                /* Initialize data for new cluster */
+                dj->sect = clust2sect(dj->fs, clst);
+            }
+        }
+    }
+
+    dj->index = i;
+    dj->dir = dj->fs->win + (i % (SS(dj->fs) / SZ_DIR)) * SZ_DIR;
+
+    return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};    /* Offset of LFN chars in the directory entry */
+
+
+static
+int cmp_lfn (            /* 1:Matched, 0:Not matched */
+    WCHAR *lfnbuf,        /* Pointer to the LFN to be compared */
+    BYTE *dir            /* Pointer to the directory entry containing a part of LFN */
+)
+{
+    UINT i, s;
+    WCHAR wc, uc;
+
+
+    i = ((dir[LDIR_Ord] & ~LLE) - 1) * 13;    /* Get offset in the LFN buffer */
+    s = 0; wc = 1;
+    do {
+        uc = LD_WORD(dir+LfnOfs[s]);    /* Pick an LFN character from the entry */
+        if (wc) {    /* Last char has not been processed */
+            wc = ff_wtoupper(uc);        /* Convert it to upper case */
+            if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++]))    /* Compare it */
+                return 0;                /* Not matched */
+        } else {
+            if (uc != 0xFFFF) return 0;    /* Check filler */
+        }
+    } while (++s < 13);                /* Repeat until all chars in the entry are checked */
+
+    if ((dir[LDIR_Ord] & LLE) && wc && lfnbuf[i])    /* Last segment matched but different length */
+        return 0;
+
+    return 1;                        /* The part of LFN matched */
+}
+
+
+
+static
+int pick_lfn (            /* 1:Succeeded, 0:Buffer overflow */
+    WCHAR *lfnbuf,        /* Pointer to the Unicode-LFN buffer */
+    BYTE *dir            /* Pointer to the directory entry */
+)
+{
+    UINT i, s;
+    WCHAR wc, uc;
+
+
+    i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;    /* Offset in the LFN buffer */
+
+    s = 0; wc = 1;
+    do {
+        uc = LD_WORD(dir+LfnOfs[s]);        /* Pick an LFN character from the entry */
+        if (wc) {    /* Last char has not been processed */
+            if (i >= _MAX_LFN) return 0;    /* Buffer overflow? */
+            lfnbuf[i++] = wc = uc;            /* Store it */
+        } else {
+            if (uc != 0xFFFF) return 0;        /* Check filler */
+        }
+    } while (++s < 13);                        /* Read all character in the entry */
+
+    if (dir[LDIR_Ord] & LLE) {                /* Put terminator if it is the last LFN part */
+        if (i >= _MAX_LFN) return 0;        /* Buffer overflow? */
+        lfnbuf[i] = 0;
+    }
+
+    return 1;
+}
+
+
+#if !_FS_READONLY
+static
+void fit_lfn (
+    const WCHAR *lfnbuf,    /* Pointer to the LFN buffer */
+    BYTE *dir,                /* Pointer to the directory entry */
+    BYTE ord,                /* LFN order (1-20) */
+    BYTE sum                /* SFN sum */
+)
+{
+    UINT i, s;
+    WCHAR wc;
+
+
+    dir[LDIR_Chksum] = sum;            /* Set check sum */
+    dir[LDIR_Attr] = AM_LFN;        /* Set attribute. LFN entry */
+    dir[LDIR_Type] = 0;
+    ST_WORD(dir+LDIR_FstClusLO, 0);
+
+    i = (ord - 1) * 13;                /* Get offset in the LFN buffer */
+    s = wc = 0;
+    do {
+        if (wc != 0xFFFF) wc = lfnbuf[i++];    /* Get an effective char */
+        ST_WORD(dir+LfnOfs[s], wc);    /* Put it */
+        if (!wc) wc = 0xFFFF;        /* Padding chars following last char */
+    } while (++s < 13);
+    if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLE;    /* Bottom LFN part is the start of LFN sequence */
+    dir[LDIR_Ord] = ord;            /* Set the LFN order */
+}
+
+#endif
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create numbered name                                                  */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+void gen_numname (
+    BYTE *dst,            /* Pointer to generated SFN */
+    const BYTE *src,    /* Pointer to source SFN to be modified */
+    const WCHAR *lfn,    /* Pointer to LFN */
+    WORD seq            /* Sequence number */
+)
+{
+    BYTE ns[8], c;
+    UINT i, j;
+
+
+    mem_cpy(dst, src, 11);
+
+    if (seq > 5) {    /* On many collisions, generate a hash number instead of sequential number */
+        do seq = (seq >> 1) + (seq << 15) + (WORD)*lfn++; while (*lfn);
+    }
+
+    /* itoa (hexdecimal) */
+    i = 7;
+    do {
+        c = (seq % 16) + '0';
+        if (c > '9') c += 7;
+        ns[i--] = c;
+        seq /= 16;
+    } while (seq);
+    ns[i] = '~';
+
+    /* Append the number */
+    for (j = 0; j < i && dst[j] != ' '; j++) {
+        if (IsDBCS1(dst[j])) {
+            if (j == i - 1) break;
+            j++;
+        }
+    }
+    do {
+        dst[j++] = (i < 8) ? ns[i++] : ' ';
+    } while (j < 8);
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Calculate sum of an SFN                                               */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+BYTE sum_sfn (
+    const BYTE *dir        /* Ptr to directory entry */
+)
+{
+    BYTE sum = 0;
+    UINT n = 11;
+
+    do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
+    return sum;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Find an object in the directory                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_find (
+    FATFS_DIR *dj            /* Pointer to the directory object linked to the file name */
+)
+{
+    FRESULT res;
+    BYTE c, *dir;
+#if _USE_LFN
+    BYTE a, ord, sum;
+#endif
+
+    res = dir_sdi(dj, 0);            /* Rewind directory object */
+    if (res != FR_OK) return res;
+
+#if _USE_LFN
+    ord = sum = 0xFF;
+#endif
+    do {
+        res = move_window(dj->fs, dj->sect);
+        if (res != FR_OK) break;
+        dir = dj->dir;                    /* Ptr to the directory entry of current index */
+        c = dir[DIR_Name];
+        if (c == 0) { res = FR_NO_FILE; break; }    /* Reached to end of table */
+#if _USE_LFN    /* LFN configuration */
+        a = dir[DIR_Attr] & AM_MASK;
+        if (c == DDE || ((a & AM_VOL) && a != AM_LFN)) {    /* An entry without valid data */
+            ord = 0xFF;
+        } else {
+            if (a == AM_LFN) {            /* An LFN entry is found */
+                if (dj->lfn) {
+                    if (c & LLE) {        /* Is it start of LFN sequence? */
+                        sum = dir[LDIR_Chksum];
+                        c &= ~LLE; ord = c;    /* LFN start order */
+                        dj->lfn_idx = dj->index;
+                    }
+                    /* Check validity of the LFN entry and compare it with given name */
+                    ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+                }
+            } else {                    /* An SFN entry is found */
+                if (!ord && sum == sum_sfn(dir)) break;    /* LFN matched? */
+                ord = 0xFF; dj->lfn_idx = 0xFFFF;    /* Reset LFN sequence */
+                if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break;    /* SFN matched? */
+            }
+        }
+#else        /* Non LFN configuration */
+        if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */
+            break;
+#endif
+        res = dir_next(dj, 0);        /* Next entry */
+    } while (res == FR_OK);
+
+    return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read an object from the directory                                     */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+static
+FRESULT dir_read (
+    FATFS_DIR *dj            /* Pointer to the directory object that pointing the entry to be read */
+)
+{
+    FRESULT res;
+    BYTE c, *dir;
+#if _USE_LFN
+    BYTE a, ord = 0xFF, sum = 0xFF;
+#endif
+
+    res = FR_NO_FILE;
+    while (dj->sect) {
+        res = move_window(dj->fs, dj->sect);
+        if (res != FR_OK) break;
+        dir = dj->dir;                    /* Ptr to the directory entry of current index */
+        c = dir[DIR_Name];
+        if (c == 0) { res = FR_NO_FILE; break; }    /* Reached to end of table */
+#if _USE_LFN    /* LFN configuration */
+        a = dir[DIR_Attr] & AM_MASK;
+        if (c == DDE || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) {    /* An entry without valid data */
+            ord = 0xFF;
+        } else {
+            if (a == AM_LFN) {            /* An LFN entry is found */
+                if (c & LLE) {            /* Is it start of LFN sequence? */
+                    sum = dir[LDIR_Chksum];
+                    c &= ~LLE; ord = c;
+                    dj->lfn_idx = dj->index;
+                }
+                /* Check LFN validity and capture it */
+                ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+            } else {                    /* An SFN entry is found */
+                if (ord || sum != sum_sfn(dir))    /* Is there a valid LFN? */
+                    dj->lfn_idx = 0xFFFF;        /* It has no LFN. */
+                break;
+            }
+        }
+#else        /* Non LFN configuration */
+        if (c != DDE && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL))    /* Is it a valid entry? */
+            break;
+#endif
+        res = dir_next(dj, 0);                /* Next entry */
+        if (res != FR_OK) break;
+    }
+
+    if (res != FR_OK) dj->sect = 0;
+
+    return res;
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Register an object to the directory                                   */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT dir_register (    /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
+    FATFS_DIR *dj                /* Target directory with object name to be created */
+)
+{
+    FRESULT res;
+    BYTE c, *dir;
+#if _USE_LFN    /* LFN configuration */
+    WORD n, ne, is;
+    BYTE sn[12], *fn, sum;
+    WCHAR *lfn;
+
+
+    fn = dj->fn; lfn = dj->lfn;
+    mem_cpy(sn, fn, 12);
+
+    if (_FS_RPATH && (sn[NS] & NS_DOT))        /* Cannot create dot entry */
+        return FR_INVALID_NAME;
+
+    if (sn[NS] & NS_LOSS) {            /* When LFN is out of 8.3 format, generate a numbered name */
+        fn[NS] = 0; dj->lfn = 0;            /* Find only SFN */
+        for (n = 1; n < 100; n++) {
+            gen_numname(fn, sn, lfn, n);    /* Generate a numbered name */
+            res = dir_find(dj);                /* Check if the name collides with existing SFN */
+            if (res != FR_OK) break;
+        }
+        if (n == 100) return FR_DENIED;        /* Abort if too many collisions */
+        if (res != FR_NO_FILE) return res;    /* Abort if the result is other than 'not collided' */
+        fn[NS] = sn[NS]; dj->lfn = lfn;
+    }
+
+    if (sn[NS] & NS_LFN) {            /* When LFN is to be created, reserve an SFN + LFN entries. */
+        for (ne = 0; lfn[ne]; ne++) ;
+        ne = (ne + 25) / 13;
+    } else {                        /* Otherwise reserve only an SFN entry. */
+        ne = 1;
+    }
+
+    /* Reserve contiguous entries */
+    res = dir_sdi(dj, 0);
+    if (res != FR_OK) return res;
+    n = is = 0;
+    do {
+        res = move_window(dj->fs, dj->sect);
+        if (res != FR_OK) break;
+        c = *dj->dir;                /* Check the entry status */
+        if (c == DDE || c == 0) {    /* Is it a blank entry? */
+            if (n == 0) is = dj->index;    /* First index of the contiguous entry */
+            if (++n == ne) break;    /* A contiguous entry that required count is found */
+        } else {
+            n = 0;                    /* Not a blank entry. Restart to search */
+        }
+        res = dir_next(dj, 1);        /* Next entry with table stretch */
+    } while (res == FR_OK);
+
+    if (res == FR_OK && ne > 1) {    /* Initialize LFN entry if needed */
+        res = dir_sdi(dj, is);
+        if (res == FR_OK) {
+            sum = sum_sfn(dj->fn);    /* Sum of the SFN tied to the LFN */
+            ne--;
+            do {                    /* Store LFN entries in bottom first */
+                res = move_window(dj->fs, dj->sect);
+                if (res != FR_OK) break;
+                fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum);
+                dj->fs->wflag = 1;
+                res = dir_next(dj, 0);    /* Next entry */
+            } while (res == FR_OK && --ne);
+        }
+    }
+
+#else    /* Non LFN configuration */
+    res = dir_sdi(dj, 0);
+    if (res == FR_OK) {
+        do {    /* Find a blank entry for the SFN */
+            res = move_window(dj->fs, dj->sect);
+            if (res != FR_OK) break;
+            c = *dj->dir;
+            if (c == DDE || c == 0) break;    /* Is it a blank entry? */
+            res = dir_next(dj, 1);            /* Next entry with table stretch */
+        } while (res == FR_OK);
+    }
+#endif
+
+    if (res == FR_OK) {        /* Initialize the SFN entry */
+        res = move_window(dj->fs, dj->sect);
+        if (res == FR_OK) {
+            dir = dj->dir;
+            mem_set(dir, 0, SZ_DIR);    /* Clean the entry */
+            mem_cpy(dir, dj->fn, 11);    /* Put SFN */
+#if _USE_LFN
+            dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT);    /* Put NT flag */
+#endif
+            dj->fs->wflag = 1;
+        }
+    }
+
+    return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Remove an object from the directory                                   */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY && !_FS_MINIMIZE
+static
+FRESULT dir_remove (    /* FR_OK: Successful, FR_DISK_ERR: A disk error */
+    FATFS_DIR *dj                /* Directory object pointing the entry to be removed */
+)
+{
+    FRESULT res;
+#if _USE_LFN    /* LFN configuration */
+    WORD i;
+
+    i = dj->index;    /* SFN index */
+    res = dir_sdi(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx));    /* Goto the SFN or top of the LFN entries */
+    if (res == FR_OK) {
+        do {
+            res = move_window(dj->fs, dj->sect);
+            if (res != FR_OK) break;
+            *dj->dir = DDE;            /* Mark the entry "deleted" */
+            dj->fs->wflag = 1;
+            if (dj->index >= i) break;    /* When reached SFN, all entries of the object has been deleted. */
+            res = dir_next(dj, 0);        /* Next entry */
+        } while (res == FR_OK);
+        if (res == FR_NO_FILE) res = FR_INT_ERR;
+    }
+
+#else            /* Non LFN configuration */
+    res = dir_sdi(dj, dj->index);
+    if (res == FR_OK) {
+        res = move_window(dj->fs, dj->sect);
+        if (res == FR_OK) {
+            *dj->dir = DDE;            /* Mark the entry "deleted" */
+            dj->fs->wflag = 1;
+        }
+    }
+#endif
+
+    return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Pick a segment and create the object name in directory form           */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT create_name (
+    FATFS_DIR *dj,            /* Pointer to the directory object */
+    const TCHAR **path    /* Pointer to pointer to the segment in the path string */
+)
+{
+#ifdef _EXCVT
+    static const BYTE excvt[] = _EXCVT;    /* Upper conversion table for extended chars */
+#endif
+
+#if _USE_LFN    /* LFN configuration */
+    BYTE b, cf;
+    WCHAR w, *lfn;
+    UINT i, ni, si, di;
+    const TCHAR *p;
+
+    /* Create LFN in Unicode */
+    for (p = *path; *p == '/' || *p == '\\'; p++) ;    /* Strip duplicated separator */
+    lfn = dj->lfn;
+    si = di = 0;
+    for (;;) {
+        w = p[si++];                    /* Get a character */
+        if (w < ' ' || w == '/' || w == '\\') break;    /* Break on end of segment */
+        if (di >= _MAX_LFN)                /* Reject too long name */
+            return FR_INVALID_NAME;
+#if !_LFN_UNICODE
+        w &= 0xFF;
+        if (IsDBCS1(w)) {                /* Check if it is a DBC 1st byte (always false on SBCS cfg) */
+            b = (BYTE)p[si++];            /* Get 2nd byte */
+            if (!IsDBCS2(b))
+                return FR_INVALID_NAME;    /* Reject invalid sequence */
+            w = (w << 8) + b;            /* Create a DBC */
+        }
+        w = ff_convert(w, 1);            /* Convert ANSI/OEM to Unicode */
+        if (!w) return FR_INVALID_NAME;    /* Reject invalid code */
+#endif
+        if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */
+            return FR_INVALID_NAME;
+        lfn[di++] = w;                    /* Store the Unicode char */
+    }
+    *path = &p[si];                        /* Return pointer to the next segment */
+    cf = (w < ' ') ? NS_LAST : 0;        /* Set last segment flag if end of path */
+#if _FS_RPATH
+    if ((di == 1 && lfn[di-1] == '.') || /* Is this a dot entry? */
+        (di == 2 && lfn[di-1] == '.' && lfn[di-2] == '.')) {
+        lfn[di] = 0;
+        for (i = 0; i < 11; i++)
+            dj->fn[i] = (i < di) ? '.' : ' ';
+        dj->fn[i] = cf | NS_DOT;        /* This is a dot entry */
+        return FR_OK;
+    }
+#endif
+    while (di) {                        /* Strip trailing spaces and dots */
+        w = lfn[di-1];
+        if (w != ' ' && w != '.') break;
+        di--;
+    }
+    if (!di) return FR_INVALID_NAME;    /* Reject nul string */
+
+    lfn[di] = 0;                        /* LFN is created */
+
+    /* Create SFN in directory form */
+    mem_set(dj->fn, ' ', 11);
+    for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;    /* Strip leading spaces and dots */
+    if (si) cf |= NS_LOSS | NS_LFN;
+    while (di && lfn[di - 1] != '.') di--;    /* Find extension (di<=si: no extension) */
+
+    b = i = 0; ni = 8;
+    for (;;) {
+        w = lfn[si++];                    /* Get an LFN char */
+        if (!w) break;                    /* Break on end of the LFN */
+        if (w == ' ' || (w == '.' && si != di)) {    /* Remove spaces and dots */
+            cf |= NS_LOSS | NS_LFN; continue;
+        }
+
+        if (i >= ni || si == di) {        /* Extension or end of SFN */
+            if (ni == 11) {                /* Long extension */
+                cf |= NS_LOSS | NS_LFN; break;
+            }
+            if (si != di) cf |= NS_LOSS | NS_LFN;    /* Out of 8.3 format */
+            if (si > di) break;            /* No extension */
+            si = di; i = 8; ni = 11;    /* Enter extension section */
+            b <<= 2; continue;
+        }
+
+        if (w >= 0x80) {                /* Non ASCII char */
+#ifdef _EXCVT
+            w = ff_convert(w, 0);        /* Unicode -> OEM code */
+            if (w) w = excvt[w - 0x80];    /* Convert extended char to upper (SBCS) */
+#else
+            w = ff_convert(ff_wtoupper(w), 0);    /* Upper converted Unicode -> OEM code */
+#endif
+            cf |= NS_LFN;                /* Force create LFN entry */
+        }
+
+        if (_DF1S && w >= 0x100) {        /* Double byte char (always false on SBCS cfg) */
+            if (i >= ni - 1) {
+                cf |= NS_LOSS | NS_LFN; i = ni; continue;
+            }
+            dj->fn[i++] = (BYTE)(w >> 8);
+        } else {                        /* Single byte char */
+            if (!w || chk_chr("+,;=[]", w)) {    /* Replace illegal chars for SFN */
+                w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */
+            } else {
+                if (IsUpper(w)) {        /* ASCII large capital */
+                    b |= 2;
+                } else {
+                    if (IsLower(w)) {    /* ASCII small capital */
+                        b |= 1; w -= 0x20;
+                    }
+                }
+            }
+        }
+        dj->fn[i++] = (BYTE)w;
+    }
+
+    if (dj->fn[0] == DDE) dj->fn[0] = NDDE;    /* If the first char collides with deleted mark, replace it with 0x05 */
+
+    if (ni == 8) b <<= 2;
+    if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03)    /* Create LFN entry when there are composite capitals */
+        cf |= NS_LFN;
+    if (!(cf & NS_LFN)) {                        /* When LFN is in 8.3 format without extended char, NT flags are created */
+        if ((b & 0x03) == 0x01) cf |= NS_EXT;    /* NT flag (Extension has only small capital) */
+        if ((b & 0x0C) == 0x04) cf |= NS_BODY;    /* NT flag (Filename has only small capital) */
+    }
+
+    dj->fn[NS] = cf;    /* SFN is created */
+
+    return FR_OK;
+
+
+#else    /* Non-LFN configuration */
+    BYTE b, c, d, *sfn;
+    UINT ni, si, i;
+    const char *p;
+
+    /* Create file name in directory form */
+    for (p = *path; *p == '/' || *p == '\\'; p++) ;    /* Strip duplicated separator */
+    sfn = dj->fn;
+    mem_set(sfn, ' ', 11);
+    si = i = b = 0; ni = 8;
+#if _FS_RPATH
+    if (p[si] == '.') { /* Is this a dot entry? */
+        for (;;) {
+            c = (BYTE)p[si++];
+            if (c != '.' || si >= 3) break;
+            sfn[i++] = c;
+        }
+        if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
+        *path = &p[si];                                    /* Return pointer to the next segment */
+        sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT;    /* Set last segment flag if end of path */
+        return FR_OK;
+    }
+#endif
+    for (;;) {
+        c = (BYTE)p[si++];
+        if (c <= ' ' || c == '/' || c == '\\') break;    /* Break on end of segment */
+        if (c == '.' || i >= ni) {
+            if (ni != 8 || c != '.') return FR_INVALID_NAME;
+            i = 8; ni = 11;
+            b <<= 2; continue;
+        }
+        if (c >= 0x80) {                /* Extended char? */
+            b |= 3;                        /* Eliminate NT flag */
+#ifdef _EXCVT
+            c = excvt[c-0x80];            /* Upper conversion (SBCS) */
+#else
+#if !_DF1S    /* ASCII only cfg */
+            return FR_INVALID_NAME;
+#endif
+#endif
+        }
+        if (IsDBCS1(c)) {                /* Check if it is a DBC 1st byte (always false on SBCS cfg) */
+            d = (BYTE)p[si++];            /* Get 2nd byte */
+            if (!IsDBCS2(d) || i >= ni - 1)    /* Reject invalid DBC */
+                return FR_INVALID_NAME;
+            sfn[i++] = c;
+            sfn[i++] = d;
+        } else {                        /* Single byte code */
+            if (chk_chr("\"*+,:;<=>\?[]|\x7F", c))    /* Reject illegal chrs for SFN */
+                return FR_INVALID_NAME;
+            if (IsUpper(c)) {            /* ASCII large capital? */
+                b |= 2;
+            } else {
+                if (IsLower(c)) {        /* ASCII small capital? */
+                    b |= 1; c -= 0x20;
+                }
+            }
+            sfn[i++] = c;
+        }
+    }
+    *path = &p[si];                        /* Return pointer to the next segment */
+    c = (c <= ' ') ? NS_LAST : 0;        /* Set last segment flag if end of path */
+
+    if (!i) return FR_INVALID_NAME;        /* Reject nul string */
+    if (sfn[0] == DDE) sfn[0] = NDDE;    /* When first char collides with DDE, replace it with 0x05 */
+
+    if (ni == 8) b <<= 2;
+    if ((b & 0x03) == 0x01) c |= NS_EXT;    /* NT flag (Name extension has only small capital) */
+    if ((b & 0x0C) == 0x04) c |= NS_BODY;    /* NT flag (Name body has only small capital) */
+
+    sfn[NS] = c;        /* Store NT flag, File name is created */
+
+    return FR_OK;
+#endif
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get file information from directory entry                             */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+static
+void get_fileinfo (        /* No return code */
+    FATFS_DIR *dj,            /* Pointer to the directory object */
+    FILINFO *fno         /* Pointer to the file information to be filled */
+)
+{
+    UINT i;
+    BYTE nt, *dir;
+    TCHAR *p, c;
+
+
+    p = fno->fname;
+    if (dj->sect) {
+        dir = dj->dir;
+        nt = dir[DIR_NTres];        /* NT flag */
+        for (i = 0; i < 8; i++) {    /* Copy name body */
+            c = dir[i];
+            if (c == ' ') break;
+            if (c == NDDE) c = (TCHAR)DDE;
+            if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20;
+#if _LFN_UNICODE
+            if (IsDBCS1(c) && i < 7 && IsDBCS2(dir[i+1]))
+                c = (c << 8) | dir[++i];
+            c = ff_convert(c, 1);
+            if (!c) c = '?';
+#endif
+            *p++ = c;
+        }
+        if (dir[8] != ' ') {        /* Copy name extension */
+            *p++ = '.';
+            for (i = 8; i < 11; i++) {
+                c = dir[i];
+                if (c == ' ') break;
+                if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20;
+#if _LFN_UNICODE
+                if (IsDBCS1(c) && i < 10 && IsDBCS2(dir[i+1]))
+                    c = (c << 8) | dir[++i];
+                c = ff_convert(c, 1);
+                if (!c) c = '?';
+#endif
+                *p++ = c;
+            }
+        }
+        fno->fattrib = dir[DIR_Attr];                /* Attribute */
+        fno->fsize = LD_DWORD(dir+DIR_FileSize);    /* Size */
+        fno->fdate = LD_WORD(dir+DIR_WrtDate);        /* Date */
+        fno->ftime = LD_WORD(dir+DIR_WrtTime);        /* Time */
+    }
+    *p = 0;        /* Terminate SFN str by a \0 */
+
+#if _USE_LFN
+    if (fno->lfname && fno->lfsize) {
+        TCHAR *tp = fno->lfname;
+        WCHAR w, *lfn;
+
+        i = 0;
+        if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */
+            lfn = dj->lfn;
+            while ((w = *lfn++) != 0) {            /* Get an LFN char */
+#if !_LFN_UNICODE
+                w = ff_convert(w, 0);            /* Unicode -> OEM conversion */
+                if (!w) { i = 0; break; }        /* Could not convert, no LFN */
+                if (_DF1S && w >= 0x100)        /* Put 1st byte if it is a DBC (always false on SBCS cfg) */
+                    tp[i++] = (TCHAR)(w >> 8);
+#endif
+                if (i >= fno->lfsize - 1) { i = 0; break; }    /* Buffer overflow, no LFN */
+                tp[i++] = (TCHAR)w;
+            }
+        }
+        tp[i] = 0;    /* Terminate the LFN str by a \0 */
+    }
+#endif
+}
+#endif /* _FS_MINIMIZE <= 1 */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Follow a file path                                                    */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT follow_path (    /* FR_OK(0): successful, !=0: error code */
+    FATFS_DIR *dj,            /* Directory object to return last directory and found object */
+    const TCHAR *path    /* Full-path string to find a file or directory */
+)
+{
+    FRESULT res;
+    BYTE *dir, ns;
+
+
+#if _FS_RPATH
+    if (*path == '/' || *path == '\\') { /* There is a heading separator */
+        path++;    dj->sclust = 0;        /* Strip it and start from the root dir */
+    } else {                            /* No heading separator */
+        dj->sclust = dj->fs->cdir;    /* Start from the current dir */
+    }
+#else
+    if (*path == '/' || *path == '\\')    /* Strip heading separator if exist */
+        path++;
+    dj->sclust = 0;                        /* Start from the root dir */
+#endif
+
+    if ((UINT)*path < ' ') {            /* Nul path means the start directory itself */
+        res = dir_sdi(dj, 0);
+        dj->dir = 0;
+
+    } else {                            /* Follow path */
+        for (;;) {
+            res = create_name(dj, &path);    /* Get a segment */
+            if (res != FR_OK) break;
+            res = dir_find(dj);                /* Find it */
+            ns = *(dj->fn+NS);
+            if (res != FR_OK) {                /* Failed to find the object */
+                if (res != FR_NO_FILE) break;    /* Abort if any hard error occured */
+                /* Object not found */
+                if (_FS_RPATH && (ns & NS_DOT)) {    /* If dot entry is not exit */
+                    dj->sclust = 0; dj->dir = 0;    /* It is the root dir */
+                    res = FR_OK;
+                    if (!(ns & NS_LAST)) continue;
+                } else {                            /* Could not find the object */
+                    if (!(ns & NS_LAST)) res = FR_NO_PATH;
+                }
+                break;
+            }
+            if (ns & NS_LAST) break;            /* Last segment match. Function completed. */
+            dir = dj->dir;                        /* There is next segment. Follow the sub directory */
+            if (!(dir[DIR_Attr] & AM_DIR)) {    /* Cannot follow because it is a file */
+                res = FR_NO_PATH; break;
+            }
+            dj->sclust = LD_CLUST(dir);
+        }
+    }
+
+    return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load a sector and check if it is an FAT Volume Boot Record            */
+/*-----------------------------------------------------------------------*/
+
+static
+BYTE check_fs (    /* 0:FAT-VBR, 1:Valid BR but not FAT, 2:Not a BR, 3:Disk error */
+    FATFS *fs,    /* File system object */
+    DWORD sect    /* Sector# (lba) to check if it is an FAT boot record or not */
+)
+{
+    if (disk_read(fs->drv, fs->win, sect, 1) != RES_OK)    /* Load boot record */
+        return 3;
+    if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)        /* Check record signature (always placed at offset 510 even if the sector size is >512) */
+        return 2;
+
+    if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146)    /* Check "FAT" string */
+        return 0;
+    if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)
+        return 0;
+
+    return 1;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Check if the file system object is valid or not                       */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT chk_mounted (    /* FR_OK(0): successful, !=0: any error occurred */
+    const TCHAR **path,    /* Pointer to pointer to the path name (drive number) */
+    FATFS **rfs,        /* Pointer to pointer to the found file system object */
+    BYTE chk_wp            /* !=0: Check media write protection for write access */
+)
+{
+    BYTE fmt, b, pi, *tbl;
+    UINT vol;
+    DSTATUS stat;
+    DWORD bsect, fasize, tsect, sysect, nclst, szbfat;
+    WORD nrsv;
+    const TCHAR *p = *path;
+    FATFS *fs;
+
+    /* Get logical drive number from the path name */
+    vol = p[0] - '0';                    /* Is there a drive number? */
+    if (vol <= 9 && p[1] == ':') {        /* Found a drive number, get and strip it */
+        p += 2; *path = p;                /* Return pointer to the path name */
+    } else {                            /* No drive number is given */
+#if _FS_RPATH
+        vol = CurrVol;                    /* Use current drive */
+#else
+        vol = 0;                        /* Use drive 0 */
+#endif
+    }
+
+    /* Check if the file system object is valid or not */
+    if (vol >= _VOLUMES)                 /* Is the drive number valid? */
+        return FR_INVALID_DRIVE;
+    *rfs = fs = FatFs[vol];                /* Return pointer to the corresponding file system object */
+    if (!fs) return FR_NOT_ENABLED;        /* Is the file system object available? */
+
+    ENTER_FF(fs);                        /* Lock file system */
+
+    if (fs->fs_type) {                    /* If the logical drive has been mounted */
+        stat = disk_status(fs->drv);
+        if (!(stat & STA_NOINIT)) {        /* and the physical drive is kept initialized (has not been changed), */
+            if (!_FS_READONLY && chk_wp && (stat & STA_PROTECT))    /* Check write protection if needed */
+                return FR_WRITE_PROTECTED;
+            return FR_OK;                /* The file system object is valid */
+        }
+    }
+
+    /* The file system object is not valid. */
+    /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */
+
+    fs->fs_type = 0;                    /* Clear the file system object */
+    fs->drv = LD2PD(vol);                /* Bind the logical drive and a physical drive */
+    stat = disk_initialize(fs->drv);    /* Initialize low level disk I/O layer */
+    if (stat & STA_NOINIT)                /* Check if the initialization succeeded */
+        return FR_NOT_READY;            /* Failed to initialize due to no media or hard error */
+    if (!_FS_READONLY && chk_wp && (stat & STA_PROTECT))    /* Check disk write protection if needed */
+        return FR_WRITE_PROTECTED;
+#if _MAX_SS != 512                        /* Get disk sector size (variable sector size cfg only) */
+    if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &fs->ssize) != RES_OK)
+        return FR_DISK_ERR;
+#endif
+    /* Search FAT partition on the drive. Supports only generic partitionings, FDISK and SFD. */
+    fmt = check_fs(fs, bsect = 0);        /* Load sector 0 and check if it is an FAT-VBR (in SFD) */
+    if (LD2PT(vol) && !fmt) fmt = 1;    /* Force non-SFD if the volume is forced partition */
+    if (fmt == 1) {                        /* Not an FAT-VBR, the physical drive can be partitioned */
+        /* Check the partition listed in the partition table */
+        pi = LD2PT(vol);
+        if (pi) pi--;
+        tbl = &fs->win[MBR_Table + pi * SZ_PTE];/* Partition table */
+        if (tbl[4]) {                        /* Is the partition existing? */
+            bsect = LD_DWORD(&tbl[8]);        /* Partition offset in LBA */
+            fmt = check_fs(fs, bsect);        /* Check the partition */
+        }
+    }
+    if (fmt == 3) return FR_DISK_ERR;
+    if (fmt) return FR_NO_FILESYSTEM;        /* No FAT volume is found */
+
+    /* An FAT volume is found. Following code initializes the file system object */
+
+    if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs))        /* (BPB_BytsPerSec must be equal to the physical sector size) */
+        return FR_NO_FILESYSTEM;
+
+    fasize = LD_WORD(fs->win+BPB_FATSz16);                /* Number of sectors per FAT */
+    if (!fasize) fasize = LD_DWORD(fs->win+BPB_FATSz32);
+    fs->fsize = fasize;
+
+    fs->n_fats = b = fs->win[BPB_NumFATs];                /* Number of FAT copies */
+    if (b != 1 && b != 2) return FR_NO_FILESYSTEM;        /* (Must be 1 or 2) */
+    fasize *= b;                                        /* Number of sectors for FAT area */
+
+    fs->csize = b = fs->win[BPB_SecPerClus];            /* Number of sectors per cluster */
+    if (!b || (b & (b - 1))) return FR_NO_FILESYSTEM;    /* (Must be power of 2) */
+
+    fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt);    /* Number of root directory entries */
+    if (fs->n_rootdir % (SS(fs) / SZ_DIR)) return FR_NO_FILESYSTEM;    /* (BPB_RootEntCnt must be sector aligned) */
+
+    tsect = LD_WORD(fs->win+BPB_TotSec16);                /* Number of sectors on the volume */
+    if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);
+
+    nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt);                /* Number of reserved sectors */
+    if (!nrsv) return FR_NO_FILESYSTEM;                    /* (BPB_RsvdSecCnt must not be 0) */
+
+    /* Determine the FAT sub type */
+    sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIR);    /* RSV+FAT+FATFS_DIR */
+    if (tsect < sysect) return FR_NO_FILESYSTEM;        /* (Invalid volume size) */
+    nclst = (tsect - sysect) / fs->csize;                /* Number of clusters */
+    if (!nclst) return FR_NO_FILESYSTEM;                /* (Invalid volume size) */
+    fmt = FS_FAT12;
+    if (nclst >= MIN_FAT16) fmt = FS_FAT16;
+    if (nclst >= MIN_FAT32) fmt = FS_FAT32;
+
+    /* Boundaries and Limits */
+    fs->n_fatent = nclst + 2;                            /* Number of FAT entries */
+    fs->database = bsect + sysect;                        /* Data start sector */
+    fs->fatbase = bsect + nrsv;                         /* FAT start sector */
+    if (fmt == FS_FAT32) {
+        if (fs->n_rootdir) return FR_NO_FILESYSTEM;        /* (BPB_RootEntCnt must be 0) */
+        fs->dirbase = LD_DWORD(fs->win+BPB_RootClus);    /* Root directory start cluster */
+        szbfat = fs->n_fatent * 4;                        /* (Required FAT size) */
+    } else {
+        if (!fs->n_rootdir)    return FR_NO_FILESYSTEM;    /* (BPB_RootEntCnt must not be 0) */
+        fs->dirbase = fs->fatbase + fasize;                /* Root directory start sector */
+        szbfat = (fmt == FS_FAT16) ?                    /* (Required FAT size) */
+            fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
+    }
+    if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs))    /* (BPB_FATSz must not be less than required) */
+        return FR_NO_FILESYSTEM;
+
+#if !_FS_READONLY
+    /* Initialize cluster allocation information */
+    fs->free_clust = 0xFFFFFFFF;
+    fs->last_clust = 0;
+
+    /* Get fsinfo if available */
+    if (fmt == FS_FAT32) {
+         fs->fsi_flag = 0;
+        fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo);
+        if (disk_read(fs->drv, fs->win, fs->fsi_sector, 1) == RES_OK &&
+            LD_WORD(fs->win+BS_55AA) == 0xAA55 &&
+            LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 &&
+            LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) {
+                fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free);
+                fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count);
+        }
+    }
+#endif
+    fs->fs_type = fmt;        /* FAT sub-type */
+    fs->id = ++Fsid;        /* File system mount ID */
+    fs->winsect = 0;        /* Invalidate sector cache */
+    fs->wflag = 0;
+#if _FS_RPATH
+    fs->cdir = 0;            /* Current directory (root dir) */
+#endif
+#if _FS_SHARE                /* Clear file lock semaphores */
+    clear_lock(fs);
+#endif
+
+    return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Check if the file/dir object is valid or not                          */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT validate (    /* FR_OK(0): The object is valid, !=0: Invalid */
+    FATFS *fs,        /* Pointer to the file system object */
+    WORD id            /* Member id of the target object to be checked */
+)
+{
+    if (!fs || !fs->fs_type || fs->id != id)
+        return FR_INVALID_OBJECT;
+
+    ENTER_FF(fs);        /* Lock file system */
+
+    if (disk_status(fs->drv) & STA_NOINIT)
+        return FR_NOT_READY;
+
+    return FR_OK;
+}
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Public Functions
+
+--------------------------------------------------------------------------*/
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Mount/Unmount a Logical Drive                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mount (
+    BYTE vol,        /* Logical drive number to be mounted/unmounted */
+    FATFS *fs        /* Pointer to new file system object (NULL for unmount)*/
+)
+{
+    FATFS *rfs;
+
+
+    if (vol >= _VOLUMES)        /* Check if the drive number is valid */
+        return FR_INVALID_DRIVE;
+    rfs = FatFs[vol];            /* Get current fs object */
+
+    if (rfs) {
+#if _FS_SHARE
+        clear_lock(rfs);
+#endif
+#if _FS_REENTRANT                /* Discard sync object of the current volume */
+        if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR;
+#endif
+        rfs->fs_type = 0;        /* Clear old fs object */
+    }
+
+    if (fs) {
+        fs->fs_type = 0;        /* Clear new fs object */
+#if _FS_REENTRANT                /* Create sync object for the new volume */
+        if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR;
+#endif
+    }
+    FatFs[vol] = fs;            /* Register new fs object */
+
+    return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Open or Create a File                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_open (
+    FIL *fp,            /* Pointer to the blank file object */
+    const TCHAR *path,    /* Pointer to the file name */
+    BYTE mode            /* Access mode and file open mode flags */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj;
+    BYTE *dir;
+    DEF_NAMEBUF;
+
+
+    fp->fs = 0;            /* Clear file object */
+
+#if !_FS_READONLY
+    mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW;
+    res = chk_mounted(&path, &dj.fs, (BYTE)(mode & ~FA_READ));
+#else
+    mode &= FA_READ;
+    res = chk_mounted(&path, &dj.fs, 0);
+#endif
+    INIT_BUF(dj);
+    if (res == FR_OK)
+        res = follow_path(&dj, path);    /* Follow the file path */
+    dir = dj.dir;
+
+#if !_FS_READONLY    /* R/W configuration */
+    if (res == FR_OK) {
+        if (!dir)    /* Current dir itself */
+            res = FR_INVALID_NAME;
+#if _FS_SHARE
+        else
+            res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
+#endif
+    }
+    /* Create or Open a file */
+    if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
+        DWORD dw, cl;
+
+        if (res != FR_OK) {                    /* No file, create new */
+            if (res == FR_NO_FILE)            /* There is no file to open, create a new entry */
+#if _FS_SHARE
+                res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;
+#else
+                res = dir_register(&dj);
+#endif
+            mode |= FA_CREATE_ALWAYS;        /* File is created */
+            dir = dj.dir;                    /* New entry */
+        }
+        else {                                /* Any object is already existing */
+            if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) {    /* Cannot overwrite it (R/O or FATFS_DIR) */
+                res = FR_DENIED;
+            } else {
+                if (mode & FA_CREATE_NEW)    /* Cannot create as new file */
+                    res = FR_EXIST;
+            }
+        }
+        if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) {    /* Truncate it if overwrite mode */
+            dw = get_fattime();                    /* Created time */
+            ST_DWORD(dir+DIR_CrtTime, dw);
+            dir[DIR_Attr] = 0;                    /* Reset attribute */
+            ST_DWORD(dir+DIR_FileSize, 0);        /* size = 0 */
+            cl = LD_CLUST(dir);                    /* Get start cluster */
+            ST_CLUST(dir, 0);                    /* cluster = 0 */
+            dj.fs->wflag = 1;
+            if (cl) {                            /* Remove the cluster chain if exist */
+                dw = dj.fs->winsect;
+                res = remove_chain(dj.fs, cl);
+                if (res == FR_OK) {
+                    dj.fs->last_clust = cl - 1;    /* Reuse the cluster hole */
+                    res = move_window(dj.fs, dw);
+                }
+            }
+        }
+    }
+    else {    /* Open an existing file */
+        if (res == FR_OK) {                        /* Follow succeeded */
+            if (dir[DIR_Attr] & AM_DIR) {        /* It is a directory */
+                res = FR_NO_FILE;
+            } else {
+                if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
+                    res = FR_DENIED;
+            }
+        }
+    }
+    if (res == FR_OK) {
+        if (mode & FA_CREATE_ALWAYS)            /* Set file change flag if created or overwritten */
+            mode |= FA__WRITTEN;
+        fp->dir_sect = dj.fs->winsect;            /* Pointer to the directory entry */
+        fp->dir_ptr = dir;
+#if _FS_SHARE
+        fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
+        if (!fp->lockid) res = FR_INT_ERR;
+#endif
+    }
+
+#else                /* R/O configuration */
+    if (res == FR_OK) {                    /* Follow succeeded */
+        if (!dir) {                        /* Current dir itself */
+            res = FR_INVALID_NAME;
+        } else {
+            if (dir[DIR_Attr] & AM_DIR)    /* It is a directory */
+                res = FR_NO_FILE;
+        }
+    }
+#endif
+    FREE_BUF();
+
+    if (res == FR_OK) {
+        fp->flag = mode;                    /* File access mode */
+        fp->sclust = LD_CLUST(dir);            /* File start cluster */
+        fp->fsize = LD_DWORD(dir+DIR_FileSize);    /* File size */
+        fp->fptr = 0;                        /* File pointer */
+        fp->dsect = 0;
+#if _USE_FASTSEEK
+        fp->cltbl = 0;                        /* Normal seek mode */
+#endif
+        fp->fs = dj.fs; fp->id = dj.fs->id;    /* Validate file object */
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read File                                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_read (
+    FIL *fp,         /* Pointer to the file object */
+    void *buff,        /* Pointer to data buffer */
+    UINT btr,        /* Number of bytes to read */
+    UINT *br        /* Pointer to number of bytes read */
+)
+{
+    FRESULT res;
+    DWORD clst, sect, remain;
+    UINT rcnt, cc;
+    BYTE csect, *rbuff = (BYTE*)buff;
+
+
+    *br = 0;    /* Initialize byte counter */
+
+    res = validate(fp->fs, fp->id);                /* Check validity */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                    /* Aborted file? */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_READ))                     /* Check access mode */
+        LEAVE_FF(fp->fs, FR_DENIED);
+    remain = fp->fsize - fp->fptr;
+    if (btr > remain) btr = (UINT)remain;        /* Truncate btr by remaining bytes */
+
+    for ( ;  btr;                                /* Repeat until all data read */
+        rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
+        if ((fp->fptr % SS(fp->fs)) == 0) {        /* On the sector boundary? */
+            csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));    /* Sector offset in the cluster */
+            if (!csect) {                        /* On the cluster boundary? */
+                if (fp->fptr == 0) {            /* On the top of the file? */
+                    clst = fp->sclust;            /* Follow from the origin */
+                } else {                        /* Middle or end of the file */
+#if _USE_FASTSEEK
+                    if (fp->cltbl)
+                        clst = clmt_clust(fp, fp->fptr);    /* Get cluster# from the CLMT */
+                    else
+#endif
+                        clst = get_fat(fp->fs, fp->clust);    /* Follow cluster chain on the FAT */
+                }
+                if (clst < 2) ABORT(fp->fs, FR_INT_ERR);
+                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                fp->clust = clst;                /* Update current cluster */
+            }
+            sect = clust2sect(fp->fs, fp->clust);    /* Get current sector */
+            if (!sect) ABORT(fp->fs, FR_INT_ERR);
+            sect += csect;
+            cc = btr / SS(fp->fs);                /* When remaining bytes >= sector size, */
+            if (cc) {                            /* Read maximum contiguous sectors directly */
+                if (csect + cc > fp->fs->csize)    /* Clip at cluster boundary */
+                    cc = fp->fs->csize - csect;
+                if (disk_read(fp->fs->drv, rbuff, sect, (BYTE)cc) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+#if !_FS_READONLY && _FS_MINIMIZE <= 2            /* Replace one of the read sectors with cached data if it contains a dirty sector */
+#if _FS_TINY
+                if (fp->fs->wflag && fp->fs->winsect - sect < cc)
+                    mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs));
+#else
+                if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc)
+                    mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs));
+#endif
+#endif
+                rcnt = SS(fp->fs) * cc;            /* Number of bytes transferred */
+                continue;
+            }
+#if !_FS_TINY
+            if (fp->dsect != sect) {            /* Load data sector if not in cache */
+#if !_FS_READONLY
+                if (fp->flag & FA__DIRTY) {        /* Write-back dirty sector cache */
+                    if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+                        ABORT(fp->fs, FR_DISK_ERR);
+                    fp->flag &= ~FA__DIRTY;
+                }
+#endif
+                if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK)    /* Fill sector cache */
+                    ABORT(fp->fs, FR_DISK_ERR);
+            }
+#endif
+            fp->dsect = sect;
+        }
+        rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));    /* Get partial sector data from sector buffer */
+        if (rcnt > btr) rcnt = btr;
+#if _FS_TINY
+        if (move_window(fp->fs, fp->dsect))        /* Move sector window */
+            ABORT(fp->fs, FR_DISK_ERR);
+        mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt);    /* Pick partial sector */
+#else
+        mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt);    /* Pick partial sector */
+#endif
+    }
+
+    LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Write File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write (
+    FIL *fp,            /* Pointer to the file object */
+    const void *buff,    /* Pointer to the data to be written */
+    UINT btw,            /* Number of bytes to write */
+    UINT *bw            /* Pointer to number of bytes written */
+)
+{
+    FRESULT res;
+    DWORD clst, sect;
+    UINT wcnt, cc;
+    const BYTE *wbuff = (const BYTE*)buff;
+    BYTE csect;
+
+
+    *bw = 0;    /* Initialize byte counter */
+
+    res = validate(fp->fs, fp->id);            /* Check validity */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                /* Aborted file? */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_WRITE))                /* Check access mode */
+        LEAVE_FF(fp->fs, FR_DENIED);
+    if ((DWORD)(fp->fsize + btw) < fp->fsize) btw = 0;    /* File size cannot reach 4GB */
+
+    for ( ;  btw;                            /* Repeat until all data written */
+        wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
+        if ((fp->fptr % SS(fp->fs)) == 0) {    /* On the sector boundary? */
+            csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));    /* Sector offset in the cluster */
+            if (!csect) {                    /* On the cluster boundary? */
+                if (fp->fptr == 0) {        /* On the top of the file? */
+                    clst = fp->sclust;        /* Follow from the origin */
+                    if (clst == 0)            /* When no cluster is allocated, */
+                        fp->sclust = clst = create_chain(fp->fs, 0);    /* Create a new cluster chain */
+                } else {                    /* Middle or end of the file */
+#if _USE_FASTSEEK
+                    if (fp->cltbl)
+                        clst = clmt_clust(fp, fp->fptr);    /* Get cluster# from the CLMT */
+                    else
+#endif
+                        clst = create_chain(fp->fs, fp->clust);    /* Follow or stretch cluster chain on the FAT */
+                }
+                if (clst == 0) break;        /* Could not allocate a new cluster (disk full) */
+                if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                fp->clust = clst;            /* Update current cluster */
+            }
+#if _FS_TINY
+            if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0))    /* Write-back sector cache */
+                ABORT(fp->fs, FR_DISK_ERR);
+#else
+            if (fp->flag & FA__DIRTY) {        /* Write-back sector cache */
+                if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+                fp->flag &= ~FA__DIRTY;
+            }
+#endif
+            sect = clust2sect(fp->fs, fp->clust);    /* Get current sector */
+            if (!sect) ABORT(fp->fs, FR_INT_ERR);
+            sect += csect;
+            cc = btw / SS(fp->fs);            /* When remaining bytes >= sector size, */
+            if (cc) {                        /* Write maximum contiguous sectors directly */
+                if (csect + cc > fp->fs->csize)    /* Clip at cluster boundary */
+                    cc = fp->fs->csize - csect;
+                if (disk_write(fp->fs->drv, wbuff, sect, (BYTE)cc) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+#if _FS_TINY
+                if (fp->fs->winsect - sect < cc) {    /* Refill sector cache if it gets invalidated by the direct write */
+                    mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs));
+                    fp->fs->wflag = 0;
+                }
+#else
+                if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
+                    mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs));
+                    fp->flag &= ~FA__DIRTY;
+                }
+#endif
+                wcnt = SS(fp->fs) * cc;        /* Number of bytes transferred */
+                continue;
+            }
+#if _FS_TINY
+            if (fp->fptr >= fp->fsize) {    /* Avoid silly cache filling at growing edge */
+                if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR);
+                fp->fs->winsect = sect;
+            }
+#else
+            if (fp->dsect != sect) {        /* Fill sector cache with file data */
+                if (fp->fptr < fp->fsize &&
+                    disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK)
+                        ABORT(fp->fs, FR_DISK_ERR);
+            }
+#endif
+            fp->dsect = sect;
+        }
+        wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */
+        if (wcnt > btw) wcnt = btw;
+#if _FS_TINY
+        if (move_window(fp->fs, fp->dsect))    /* Move sector window */
+            ABORT(fp->fs, FR_DISK_ERR);
+        mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt);    /* Fit partial sector */
+        fp->fs->wflag = 1;
+#else
+        mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt);    /* Fit partial sector */
+        fp->flag |= FA__DIRTY;
+#endif
+    }
+
+    if (fp->fptr > fp->fsize) fp->fsize = fp->fptr;    /* Update file size if needed */
+    fp->flag |= FA__WRITTEN;                        /* Set file change flag */
+
+    LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize the File Object                                           */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_sync (
+    FIL *fp        /* Pointer to the file object */
+)
+{
+    FRESULT res;
+    DWORD tim;
+    BYTE *dir;
+
+
+    res = validate(fp->fs, fp->id);        /* Check validity of the object */
+    if (res == FR_OK) {
+        if (fp->flag & FA__WRITTEN) {    /* Has the file been written? */
+#if !_FS_TINY    /* Write-back dirty buffer */
+            if (fp->flag & FA__DIRTY) {
+                if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+                    LEAVE_FF(fp->fs, FR_DISK_ERR);
+                fp->flag &= ~FA__DIRTY;
+            }
+#endif
+            /* Update the directory entry */
+            res = move_window(fp->fs, fp->dir_sect);
+            if (res == FR_OK) {
+                dir = fp->dir_ptr;
+                dir[DIR_Attr] |= AM_ARC;                    /* Set archive bit */
+                ST_DWORD(dir+DIR_FileSize, fp->fsize);        /* Update file size */
+                ST_CLUST(dir, fp->sclust);                    /* Update start cluster */
+                tim = get_fattime();                        /* Update updated time */
+                ST_DWORD(dir+DIR_WrtTime, tim);
+                fp->flag &= ~FA__WRITTEN;
+                fp->fs->wflag = 1;
+                res = sync(fp->fs);
+            }
+        }
+    }
+
+    LEAVE_FF(fp->fs, res);
+}
+
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_close (
+    FIL *fp        /* Pointer to the file object to be closed */
+)
+{
+    FRESULT res;
+
+#if _FS_READONLY
+    FATFS *fs = fp->fs;
+    res = validate(fs, fp->id);
+    if (res == FR_OK) fp->fs = 0;    /* Discard file object */
+    LEAVE_FF(fs, res);
+
+#else
+    res = f_sync(fp);        /* Flush cached data */
+#if _FS_SHARE
+    if (res == FR_OK) {        /* Decrement open counter */
+#if _FS_REENTRANT
+        res = validate(fp->fs, fp->id);
+        if (res == FR_OK) {
+            res = dec_lock(fp->lockid);    
+            unlock_fs(fp->fs, FR_OK);
+        }
+#else
+        res = dec_lock(fp->lockid);
+#endif
+    }
+#endif
+    if (res == FR_OK) fp->fs = 0;    /* Discard file object */
+    return res;
+#endif
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Current Drive/Directory Handlings                                     */
+/*-----------------------------------------------------------------------*/
+
+#if _FS_RPATH >= 1
+
+FRESULT f_chdrive (
+    BYTE drv        /* Drive number */
+)
+{
+    if (drv >= _VOLUMES) return FR_INVALID_DRIVE;
+
+    CurrVol = drv;
+
+    return FR_OK;
+}
+
+
+
+FRESULT f_chdir (
+    const TCHAR *path    /* Pointer to the directory path */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj.fs, 0);
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);        /* Follow the path */
+        FREE_BUF();
+        if (res == FR_OK) {                    /* Follow completed */
+            if (!dj.dir) {
+                dj.fs->cdir = dj.sclust;    /* Start directory itself */
+            } else {
+                if (dj.dir[DIR_Attr] & AM_DIR)    /* Reached to the directory */
+                    dj.fs->cdir = LD_CLUST(dj.dir);
+                else
+                    res = FR_NO_PATH;        /* Reached but a file */
+            }
+        }
+        if (res == FR_NO_FILE) res = FR_NO_PATH;
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+#if _FS_RPATH >= 2
+FRESULT f_getcwd (
+    TCHAR *path,    /* Pointer to the directory path */
+    UINT sz_path    /* Size of path */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj;
+    UINT i, n;
+    DWORD ccl;
+    TCHAR *tp;
+    FILINFO fno;
+    DEF_NAMEBUF;
+
+
+    *path = 0;
+    res = chk_mounted((const TCHAR**)&path, &dj.fs, 0);    /* Get current volume */
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        i = sz_path;        /* Bottom of buffer (dir stack base) */
+        dj.sclust = dj.fs->cdir;            /* Start to follow upper dir from current dir */
+        while ((ccl = dj.sclust) != 0) {    /* Repeat while current dir is a sub-dir */
+            res = dir_sdi(&dj, 1);            /* Get parent dir */
+            if (res != FR_OK) break;
+            res = dir_read(&dj);
+            if (res != FR_OK) break;
+            dj.sclust = LD_CLUST(dj.dir);    /* Goto parent dir */
+            res = dir_sdi(&dj, 0);
+            if (res != FR_OK) break;
+            do {                            /* Find the entry links to the child dir */
+                res = dir_read(&dj);
+                if (res != FR_OK) break;
+                if (ccl == LD_CLUST(dj.dir)) break;    /* Found the entry */
+                res = dir_next(&dj, 0);    
+            } while (res == FR_OK);
+            if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */
+            if (res != FR_OK) break;
+#if _USE_LFN
+            fno.lfname = path;
+            fno.lfsize = i;
+#endif
+            get_fileinfo(&dj, &fno);        /* Get the dir name and push it to the buffer */
+            tp = fno.fname;
+            if (_USE_LFN && *path) tp = path;
+            for (n = 0; tp[n]; n++) ;
+            if (i < n + 3) {
+                res = FR_NOT_ENOUGH_CORE; break;
+            }
+            while (n) path[--i] = tp[--n];
+            path[--i] = '/';
+        }
+        tp = path;
+        if (res == FR_OK) {
+            *tp++ = '0' + CurrVol;            /* Put drive number */
+            *tp++ = ':';
+            if (i == sz_path) {                /* Root-dir */
+                *tp++ = '/';
+            } else {                        /* Sub-dir */
+                do        /* Add stacked path str */
+                    *tp++ = path[i++];
+                while (i < sz_path);
+            }
+        }
+        *tp = 0;
+        FREE_BUF();
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+#endif /* _FS_RPATH >= 2 */
+#endif /* _FS_RPATH >= 1 */
+
+
+
+#if _FS_MINIMIZE <= 2
+/*-----------------------------------------------------------------------*/
+/* Seek File R/W Pointer                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_lseek (
+    FIL *fp,        /* Pointer to the file object */
+    DWORD ofs        /* File pointer from top of file */
+)
+{
+    FRESULT res;
+
+
+    res = validate(fp->fs, fp->id);        /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)            /* Check abort flag */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+
+#if _USE_FASTSEEK
+    if (fp->cltbl) {    /* Fast seek */
+        DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;
+
+        if (ofs == CREATE_LINKMAP) {    /* Create CLMT */
+            tbl = fp->cltbl;
+            tlen = *tbl++; ulen = 2;    /* Given table size and required table size */
+            cl = fp->sclust;            /* Top of the chain */
+            if (cl) {
+                do {
+                    /* Get a fragment */
+                    tcl = cl; ncl = 0; ulen += 2;    /* Top, length and used items */
+                    do {
+                        pcl = cl; ncl++;
+                        cl = get_fat(fp->fs, cl);
+                        if (cl <= 1) ABORT(fp->fs, FR_INT_ERR);
+                        if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                    } while (cl == pcl + 1);
+                    if (ulen <= tlen) {        /* Store the length and top of the fragment */
+                        *tbl++ = ncl; *tbl++ = tcl;
+                    }
+                } while (cl < fp->fs->n_fatent);    /* Repeat until end of chain */
+            }
+            *fp->cltbl = ulen;    /* Number of items used */
+            if (ulen <= tlen)
+                *tbl = 0;        /* Terminate table */
+            else
+                res = FR_NOT_ENOUGH_CORE;    /* Given table size is smaller than required */
+
+        } else {                        /* Fast seek */
+            if (ofs > fp->fsize)        /* Clip offset at the file size */
+                ofs = fp->fsize;
+            fp->fptr = ofs;                /* Set file pointer */
+            if (ofs) {
+                fp->clust = clmt_clust(fp, ofs - 1);
+                dsc = clust2sect(fp->fs, fp->clust);
+                if (!dsc) ABORT(fp->fs, FR_INT_ERR);
+                dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1);
+                if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) {    /* Refill sector cache if needed */
+#if !_FS_TINY
+#if !_FS_READONLY
+                    if (fp->flag & FA__DIRTY) {        /* Write-back dirty sector cache */
+                        if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+                            ABORT(fp->fs, FR_DISK_ERR);
+                        fp->flag &= ~FA__DIRTY;
+                    }
+#endif
+                    if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK)    /* Load current sector */
+                        ABORT(fp->fs, FR_DISK_ERR);
+#endif
+                    fp->dsect = dsc;
+                }
+            }
+        }
+    } else
+#endif
+
+    /* Normal Seek */
+    {
+        DWORD clst, bcs, nsect, ifptr;
+
+        if (ofs > fp->fsize                    /* In read-only mode, clip offset with the file size */
+#if !_FS_READONLY
+             && !(fp->flag & FA_WRITE)
+#endif
+            ) ofs = fp->fsize;
+
+        ifptr = fp->fptr;
+        fp->fptr = nsect = 0;
+        if (ofs) {
+            bcs = (DWORD)fp->fs->csize * SS(fp->fs);    /* Cluster size (byte) */
+            if (ifptr > 0 &&
+                (ofs - 1) / bcs >= (ifptr - 1) / bcs) {    /* When seek to same or following cluster, */
+                fp->fptr = (ifptr - 1) & ~(bcs - 1);    /* start from the current cluster */
+                ofs -= fp->fptr;
+                clst = fp->clust;
+            } else {                                    /* When seek to back cluster, */
+                clst = fp->sclust;                        /* start from the first cluster */
+#if !_FS_READONLY
+                if (clst == 0) {                        /* If no cluster chain, create a new chain */
+                    clst = create_chain(fp->fs, 0);
+                    if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+                    if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                    fp->sclust = clst;
+                }
+#endif
+                fp->clust = clst;
+            }
+            if (clst != 0) {
+                while (ofs > bcs) {                        /* Cluster following loop */
+#if !_FS_READONLY
+                    if (fp->flag & FA_WRITE) {            /* Check if in write mode or not */
+                        clst = create_chain(fp->fs, clst);    /* Force stretch if in write mode */
+                        if (clst == 0) {                /* When disk gets full, clip file size */
+                            ofs = bcs; break;
+                        }
+                    } else
+#endif
+                        clst = get_fat(fp->fs, clst);    /* Follow cluster chain if not in write mode */
+                    if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                    if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR);
+                    fp->clust = clst;
+                    fp->fptr += bcs;
+                    ofs -= bcs;
+                }
+                fp->fptr += ofs;
+                if (ofs % SS(fp->fs)) {
+                    nsect = clust2sect(fp->fs, clst);    /* Current sector */
+                    if (!nsect) ABORT(fp->fs, FR_INT_ERR);
+                    nsect += ofs / SS(fp->fs);
+                }
+            }
+        }
+        if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) {    /* Fill sector cache if needed */
+#if !_FS_TINY
+#if !_FS_READONLY
+            if (fp->flag & FA__DIRTY) {            /* Write-back dirty sector cache */
+                if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+                fp->flag &= ~FA__DIRTY;
+            }
+#endif
+            if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK)    /* Fill sector cache */
+                ABORT(fp->fs, FR_DISK_ERR);
+#endif
+            fp->dsect = nsect;
+        }
+#if !_FS_READONLY
+        if (fp->fptr > fp->fsize) {            /* Set file change flag if the file size is extended */
+            fp->fsize = fp->fptr;
+            fp->flag |= FA__WRITTEN;
+        }
+#endif
+    }
+
+    LEAVE_FF(fp->fs, res);
+}
+
+
+
+#if _FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Create a Directroy Object                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_opendir (
+    FATFS_DIR *dj,            /* Pointer to directory object to create */
+    const TCHAR *path    /* Pointer to the directory path */
+)
+{
+    FRESULT res;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj->fs, 0);
+    if (res == FR_OK) {
+        INIT_BUF(*dj);
+        res = follow_path(dj, path);            /* Follow the path to the directory */
+        FREE_BUF();
+        if (res == FR_OK) {                        /* Follow completed */
+            if (dj->dir) {                        /* It is not the root dir */
+                if (dj->dir[DIR_Attr] & AM_DIR) {    /* The object is a directory */
+                    dj->sclust = LD_CLUST(dj->dir);
+                } else {                        /* The object is not a directory */
+                    res = FR_NO_PATH;
+                }
+            }
+            if (res == FR_OK) {
+                dj->id = dj->fs->id;
+                res = dir_sdi(dj, 0);            /* Rewind dir */
+            }
+        }
+        if (res == FR_NO_FILE) res = FR_NO_PATH;
+    }
+
+    LEAVE_FF(dj->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Directory Entry in Sequense                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_readdir (
+    FATFS_DIR *dj,            /* Pointer to the open directory object */
+    FILINFO *fno        /* Pointer to file information to return */
+)
+{
+    FRESULT res;
+    DEF_NAMEBUF;
+
+
+    res = validate(dj->fs, dj->id);            /* Check validity of the object */
+    if (res == FR_OK) {
+        if (!fno) {
+            res = dir_sdi(dj, 0);            /* Rewind the directory object */
+        } else {
+            INIT_BUF(*dj);
+            res = dir_read(dj);                /* Read an directory item */
+            if (res == FR_NO_FILE) {        /* Reached end of dir */
+                dj->sect = 0;
+                res = FR_OK;
+            }
+            if (res == FR_OK) {                /* A valid entry is found */
+                get_fileinfo(dj, fno);        /* Get the object information */
+                res = dir_next(dj, 0);        /* Increment index for next */
+                if (res == FR_NO_FILE) {
+                    dj->sect = 0;
+                    res = FR_OK;
+                }
+            }
+            FREE_BUF();
+        }
+    }
+
+    LEAVE_FF(dj->fs, res);
+}
+
+
+
+#if _FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Get File Status                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_stat (
+    const TCHAR *path,    /* Pointer to the file path */
+    FILINFO *fno        /* Pointer to file information to return */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj.fs, 0);
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);    /* Follow the file path */
+        if (res == FR_OK) {                /* Follow completed */
+            if (dj.dir)        /* Found an object */
+                get_fileinfo(&dj, fno);
+            else            /* It is root dir */
+                res = FR_INVALID_NAME;
+        }
+        FREE_BUF();
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Get Number of Free Clusters                                           */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getfree (
+    const TCHAR *path,    /* Pointer to the logical drive number (root dir) */
+    DWORD *nclst,        /* Pointer to the variable to return number of free clusters */
+    FATFS **fatfs        /* Pointer to pointer to corresponding file system object to return */
+)
+{
+    FRESULT res;
+    DWORD n, clst, sect, stat;
+    UINT i;
+    BYTE fat, *p;
+
+
+    /* Get drive number */
+    res = chk_mounted(&path, fatfs, 0);
+    if (res == FR_OK) {
+        /* If free_clust is valid, return it without full cluster scan */
+        if ((*fatfs)->free_clust <= (*fatfs)->n_fatent - 2) {
+            *nclst = (*fatfs)->free_clust;
+        } else {
+            /* Get number of free clusters */
+            fat = (*fatfs)->fs_type;
+            n = 0;
+            if (fat == FS_FAT12) {
+                clst = 2;
+                do {
+                    stat = get_fat(*fatfs, clst);
+                    if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
+                    if (stat == 1) { res = FR_INT_ERR; break; }
+                    if (stat == 0) n++;
+                } while (++clst < (*fatfs)->n_fatent);
+            } else {
+                clst = (*fatfs)->n_fatent;
+                sect = (*fatfs)->fatbase;
+                i = 0; p = 0;
+                do {
+                    if (!i) {
+                        res = move_window(*fatfs, sect++);
+                        if (res != FR_OK) break;
+                        p = (*fatfs)->win;
+                        i = SS(*fatfs);
+                    }
+                    if (fat == FS_FAT16) {
+                        if (LD_WORD(p) == 0) n++;
+                        p += 2; i -= 2;
+                    } else {
+                        if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++;
+                        p += 4; i -= 4;
+                    }
+                } while (--clst);
+            }
+            (*fatfs)->free_clust = n;
+            if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1;
+            *nclst = n;
+        }
+    }
+    LEAVE_FF(*fatfs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Truncate File                                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_truncate (
+    FIL *fp        /* Pointer to the file object */
+)
+{
+    FRESULT res;
+    DWORD ncl;
+
+
+    res = validate(fp->fs, fp->id);        /* Check validity of the object */
+    if (res == FR_OK) {
+        if (fp->flag & FA__ERROR) {            /* Check abort flag */
+            res = FR_INT_ERR;
+        } else {
+            if (!(fp->flag & FA_WRITE))        /* Check access mode */
+                res = FR_DENIED;
+        }
+    }
+    if (res == FR_OK) {
+        if (fp->fsize > fp->fptr) {
+            fp->fsize = fp->fptr;    /* Set file size to current R/W point */
+            fp->flag |= FA__WRITTEN;
+            if (fp->fptr == 0) {    /* When set file size to zero, remove entire cluster chain */
+                res = remove_chain(fp->fs, fp->sclust);
+                fp->sclust = 0;
+            } else {                /* When truncate a part of the file, remove remaining clusters */
+                ncl = get_fat(fp->fs, fp->clust);
+                res = FR_OK;
+                if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
+                if (ncl == 1) res = FR_INT_ERR;
+                if (res == FR_OK && ncl < fp->fs->n_fatent) {
+                    res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF);
+                    if (res == FR_OK) res = remove_chain(fp->fs, ncl);
+                }
+            }
+        }
+        if (res != FR_OK) fp->flag |= FA__ERROR;
+    }
+
+    LEAVE_FF(fp->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Delete a File or Directory                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_unlink (
+    const TCHAR *path        /* Pointer to the file or directory path */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj, sdj;
+    BYTE *dir;
+    DWORD dclst;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);        /* Follow the file path */
+        if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;            /* Cannot remove dot entry */
+#if _FS_SHARE
+        if (res == FR_OK) res = chk_lock(&dj, 2);    /* Cannot remove open file */
+#endif
+        if (res == FR_OK) {                    /* The object is accessible */
+            dir = dj.dir;
+            if (!dir) {
+                res = FR_INVALID_NAME;        /* Cannot remove the start directory */
+            } else {
+                if (dir[DIR_Attr] & AM_RDO)
+                    res = FR_DENIED;        /* Cannot remove R/O object */
+            }
+            dclst = LD_CLUST(dir);
+            if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) {    /* Is it a sub-dir? */
+                if (dclst < 2) {
+                    res = FR_INT_ERR;
+                } else {
+                    mem_cpy(&sdj, &dj, sizeof(FATFS_DIR));    /* Check if the sub-dir is empty or not */
+                    sdj.sclust = dclst;
+                    res = dir_sdi(&sdj, 2);        /* Exclude dot entries */
+                    if (res == FR_OK) {
+                        res = dir_read(&sdj);
+                        if (res == FR_OK            /* Not empty dir */
+#if _FS_RPATH
+                        || dclst == sdj.fs->cdir    /* Current dir */
+#endif
+                        ) res = FR_DENIED;
+                        if (res == FR_NO_FILE) res = FR_OK;    /* Empty */
+                    }
+                }
+            }
+            if (res == FR_OK) {
+                res = dir_remove(&dj);        /* Remove the directory entry */
+                if (res == FR_OK) {
+                    if (dclst)                /* Remove the cluster chain if exist */
+                        res = remove_chain(dj.fs, dclst);
+                    if (res == FR_OK) res = sync(dj.fs);
+                }
+            }
+        }
+        FREE_BUF();
+    }
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create a Directory                                                    */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkdir (
+    const TCHAR *path        /* Pointer to the directory path */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj;
+    BYTE *dir, n;
+    DWORD dsc, dcl, pcl, tim = get_fattime();
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);            /* Follow the file path */
+        if (res == FR_OK) res = FR_EXIST;        /* Any object with same name is already existing */
+        if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;
+        if (res == FR_NO_FILE) {                /* Can create a new directory */
+            dcl = create_chain(dj.fs, 0);        /* Allocate a cluster for the new directory table */
+            res = FR_OK;
+            if (dcl == 0) res = FR_DENIED;        /* No space to allocate a new cluster */
+            if (dcl == 1) res = FR_INT_ERR;
+            if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR;
+            if (res == FR_OK)                    /* Flush FAT */
+                res = move_window(dj.fs, 0);
+            if (res == FR_OK) {                    /* Initialize the new directory table */
+                dsc = clust2sect(dj.fs, dcl);
+                dir = dj.fs->win;
+                mem_set(dir, 0, SS(dj.fs));
+                mem_set(dir+DIR_Name, ' ', 8+3);    /* Create "." entry */
+                dir[DIR_Name] = '.';
+                dir[DIR_Attr] = AM_DIR;
+                ST_DWORD(dir+DIR_WrtTime, tim);
+                ST_CLUST(dir, dcl);
+                mem_cpy(dir+SZ_DIR, dir, SZ_DIR);     /* Create ".." entry */
+                dir[33] = '.'; pcl = dj.sclust;
+                if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase)
+                    pcl = 0;
+                ST_CLUST(dir+SZ_DIR, pcl);
+                for (n = dj.fs->csize; n; n--) {    /* Write dot entries and clear following sectors */
+                    dj.fs->winsect = dsc++;
+                    dj.fs->wflag = 1;
+                    res = move_window(dj.fs, 0);
+                    if (res != FR_OK) break;
+                    mem_set(dir, 0, SS(dj.fs));
+                }
+            }
+            if (res == FR_OK) res = dir_register(&dj);    /* Register the object to the directoy */
+            if (res != FR_OK) {
+                remove_chain(dj.fs, dcl);            /* Could not register, remove cluster chain */
+            } else {
+                dir = dj.dir;
+                dir[DIR_Attr] = AM_DIR;                /* Attribute */
+                ST_DWORD(dir+DIR_WrtTime, tim);        /* Created time */
+                ST_CLUST(dir, dcl);                    /* Table start cluster */
+                dj.fs->wflag = 1;
+                res = sync(dj.fs);
+            }
+        }
+        FREE_BUF();
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Attribute                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chmod (
+    const TCHAR *path,    /* Pointer to the file path */
+    BYTE value,            /* Attribute bits */
+    BYTE mask            /* Attribute mask to change */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj;
+    BYTE *dir;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);        /* Follow the file path */
+        FREE_BUF();
+        if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;
+        if (res == FR_OK) {
+            dir = dj.dir;
+            if (!dir) {                        /* Is it a root directory? */
+                res = FR_INVALID_NAME;
+            } else {                        /* File or sub directory */
+                mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;    /* Valid attribute mask */
+                dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask);    /* Apply attribute change */
+                dj.fs->wflag = 1;
+                res = sync(dj.fs);
+            }
+        }
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Timestamp                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_utime (
+    const TCHAR *path,    /* Pointer to the file/directory name */
+    const FILINFO *fno    /* Pointer to the time stamp to be set */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj;
+    BYTE *dir;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);    /* Follow the file path */
+        FREE_BUF();
+        if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;
+        if (res == FR_OK) {
+            dir = dj.dir;
+            if (!dir) {                    /* Root directory */
+                res = FR_INVALID_NAME;
+            } else {                    /* File or sub-directory */
+                ST_WORD(dir+DIR_WrtTime, fno->ftime);
+                ST_WORD(dir+DIR_WrtDate, fno->fdate);
+                dj.fs->wflag = 1;
+                res = sync(dj.fs);
+            }
+        }
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Rename File/Directory                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_rename (
+    const TCHAR *path_old,    /* Pointer to the old name */
+    const TCHAR *path_new    /* Pointer to the new name */
+)
+{
+    FRESULT res;
+    FATFS_DIR djo, djn;
+    BYTE buf[21], *dir;
+    DWORD dw;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path_old, &djo.fs, 1);
+    if (res == FR_OK) {
+        djn.fs = djo.fs;
+        INIT_BUF(djo);
+        res = follow_path(&djo, path_old);        /* Check old object */
+        if (_FS_RPATH && res == FR_OK && (djo.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;
+#if _FS_SHARE
+        if (res == FR_OK) res = chk_lock(&djo, 2);
+#endif
+        if (res == FR_OK) {                        /* Old object is found */
+            if (!djo.dir) {                        /* Is root dir? */
+                res = FR_NO_FILE;
+            } else {
+                mem_cpy(buf, djo.dir+DIR_Attr, 21);        /* Save the object information except for name */
+                mem_cpy(&djn, &djo, sizeof(FATFS_DIR));        /* Check new object */
+                res = follow_path(&djn, path_new);
+                if (res == FR_OK) res = FR_EXIST;        /* The new object name is already existing */
+                if (res == FR_NO_FILE) {                 /* Is it a valid path and no name collision? */
+/* Start critical section that any interruption or error can cause cross-link */
+                    res = dir_register(&djn);            /* Register the new entry */
+                    if (res == FR_OK) {
+                        dir = djn.dir;                    /* Copy object information except for name */
+                        mem_cpy(dir+13, buf+2, 19);
+                        dir[DIR_Attr] = buf[0] | AM_ARC;
+                        djo.fs->wflag = 1;
+                        if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) {        /* Update .. entry in the directory if needed */
+                            dw = clust2sect(djn.fs, LD_CLUST(dir));
+                            if (!dw) {
+                                res = FR_INT_ERR;
+                            } else {
+                                res = move_window(djn.fs, dw);
+                                dir = djn.fs->win+SZ_DIR;    /* .. entry */
+                                if (res == FR_OK && dir[1] == '.') {
+                                    dw = (djn.fs->fs_type == FS_FAT32 && djn.sclust == djn.fs->dirbase) ? 0 : djn.sclust;
+                                    ST_CLUST(dir, dw);
+                                    djn.fs->wflag = 1;
+                                }
+                            }
+                        }
+                        if (res == FR_OK) {
+                            res = dir_remove(&djo);        /* Remove old entry */
+                            if (res == FR_OK)
+                                res = sync(djo.fs);
+                        }
+                    }
+/* End critical section */
+                }
+            }
+        }
+        FREE_BUF();
+    }
+    LEAVE_FF(djo.fs, res);
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _FS_MINIMIZE == 0 */
+#endif /* _FS_MINIMIZE <= 1 */
+#endif /* _FS_MINIMIZE <= 2 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Forward data to the stream directly (available on only tiny cfg)      */
+/*-----------------------------------------------------------------------*/
+#if _USE_FORWARD && _FS_TINY
+
+FRESULT f_forward (
+    FIL *fp,                         /* Pointer to the file object */
+    UINT (*func)(const BYTE*,UINT),    /* Pointer to the streaming function */
+    UINT btr,                        /* Number of bytes to forward */
+    UINT *bf                        /* Pointer to number of bytes forwarded */
+)
+{
+    FRESULT res;
+    DWORD remain, clst, sect;
+    UINT rcnt;
+    BYTE csect;
+
+
+    *bf = 0;    /* Initialize byte counter */
+
+    res = validate(fp->fs, fp->id);                    /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                        /* Check error flag */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_READ))                        /* Check access mode */
+        LEAVE_FF(fp->fs, FR_DENIED);
+
+    remain = fp->fsize - fp->fptr;
+    if (btr > remain) btr = (UINT)remain;            /* Truncate btr by remaining bytes */
+
+    for ( ;  btr && (*func)(0, 0);                    /* Repeat until all data transferred or stream becomes busy */
+        fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) {
+        csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));    /* Sector offset in the cluster */
+        if ((fp->fptr % SS(fp->fs)) == 0) {            /* On the sector boundary? */
+            if (!csect) {                            /* On the cluster boundary? */
+                clst = (fp->fptr == 0) ?            /* On the top of the file? */
+                    fp->sclust : get_fat(fp->fs, fp->clust);
+                if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                fp->clust = clst;                    /* Update current cluster */
+            }
+        }
+        sect = clust2sect(fp->fs, fp->clust);        /* Get current data sector */
+        if (!sect) ABORT(fp->fs, FR_INT_ERR);
+        sect += csect;
+        if (move_window(fp->fs, sect))                /* Move sector window */
+            ABORT(fp->fs, FR_DISK_ERR);
+        fp->dsect = sect;
+        rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs));    /* Forward data from sector window */
+        if (rcnt > btr) rcnt = btr;
+        rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
+        if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
+    }
+
+    LEAVE_FF(fp->fs, FR_OK);
+}
+#endif /* _USE_FORWARD */
+
+
+
+#if _USE_MKFS && !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Create File System on the Drive                                       */
+/*-----------------------------------------------------------------------*/
+#define N_ROOTDIR    512        /* Number of root dir entries for FAT12/16 */
+#define N_FATS        1        /* Number of FAT copies (1 or 2) */
+
+
+FRESULT f_mkfs (
+    BYTE drv,        /* Logical drive number */
+    BYTE sfd,        /* Partitioning rule 0:FDISK, 1:SFD */
+    UINT au            /* Allocation unit size [bytes] */
+)
+{
+    static const WORD vst[] = { 1024,   512,  256,  128,   64,    32,   16,    8,    4,    2,   0};
+    static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512};
+    BYTE fmt, md, sys, *tbl, pdrv, part;
+    DWORD n_clst, vs, n, wsect;
+    UINT i;
+    DWORD b_vol, b_fat, b_dir, b_data;    /* LBA */
+    DWORD n_vol, n_rsv, n_fat, n_dir;    /* Size */
+    FATFS *fs;
+    DSTATUS stat;
+
+
+    /* Check mounted drive and clear work area */
+    if (drv >= _VOLUMES) return FR_INVALID_DRIVE;
+    if (sfd > 1) return FR_INVALID_PARAMETER;
+    if (au & (au - 1)) return FR_INVALID_PARAMETER;
+    fs = FatFs[drv];
+    if (!fs) return FR_NOT_ENABLED;
+    fs->fs_type = 0;
+    pdrv = LD2PD(drv);    /* Physical drive */
+    part = LD2PT(drv);    /* Partition (0:auto detect, 1-4:get from partition table)*/
+
+    /* Get disk statics */
+    stat = disk_initialize(pdrv);
+    if (stat & STA_NOINIT) return FR_NOT_READY;
+    if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+#if _MAX_SS != 512                    /* Get disk sector size */
+    if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS)
+        return FR_DISK_ERR;
+#endif
+    if (_MULTI_PARTITION && part) {
+        /* Get partition information from partition table in the MBR */
+        if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR;
+        if (LD_WORD(fs->win+BS_55AA) != 0xAA55) return FR_MKFS_ABORTED;
+        tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];
+        if (!tbl[4]) return FR_MKFS_ABORTED;    /* No partition? */
+        b_vol = LD_DWORD(tbl+8);    /* Volume start sector */
+        n_vol = LD_DWORD(tbl+12);    /* Volume size */
+    } else {
+        /* Create a partition in this function */
+        if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128)
+            return FR_DISK_ERR;
+        b_vol = (sfd) ? 0 : 63;        /* Volume start sector */
+        n_vol -= b_vol;                /* Volume size */
+    }
+
+    if (!au) {                /* AU auto selection */
+        vs = n_vol / (2000 / (SS(fs) / 512));
+        for (i = 0; vs < vst[i]; i++) ;
+        au = cst[i];
+    }
+    au /= SS(fs);        /* Number of sectors per cluster */
+    if (au == 0) au = 1;
+    if (au > 128) au = 128;
+
+    /* Pre-compute number of clusters and FAT syb-type */
+    n_clst = n_vol / au;
+    fmt = FS_FAT12;
+    if (n_clst >= MIN_FAT16) fmt = FS_FAT16;
+    if (n_clst >= MIN_FAT32) fmt = FS_FAT32;
+
+    /* Determine offset and size of FAT structure */
+    if (fmt == FS_FAT32) {
+        n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
+        n_rsv = 32;
+        n_dir = 0;
+    } else {
+        n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4;
+        n_fat = (n_fat + SS(fs) - 1) / SS(fs);
+        n_rsv = 1;
+        n_dir = (DWORD)N_ROOTDIR * SZ_DIR / SS(fs);
+    }
+    b_fat = b_vol + n_rsv;                /* FAT area start sector */
+    b_dir = b_fat + n_fat * N_FATS;        /* Directory area start sector */
+    b_data = b_dir + n_dir;                /* Data area start sector */
+    if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED;    /* Too small volume */
+
+    /* Align data start sector to erase block boundary (for flash memory media) */
+    if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1;
+    n = (b_data + n - 1) & ~(n - 1);    /* Next nearest erase block from current data start */
+    n = (n - b_data) / N_FATS;
+    if (fmt == FS_FAT32) {        /* FAT32: Move FAT offset */
+        n_rsv += n;
+        b_fat += n;
+    } else {                    /* FAT12/16: Expand FAT size */
+        n_fat += n;
+    }
+
+    /* Determine number of clusters and final check of validity of the FAT sub-type */
+    n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au;
+    if (   (fmt == FS_FAT16 && n_clst < MIN_FAT16)
+        || (fmt == FS_FAT32 && n_clst < MIN_FAT32))
+        return FR_MKFS_ABORTED;
+
+    switch (fmt) {    /* Determine system ID for partition table */
+    case FS_FAT12:    sys = 0x01; break;
+    case FS_FAT16:    sys = (n_vol < 0x10000) ? 0x04 : 0x06; break;
+    default:         sys = 0x0C;
+    }
+
+    if (_MULTI_PARTITION && part) {
+        /* Update system ID in the partition table */
+        tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];
+        tbl[4] = sys;
+        if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR;
+        md = 0xF8;
+    } else {
+        if (sfd) {    /* No patition table (SFD) */
+            md = 0xF0;
+        } else {    /* Create partition table (FDISK) */
+            mem_set(fs->win, 0, SS(fs));
+            tbl = fs->win+MBR_Table;    /* Create partiton table for single partition in the drive */
+            tbl[1] = 1;                        /* Partition start head */
+            tbl[2] = 1;                        /* Partition start sector */
+            tbl[3] = 0;                        /* Partition start cylinder */
+            tbl[4] = sys;                    /* System type */
+            tbl[5] = 254;                    /* Partition end head */
+            n = (b_vol + n_vol) / 63 / 255;
+            tbl[6] = (BYTE)((n >> 2) | 63);    /* Partiiton end sector */
+            tbl[7] = (BYTE)n;                /* End cylinder */
+            ST_DWORD(tbl+8, 63);            /* Partition start in LBA */
+            ST_DWORD(tbl+12, n_vol);        /* Partition size in LBA */
+            ST_WORD(fs->win+BS_55AA, 0xAA55);    /* MBR signature */
+            if (disk_write(pdrv, fs->win, 0, 1) != RES_OK)    /* Write it to the MBR sector */
+                return FR_DISK_ERR;
+            md = 0xF8;
+        }
+    }
+
+    /* Create BPB in the VBR */
+    tbl = fs->win;                            /* Clear sector */
+    mem_set(tbl, 0, SS(fs));
+    mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */
+    i = SS(fs);                                /* Sector size */
+    ST_WORD(tbl+BPB_BytsPerSec, i);
+    tbl[BPB_SecPerClus] = (BYTE)au;            /* Sectors per cluster */
+    ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv);        /* Reserved sectors */
+    tbl[BPB_NumFATs] = N_FATS;                /* Number of FATs */
+    i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR;    /* Number of rootdir entries */
+    ST_WORD(tbl+BPB_RootEntCnt, i);
+    if (n_vol < 0x10000) {                    /* Number of total sectors */
+        ST_WORD(tbl+BPB_TotSec16, n_vol);
+    } else {
+        ST_DWORD(tbl+BPB_TotSec32, n_vol);
+    }
+    tbl[BPB_Media] = md;                    /* Media descriptor */
+    ST_WORD(tbl+BPB_SecPerTrk, 63);            /* Number of sectors per track */
+    ST_WORD(tbl+BPB_NumHeads, 255);            /* Number of heads */
+    ST_DWORD(tbl+BPB_HiddSec, b_vol);        /* Hidden sectors */
+    n = get_fattime();                        /* Use current time as VSN */
+    if (fmt == FS_FAT32) {
+        ST_DWORD(tbl+BS_VolID32, n);        /* VSN */
+        ST_DWORD(tbl+BPB_FATSz32, n_fat);    /* Number of sectors per FAT */
+        ST_DWORD(tbl+BPB_RootClus, 2);        /* Root directory start cluster (2) */
+        ST_WORD(tbl+BPB_FSInfo, 1);            /* FSInfo record offset (VBR+1) */
+        ST_WORD(tbl+BPB_BkBootSec, 6);        /* Backup boot record offset (VBR+6) */
+        tbl[BS_DrvNum32] = 0x80;            /* Drive number */
+        tbl[BS_BootSig32] = 0x29;            /* Extended boot signature */
+        mem_cpy(tbl+BS_VolLab32, "NO NAME    " "FAT32   ", 19);    /* Volume label, FAT signature */
+    } else {
+        ST_DWORD(tbl+BS_VolID, n);            /* VSN */
+        ST_WORD(tbl+BPB_FATSz16, n_fat);    /* Number of sectors per FAT */
+        tbl[BS_DrvNum] = 0x80;                /* Drive number */
+        tbl[BS_BootSig] = 0x29;                /* Extended boot signature */
+        mem_cpy(tbl+BS_VolLab, "NO NAME    " "FAT     ", 19);    /* Volume label, FAT signature */
+    }
+    ST_WORD(tbl+BS_55AA, 0xAA55);            /* Signature (Offset is fixed here regardless of sector size) */
+    if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK)    /* Write it to the VBR sector */
+        return FR_DISK_ERR;
+    if (fmt == FS_FAT32)                            /* Write backup VBR if needed (VBR+6) */
+        disk_write(pdrv, tbl, b_vol + 6, 1);
+
+    /* Initialize FAT area */
+    wsect = b_fat;
+    for (i = 0; i < N_FATS; i++) {        /* Initialize each FAT copy */
+        mem_set(tbl, 0, SS(fs));            /* 1st sector of the FAT  */
+        n = md;                                /* Media descriptor byte */
+        if (fmt != FS_FAT32) {
+            n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;
+            ST_DWORD(tbl+0, n);                /* Reserve cluster #0-1 (FAT12/16) */
+        } else {
+            n |= 0xFFFFFF00;
+            ST_DWORD(tbl+0, n);                /* Reserve cluster #0-1 (FAT32) */
+            ST_DWORD(tbl+4, 0xFFFFFFFF);
+            ST_DWORD(tbl+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root dir */
+        }
+        if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)
+            return FR_DISK_ERR;
+        mem_set(tbl, 0, SS(fs));            /* Fill following FAT entries with zero */
+        for (n = 1; n < n_fat; n++) {        /* This loop may take a time on FAT32 volume due to many single sector writes */
+            if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)
+                return FR_DISK_ERR;
+        }
+    }
+
+    /* Initialize root directory */
+    i = (fmt == FS_FAT32) ? au : n_dir;
+    do {
+        if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)
+            return FR_DISK_ERR;
+    } while (--i);
+
+#if _USE_ERASE    /* Erase data area if needed */
+    {
+        DWORD eb[2];
+
+        eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1;
+        disk_ioctl(pdrv, CTRL_ERASE_SECTOR, eb);
+    }
+#endif
+
+    /* Create FSInfo if needed */
+    if (fmt == FS_FAT32) {
+        ST_DWORD(tbl+FSI_LeadSig, 0x41615252);
+        ST_DWORD(tbl+FSI_StrucSig, 0x61417272);
+        ST_DWORD(tbl+FSI_Free_Count, n_clst - 1);    /* Number of free clusters */
+        ST_DWORD(tbl+FSI_Nxt_Free, 2);                /* Last allocated cluster# */
+        ST_WORD(tbl+BS_55AA, 0xAA55);
+        disk_write(pdrv, tbl, b_vol + 1, 1);    /* Write original (VBR+1) */
+        disk_write(pdrv, tbl, b_vol + 7, 1);    /* Write backup (VBR+7) */
+    }
+
+    return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR;
+}
+
+
+#if _MULTI_PARTITION == 2
+/*-----------------------------------------------------------------------*/
+/* Divide Physical Drive                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_fdisk (
+    BYTE pdrv,            /* Physical drive number */
+    const DWORD szt[],    /* Pointer to the size table for each partitions */
+    void* work            /* Pointer to the working buffer */
+)
+{
+    UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl;
+    BYTE s_hd, e_hd, *p, *buf = (BYTE*)work;
+    DSTATUS stat;
+    DWORD sz_disk, sz_part, s_part;
+
+
+    stat = disk_initialize(pdrv);
+    if (stat & STA_NOINIT) return FR_NOT_READY;
+    if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+    if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR;
+
+    /* Determine CHS in the table regardless of the drive geometry */
+    for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;
+    if (n == 256) n--;
+    e_hd = n - 1;
+    sz_cyl = 63 * n;
+    tot_cyl = sz_disk / sz_cyl;
+
+    /* Create partition table */
+    mem_set(buf, 0, _MAX_SS);
+    p = buf + MBR_Table; b_cyl = 0;
+    for (i = 0; i < 4; i++, p += SZ_PTE) {
+        p_cyl = (szt[i] <= 100) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl;
+        if (!p_cyl) continue;
+        s_part = (DWORD)sz_cyl * b_cyl;
+        sz_part = (DWORD)sz_cyl * p_cyl;
+        if (i == 0) {    /* Exclude first track of cylinder 0 */
+            s_hd = 1;
+            s_part += 63; sz_part -= 63;
+        } else {
+            s_hd = 0;
+        }
+        e_cyl = b_cyl + p_cyl - 1;
+        if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER;
+
+        /* Set partition table */
+        p[1] = s_hd;                        /* Start head */
+        p[2] = (BYTE)((b_cyl >> 2) + 1);    /* Start sector */
+        p[3] = (BYTE)b_cyl;                    /* Start cylinder */
+        p[4] = 0x06;                        /* System type (temporary setting) */
+        p[5] = e_hd;                        /* End head */
+        p[6] = (BYTE)((e_cyl >> 2) + 63);    /* End sector */
+        p[7] = (BYTE)e_cyl;                    /* End cylinder */
+        ST_DWORD(p + 8, s_part);            /* Start sector in LBA */
+        ST_DWORD(p + 12, sz_part);            /* Partition size */
+
+        /* Next partition */
+        b_cyl += p_cyl;
+    }
+    ST_WORD(p, 0xAA55);
+
+    /* Write it to the MBR */
+    return (disk_write(pdrv, buf, 0, 1) || disk_ioctl(pdrv, CTRL_SYNC, 0)) ? FR_DISK_ERR : FR_OK;
+}
+
+
+#endif /* _MULTI_PARTITION == 2 */
+#endif /* _USE_MKFS && !_FS_READONLY */
+
+
+
+
+#if _USE_STRFUNC
+/*-----------------------------------------------------------------------*/
+/* Get a string from the file                                            */
+/*-----------------------------------------------------------------------*/
+TCHAR* f_gets (
+    TCHAR* buff,    /* Pointer to the string buffer to read */
+    int len,        /* Size of string buffer (characters) */
+    FIL* fil        /* Pointer to the file object */
+)
+{
+    int n = 0;
+    TCHAR c, *p = buff;
+    BYTE s[2];
+    UINT rc;
+
+
+    while (n < len - 1) {            /* Read bytes until buffer gets filled */
+        f_read(fil, s, 1, &rc);
+        if (rc != 1) break;            /* Break on EOF or error */
+        c = s[0];
+#if _LFN_UNICODE                    /* Read a character in UTF-8 encoding */
+        if (c >= 0x80) {
+            if (c < 0xC0) continue;    /* Skip stray trailer */
+            if (c < 0xE0) {            /* Two-byte sequense */
+                f_read(fil, s, 1, &rc);
+                if (rc != 1) break;
+                c = ((c & 0x1F) << 6) | (s[0] & 0x3F);
+                if (c < 0x80) c = '?';
+            } else {
+                if (c < 0xF0) {        /* Three-byte sequense */
+                    f_read(fil, s, 2, &rc);
+                    if (rc != 2) break;
+                    c = (c << 12) | ((s[0] & 0x3F) << 6) | (s[1] & 0x3F);
+                    if (c < 0x800) c = '?';
+                } else {            /* Reject four-byte sequense */
+                    c = '?';
+                }
+            }
+        }
+#endif
+#if _USE_STRFUNC >= 2
+        if (c == '\r') continue;    /* Strip '\r' */
+#endif
+        *p++ = c;
+        n++;
+        if (c == '\n') break;        /* Break on EOL */
+    }
+    *p = 0;
+    return n ? buff : 0;            /* When no data read (eof or error), return with error. */
+}
+
+
+
+#if !_FS_READONLY
+#include <stdarg.h>
+/*-----------------------------------------------------------------------*/
+/* Put a character to the file                                           */
+/*-----------------------------------------------------------------------*/
+int f_putc (
+    TCHAR c,    /* A character to be output */
+    FIL* fil    /* Pointer to the file object */
+)
+{
+    UINT bw, btw;
+    BYTE s[3];
+
+
+#if _USE_STRFUNC >= 2
+    if (c == '\n') f_putc ('\r', fil);    /* LF -> CRLF conversion */
+#endif
+
+#if _LFN_UNICODE    /* Write the character in UTF-8 encoding */
+    if (c < 0x80) {            /* 7-bit */
+        s[0] = (BYTE)c;
+        btw = 1;
+    } else {
+        if (c < 0x800) {    /* 11-bit */
+            s[0] = (BYTE)(0xC0 | (c >> 6));
+            s[1] = (BYTE)(0x80 | (c & 0x3F));
+            btw = 2;
+        } else {            /* 16-bit */
+            s[0] = (BYTE)(0xE0 | (c >> 12));
+            s[1] = (BYTE)(0x80 | ((c >> 6) & 0x3F));
+            s[2] = (BYTE)(0x80 | (c & 0x3F));
+            btw = 3;
+        }
+    }
+#else                /* Write the character without conversion */
+    s[0] = (BYTE)c;
+    btw = 1;
+#endif
+    f_write(fil, s, btw, &bw);        /* Write the char to the file */
+    return (bw == btw) ? 1 : EOF;    /* Return the result */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a string to the file                                              */
+/*-----------------------------------------------------------------------*/
+int f_puts (
+    const TCHAR* str,    /* Pointer to the string to be output */
+    FIL* fil            /* Pointer to the file object */
+)
+{
+    int n;
+
+
+    for (n = 0; *str; str++, n++) {
+        if (f_putc(*str, fil) == EOF) return EOF;
+    }
+    return n;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a formatted string to the file                                    */
+/*-----------------------------------------------------------------------*/
+int f_printf (
+    FIL* fil,            /* Pointer to the file object */
+    const TCHAR* str,    /* Pointer to the format string */
+    ...                    /* Optional arguments... */
+)
+{
+    va_list arp;
+    BYTE f, r;
+    UINT i, j, w;
+    ULONG v;
+    TCHAR c, d, s[16], *p;
+    int res, chc, cc;
+
+
+    va_start(arp, str);
+
+    for (cc = res = 0; cc != EOF; res += cc) {
+        c = *str++;
+        if (c == 0) break;            /* End of string */
+        if (c != '%') {                /* Non escape character */
+            cc = f_putc(c, fil);
+            if (cc != EOF) cc = 1;
+            continue;
+        }
+        w = f = 0;
+        c = *str++;
+        if (c == '0') {                /* Flag: '0' padding */
+            f = 1; c = *str++;
+        } else {
+            if (c == '-') {            /* Flag: left justified */
+                f = 2; c = *str++;
+            }
+        }
+        while (IsDigit(c)) {        /* Precision */
+            w = w * 10 + c - '0';
+            c = *str++;
+        }
+        if (c == 'l' || c == 'L') {    /* Prefix: Size is long int */
+            f |= 4; c = *str++;
+        }
+        if (!c) break;
+        d = c;
+        if (IsLower(d)) d -= 0x20;
+        switch (d) {                /* Type is... */
+        case 'S' :                    /* String */
+            p = va_arg(arp, TCHAR*);
+            for (j = 0; p[j]; j++) ;
+            chc = 0;
+            if (!(f & 2)) {
+                while (j++ < w) chc += (cc = f_putc(' ', fil));
+            }
+            chc += (cc = f_puts(p, fil));
+            while (j++ < w) chc += (cc = f_putc(' ', fil));
+            if (cc != EOF) cc = chc;
+            continue;
+        case 'C' :                    /* Character */
+            cc = f_putc((TCHAR)va_arg(arp, int), fil); continue;
+        case 'B' :                    /* Binary */
+            r = 2; break;
+        case 'O' :                    /* Octal */
+            r = 8; break;
+        case 'D' :                    /* Signed decimal */
+        case 'U' :                    /* Unsigned decimal */
+            r = 10; break;
+        case 'X' :                    /* Hexdecimal */
+            r = 16; break;
+        default:                    /* Unknown type (passthrough) */
+            cc = f_putc(c, fil); continue;
+        }
+
+        /* Get an argument and put it in numeral */
+        v = (f & 4) ? (ULONG)va_arg(arp, long) : ((d == 'D') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int));
+        if (d == 'D' && (v & 0x80000000)) {
+            v = 0 - v;
+            f |= 8;
+        }
+        i = 0;
+        do {
+            d = (TCHAR)(v % r); v /= r;
+            if (d > 9) d += (c == 'x') ? 0x27 : 0x07;
+            s[i++] = d + '0';
+        } while (v && i < sizeof(s) / sizeof(s[0]));
+        if (f & 8) s[i++] = '-';
+        j = i; d = (f & 1) ? '0' : ' ';
+        res = 0;
+        while (!(f & 2) && j++ < w) res += (cc = f_putc(d, fil));
+        do res += (cc = f_putc(s[--i], fil)); while(i);
+        while (j++ < w) res += (cc = f_putc(' ', fil));
+        if (cc != EOF) cc = res;
+    }
+
+    va_end(arp);
+    return (cc == EOF) ? cc : res;
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _USE_STRFUNC */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/ff.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,337 @@
+/*---------------------------------------------------------------------------/
+/  FatFs - FAT file system module include file  R0.09     (C)ChaN, 2011
+/----------------------------------------------------------------------------/
+/ FatFs module is a generic FAT file system module for small embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following trems.
+/
+/  Copyright (C) 2011, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/   personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/----------------------------------------------------------------------------*/
+
+#ifndef _FATFS
+#define _FATFS    6502    /* Revision ID */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "integer.h"    /* Basic integer types */
+#include "ffconf.h"        /* FatFs configuration options */
+
+#if _FATFS != _FFCONF
+#error Wrong configuration file (ffconf.h).
+#endif
+
+
+
+/* Definitions of volume management */
+
+#if _MULTI_PARTITION        /* Multiple partition configuration */
+typedef struct {
+    BYTE pd;    /* Physical drive number */
+    BYTE pt;    /* Partition: 0:Auto detect, 1-4:Forced partition) */
+} PARTITION;
+extern PARTITION VolToPart[];    /* Volume - Partition resolution table */
+#define LD2PD(vol) (VolToPart[vol].pd)    /* Get physical drive number */
+#define LD2PT(vol) (VolToPart[vol].pt)    /* Get partition index */
+
+#else                        /* Single partition configuration */
+#define LD2PD(vol) (vol)    /* Each logical drive is bound to the same physical drive number */
+#define LD2PT(vol) 0        /* Always mounts the 1st partition or in SFD */
+
+#endif
+
+
+
+/* Type of path name strings on FatFs API */
+
+#if _LFN_UNICODE            /* Unicode string */
+#if !_USE_LFN
+#error _LFN_UNICODE must be 0 in non-LFN cfg.
+#endif
+#ifndef _INC_TCHAR
+typedef WCHAR TCHAR;
+#define _T(x) L ## x
+#define _TEXT(x) L ## x
+#endif
+
+#else                        /* ANSI/OEM string */
+#ifndef _INC_TCHAR
+typedef char TCHAR;
+#define _T(x) x
+#define _TEXT(x) x
+#endif
+
+#endif
+
+
+
+/* File system object structure (FATFS) */
+
+typedef struct {
+    BYTE    fs_type;        /* FAT sub-type (0:Not mounted) */
+    BYTE    drv;            /* Physical drive number */
+    BYTE    csize;            /* Sectors per cluster (1,2,4...128) */
+    BYTE    n_fats;            /* Number of FAT copies (1,2) */
+    BYTE    wflag;            /* win[] dirty flag (1:must be written back) */
+    BYTE    fsi_flag;        /* fsinfo dirty flag (1:must be written back) */
+    WORD    id;                /* File system mount ID */
+    WORD    n_rootdir;        /* Number of root directory entries (FAT12/16) */
+#if _MAX_SS != 512
+    WORD    ssize;            /* Bytes per sector (512, 1024, 2048 or 4096) */
+#endif
+#if _FS_REENTRANT
+    _SYNC_t    sobj;            /* Identifier of sync object */
+#endif
+#if !_FS_READONLY
+    DWORD    last_clust;        /* Last allocated cluster */
+    DWORD    free_clust;        /* Number of free clusters */
+    DWORD    fsi_sector;        /* fsinfo sector (FAT32) */
+#endif
+#if _FS_RPATH
+    DWORD    cdir;            /* Current directory start cluster (0:root) */
+#endif
+    DWORD    n_fatent;        /* Number of FAT entries (= number of clusters + 2) */
+    DWORD    fsize;            /* Sectors per FAT */
+    DWORD    fatbase;        /* FAT start sector */
+    DWORD    dirbase;        /* Root directory start sector (FAT32:Cluster#) */
+    DWORD    database;        /* Data start sector */
+    DWORD    winsect;        /* Current sector appearing in the win[] */
+    BYTE    win[_MAX_SS];    /* Disk access window for Directory, FAT (and Data on tiny cfg) */
+} FATFS;
+
+
+
+/* File object structure (FIL) */
+
+typedef struct {
+    FATFS*    fs;                /* Pointer to the owner file system object */
+    WORD    id;                /* Owner file system mount ID */
+    BYTE    flag;            /* File status flags */
+    BYTE    pad1;
+    DWORD    fptr;            /* File read/write pointer (0 on file open) */
+    DWORD    fsize;            /* File size */
+    DWORD    sclust;            /* File start cluster (0 when fsize==0) */
+    DWORD    clust;            /* Current cluster */
+    DWORD    dsect;            /* Current data sector */
+#if !_FS_READONLY
+    DWORD    dir_sect;        /* Sector containing the directory entry */
+    BYTE*    dir_ptr;        /* Ponter to the directory entry in the window */
+#endif
+#if _USE_FASTSEEK
+    DWORD*    cltbl;            /* Pointer to the cluster link map table (null on file open) */
+#endif
+#if _FS_SHARE
+    UINT    lockid;            /* File lock ID (index of file semaphore table) */
+#endif
+#if !_FS_TINY
+    BYTE    buf[_MAX_SS];    /* File data read/write buffer */
+#endif
+} FIL;
+
+
+
+/* Directory object structure (FATFS_DIR) */
+
+typedef struct {
+    FATFS*    fs;                /* Pointer to the owner file system object */
+    WORD    id;                /* Owner file system mount ID */
+    WORD    index;            /* Current read/write index number */
+    DWORD    sclust;            /* Table start cluster (0:Root dir) */
+    DWORD    clust;            /* Current cluster */
+    DWORD    sect;            /* Current sector */
+    BYTE*    dir;            /* Pointer to the current SFN entry in the win[] */
+    BYTE*    fn;                /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
+#if _USE_LFN
+    WCHAR*    lfn;            /* Pointer to the LFN working buffer */
+    WORD    lfn_idx;        /* Last matched LFN index number (0xFFFF:No LFN) */
+#endif
+} FATFS_DIR;
+
+
+
+/* File status structure (FILINFO) */
+
+typedef struct {
+    DWORD    fsize;            /* File size */
+    WORD    fdate;            /* Last modified date */
+    WORD    ftime;            /* Last modified time */
+    BYTE    fattrib;        /* Attribute */
+    TCHAR    fname[13];        /* Short file name (8.3 format) */
+#if _USE_LFN
+    TCHAR*    lfname;            /* Pointer to the LFN buffer */
+    UINT     lfsize;            /* Size of LFN buffer in TCHAR */
+#endif
+} FILINFO;
+
+
+
+/* File function return code (FRESULT) */
+
+typedef enum {
+    FR_OK = 0,                /* (0) Succeeded */
+    FR_DISK_ERR,            /* (1) A hard error occured in the low level disk I/O layer */
+    FR_INT_ERR,                /* (2) Assertion failed */
+    FR_NOT_READY,            /* (3) The physical drive cannot work */
+    FR_NO_FILE,                /* (4) Could not find the file */
+    FR_NO_PATH,                /* (5) Could not find the path */
+    FR_INVALID_NAME,        /* (6) The path name format is invalid */
+    FR_DENIED,                /* (7) Acces denied due to prohibited access or directory full */
+    FR_EXIST,                /* (8) Acces denied due to prohibited access */
+    FR_INVALID_OBJECT,        /* (9) The file/directory object is invalid */
+    FR_WRITE_PROTECTED,        /* (10) The physical drive is write protected */
+    FR_INVALID_DRIVE,        /* (11) The logical drive number is invalid */
+    FR_NOT_ENABLED,            /* (12) The volume has no work area */
+    FR_NO_FILESYSTEM,        /* (13) There is no valid FAT volume */
+    FR_MKFS_ABORTED,        /* (14) The f_mkfs() aborted due to any parameter error */
+    FR_TIMEOUT,                /* (15) Could not get a grant to access the volume within defined period */
+    FR_LOCKED,                /* (16) The operation is rejected according to the file shareing policy */
+    FR_NOT_ENOUGH_CORE,        /* (17) LFN working buffer could not be allocated */
+    FR_TOO_MANY_OPEN_FILES,    /* (18) Number of open files > _FS_SHARE */
+    FR_INVALID_PARAMETER    /* (19) Given parameter is invalid */
+} FRESULT;
+
+
+
+/*--------------------------------------------------------------*/
+/* FatFs module application interface                           */
+
+FRESULT f_mount (BYTE, FATFS*);                        /* Mount/Unmount a logical drive */
+FRESULT f_open (FIL*, const TCHAR*, BYTE);            /* Open or create a file */
+FRESULT f_read (FIL*, void*, UINT, UINT*);            /* Read data from a file */
+FRESULT f_lseek (FIL*, DWORD);                        /* Move file pointer of a file object */
+FRESULT f_close (FIL*);                                /* Close an open file object */
+FRESULT f_opendir (FATFS_DIR*, const TCHAR*);                /* Open an existing directory */
+FRESULT f_readdir (FATFS_DIR*, FILINFO*);                    /* Read a directory item */
+FRESULT f_stat (const TCHAR*, FILINFO*);            /* Get file status */
+FRESULT f_write (FIL*, const void*, UINT, UINT*);    /* Write data to a file */
+FRESULT f_getfree (const TCHAR*, DWORD*, FATFS**);    /* Get number of free clusters on the drive */
+FRESULT f_truncate (FIL*);                            /* Truncate file */
+FRESULT f_sync (FIL*);                                /* Flush cached data of a writing file */
+FRESULT f_unlink (const TCHAR*);                    /* Delete an existing file or directory */
+FRESULT    f_mkdir (const TCHAR*);                        /* Create a new directory */
+FRESULT f_chmod (const TCHAR*, BYTE, BYTE);            /* Change attriburte of the file/dir */
+FRESULT f_utime (const TCHAR*, const FILINFO*);        /* Change timestamp of the file/dir */
+FRESULT f_rename (const TCHAR*, const TCHAR*);        /* Rename/Move a file or directory */
+FRESULT f_chdrive (BYTE);                            /* Change current drive */
+FRESULT f_chdir (const TCHAR*);                        /* Change current directory */
+FRESULT f_getcwd (TCHAR*, UINT);                    /* Get current directory */
+FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*);    /* Forward data to the stream */
+FRESULT f_mkfs (BYTE, BYTE, UINT);                    /* Create a file system on the drive */
+FRESULT    f_fdisk (BYTE, const DWORD[], void*);        /* Divide a physical drive into some partitions */
+int f_putc (TCHAR, FIL*);                            /* Put a character to the file */
+int f_puts (const TCHAR*, FIL*);                    /* Put a string to the file */
+int f_printf (FIL*, const TCHAR*, ...);                /* Put a formatted string to the file */
+TCHAR* f_gets (TCHAR*, int, FIL*);                    /* Get a string from the file */
+
+#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0)
+#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0)
+#define f_tell(fp) ((fp)->fptr)
+#define f_size(fp) ((fp)->fsize)
+
+#ifndef EOF
+#define EOF (-1)
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Additional user defined functions                            */
+
+/* RTC function */
+#if !_FS_READONLY
+DWORD get_fattime (void);
+#endif
+
+/* Unicode support functions */
+#if _USE_LFN                        /* Unicode - OEM code conversion */
+WCHAR ff_convert (WCHAR, UINT);        /* OEM-Unicode bidirectional conversion */
+WCHAR ff_wtoupper (WCHAR);            /* Unicode upper-case conversion */
+#if _USE_LFN == 3                    /* Memory functions */
+void* ff_memalloc (UINT);            /* Allocate memory block */
+void ff_memfree (void*);            /* Free memory block */
+#endif
+#endif
+
+/* Sync functions */
+#if _FS_REENTRANT
+int ff_cre_syncobj (BYTE, _SYNC_t*);/* Create a sync object */
+int ff_req_grant (_SYNC_t);            /* Lock sync object */
+void ff_rel_grant (_SYNC_t);        /* Unlock sync object */
+int ff_del_syncobj (_SYNC_t);        /* Delete a sync object */
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Flags and offset address                                     */
+
+
+/* File access control and file status flags (FIL.flag) */
+
+#define    FA_READ                0x01
+#define    FA_OPEN_EXISTING    0x00
+#define FA__ERROR            0x80
+
+#if !_FS_READONLY
+#define    FA_WRITE            0x02
+#define    FA_CREATE_NEW        0x04
+#define    FA_CREATE_ALWAYS    0x08
+#define    FA_OPEN_ALWAYS        0x10
+#define FA__WRITTEN            0x20
+#define FA__DIRTY            0x40
+#endif
+
+
+/* FAT sub type (FATFS.fs_type) */
+
+#define FS_FAT12    1
+#define FS_FAT16    2
+#define FS_FAT32    3
+
+
+/* File attribute bits for directory entry */
+
+#define    AM_RDO    0x01    /* Read only */
+#define    AM_HID    0x02    /* Hidden */
+#define    AM_SYS    0x04    /* System */
+#define    AM_VOL    0x08    /* Volume label */
+#define AM_LFN    0x0F    /* LFN entry */
+#define AM_DIR    0x10    /* Directory */
+#define AM_ARC    0x20    /* Archive */
+#define AM_MASK    0x3F    /* Mask of defined bits */
+
+
+/* Fast seek feature */
+#define CREATE_LINKMAP    0xFFFFFFFF
+
+
+
+/*--------------------------------*/
+/* Multi-byte word access macros  */
+
+#if _WORD_ACCESS == 1    /* Enable word access to the FAT structure */
+#define    LD_WORD(ptr)        (WORD)(*(WORD*)(BYTE*)(ptr))
+#define    LD_DWORD(ptr)        (DWORD)(*(DWORD*)(BYTE*)(ptr))
+#define    ST_WORD(ptr,val)    *(WORD*)(BYTE*)(ptr)=(WORD)(val)
+#define    ST_DWORD(ptr,val)    *(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
+#else                    /* Use byte-by-byte access to the FAT structure */
+#define    LD_WORD(ptr)        (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
+#define    LD_DWORD(ptr)        (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr))
+#define    ST_WORD(ptr,val)    *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8)
+#define    ST_DWORD(ptr,val)    *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FATFS */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/ffconf.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,190 @@
+/*---------------------------------------------------------------------------/
+/  FatFs - FAT file system module configuration file  R0.09  (C)ChaN, 2011
+/----------------------------------------------------------------------------/
+/
+/ CAUTION! Do not forget to make clean the project after any changes to
+/ the configuration options.
+/
+/----------------------------------------------------------------------------*/
+#ifndef _FFCONF
+#define _FFCONF 6502    /* Revision ID */
+
+
+/*---------------------------------------------------------------------------/
+/ Functions and Buffer Configurations
+/----------------------------------------------------------------------------*/
+
+#define    _FS_TINY        1    /* 0:Normal or 1:Tiny */
+/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system
+/  object instead of the sector buffer in the individual file object for file
+/  data transfer. This reduces memory consumption 512 bytes each file object. */
+
+
+#define _FS_READONLY    0    /* 0:Read/Write or 1:Read only */
+/* Setting _FS_READONLY to 1 defines read only configuration. This removes
+/  writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename,
+/  f_truncate and useless f_getfree. */
+
+
+#define _FS_MINIMIZE    0    /* 0 to 3 */
+/* The _FS_MINIMIZE option defines minimization level to remove some functions.
+/
+/   0: Full function.
+/   1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename
+/      are removed.
+/   2: f_opendir and f_readdir are removed in addition to 1.
+/   3: f_lseek is removed in addition to 2. */
+
+
+#define    _USE_STRFUNC    0    /* 0:Disable or 1-2:Enable */
+/* To enable string functions, set _USE_STRFUNC to 1 or 2. */
+
+
+#define    _USE_MKFS        1    /* 0:Disable or 1:Enable */
+/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */
+
+
+#define    _USE_FORWARD    0    /* 0:Disable or 1:Enable */
+/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */
+
+
+#define    _USE_FASTSEEK    0    /* 0:Disable or 1:Enable */
+/* To enable fast seek feature, set _USE_FASTSEEK to 1. */
+
+
+
+/*---------------------------------------------------------------------------/
+/ Locale and Namespace Configurations
+/----------------------------------------------------------------------------*/
+
+#define _CODE_PAGE    858
+/* The _CODE_PAGE specifies the OEM code page to be used on the target system.
+/  Incorrect setting of the code page can cause a file open failure.
+/
+/   932  - Japanese Shift-JIS (DBCS, OEM, Windows)
+/   936  - Simplified Chinese GBK (DBCS, OEM, Windows)
+/   949  - Korean (DBCS, OEM, Windows)
+/   950  - Traditional Chinese Big5 (DBCS, OEM, Windows)
+/   1250 - Central Europe (Windows)
+/   1251 - Cyrillic (Windows)
+/   1252 - Latin 1 (Windows)
+/   1253 - Greek (Windows)
+/   1254 - Turkish (Windows)
+/   1255 - Hebrew (Windows)
+/   1256 - Arabic (Windows)
+/   1257 - Baltic (Windows)
+/   1258 - Vietnam (OEM, Windows)
+/   437  - U.S. (OEM)
+/   720  - Arabic (OEM)
+/   737  - Greek (OEM)
+/   775  - Baltic (OEM)
+/   850  - Multilingual Latin 1 (OEM)
+/   858  - Multilingual Latin 1 + Euro (OEM)
+/   852  - Latin 2 (OEM)
+/   855  - Cyrillic (OEM)
+/   866  - Russian (OEM)
+/   857  - Turkish (OEM)
+/   862  - Hebrew (OEM)
+/   874  - Thai (OEM, Windows)
+/    1    - ASCII only (Valid for non LFN cfg.)
+*/
+
+
+#define    _USE_LFN    1        /* 0 to 3 */
+#define    _MAX_LFN    255        /* Maximum LFN length to handle (12 to 255) */
+/* The _USE_LFN option switches the LFN support.
+/
+/   0: Disable LFN feature. _MAX_LFN and _LFN_UNICODE have no effect.
+/   1: Enable LFN with static working buffer on the BSS. Always NOT reentrant.
+/   2: Enable LFN with dynamic working buffer on the STACK.
+/   3: Enable LFN with dynamic working buffer on the HEAP.
+/
+/  The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. To enable LFN,
+/  Unicode handling functions ff_convert() and ff_wtoupper() must be added
+/  to the project. When enable to use heap, memory control functions
+/  ff_memalloc() and ff_memfree() must be added to the project. */
+
+
+#define    _LFN_UNICODE    0    /* 0:ANSI/OEM or 1:Unicode */
+/* To switch the character code set on FatFs API to Unicode,
+/  enable LFN feature and set _LFN_UNICODE to 1. */
+
+
+#define _FS_RPATH        0    /* 0 to 2 */
+/* The _FS_RPATH option configures relative path feature.
+/
+/   0: Disable relative path feature and remove related functions.
+/   1: Enable relative path. f_chdrive() and f_chdir() are available.
+/   2: f_getcwd() is available in addition to 1.
+/
+/  Note that output of the f_readdir fnction is affected by this option. */
+
+
+
+/*---------------------------------------------------------------------------/
+/ Physical Drive Configurations
+/----------------------------------------------------------------------------*/
+
+#define _VOLUMES    4
+/* Number of volumes (logical drives) to be used. */
+
+
+#define    _MAX_SS        512        /* 512, 1024, 2048 or 4096 */
+/* Maximum sector size to be handled.
+/  Always set 512 for memory card and hard disk but a larger value may be
+/  required for on-board flash memory, floppy disk and optical disk.
+/  When _MAX_SS is larger than 512, it configures FatFs to variable sector size
+/  and GET_SECTOR_SIZE command must be implememted to the disk_ioctl function. */
+
+
+#define    _MULTI_PARTITION    0    /* 0:Single partition, 1/2:Enable multiple partition */
+/* When set to 0, each volume is bound to the same physical drive number and
+/ it can mount only first primaly partition. When it is set to 1, each volume
+/ is tied to the partitions listed in VolToPart[]. */
+
+
+#define    _USE_ERASE    0    /* 0:Disable or 1:Enable */
+/* To enable sector erase feature, set _USE_ERASE to 1. CTRL_ERASE_SECTOR command
+/  should be added to the disk_ioctl functio. */
+
+
+
+/*---------------------------------------------------------------------------/
+/ System Configurations
+/----------------------------------------------------------------------------*/
+
+#define _WORD_ACCESS    0    /* 0 or 1 */
+/* Set 0 first and it is always compatible with all platforms. The _WORD_ACCESS
+/  option defines which access method is used to the word data on the FAT volume.
+/
+/   0: Byte-by-byte access.
+/   1: Word access. Do not choose this unless following condition is met.
+/
+/  When the byte order on the memory is big-endian or address miss-aligned word
+/  access results incorrect behavior, the _WORD_ACCESS must be set to 0.
+/  If it is not the case, the value can also be set to 1 to improve the
+/  performance and code size.
+*/
+
+
+/* A header file that defines sync object types on the O/S, such as
+/  windows.h, ucos_ii.h and semphr.h, must be included prior to ff.h. */
+
+#define _FS_REENTRANT    0        /* 0:Disable or 1:Enable */
+#define _FS_TIMEOUT        1000    /* Timeout period in unit of time ticks */
+#define    _SYNC_t            HANDLE    /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */
+
+/* The _FS_REENTRANT option switches the reentrancy (thread safe) of the FatFs module.
+/
+/   0: Disable reentrancy. _SYNC_t and _FS_TIMEOUT have no effect.
+/   1: Enable reentrancy. Also user provided synchronization handlers,
+/      ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj
+/      function must be added to the project. */
+
+
+#define    _FS_SHARE    0    /* 0:Disable or >=1:Enable */
+/* To enable file shareing feature, set _FS_SHARE to 1 or greater. The value
+   defines how many files can be opened simultaneously. */
+
+
+#endif /* _FFCONFIG */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/integer.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,37 @@
+/*-------------------------------------------*/
+/* Integer type definitions for FatFs module */
+/*-------------------------------------------*/
+
+#ifndef _INTEGER
+#define _INTEGER
+
+#ifdef _WIN32    /* FatFs development platform */
+
+#include <windows.h>
+#include <tchar.h>
+
+#else            /* Embedded platform */
+
+/* These types must be 16-bit, 32-bit or larger integer */
+typedef int                INT;
+typedef unsigned int    UINT;
+
+/* These types must be 8-bit integer */
+typedef char            CHAR;
+typedef unsigned char    UCHAR;
+typedef unsigned char    BYTE;
+
+/* These types must be 16-bit integer */
+typedef short            SHORT;
+typedef unsigned short    USHORT;
+typedef unsigned short    WORD;
+typedef unsigned short    WCHAR;
+
+/* These types must be 32-bit integer */
+typedef long            LONG;
+typedef unsigned long    ULONG;
+typedef unsigned long    DWORD;
+
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFileSystem/option/ccsbcs.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,540 @@
+/*------------------------------------------------------------------------*/
+/* Unicode - Local code bidirectional converter  (C)ChaN, 2009            */
+/* (SBCS code pages)                                                      */
+/*------------------------------------------------------------------------*/
+/*  437   U.S. (OEM)
+/   720   Arabic (OEM)
+/   1256  Arabic (Windows)
+/   737   Greek (OEM)
+/   1253  Greek (Windows)
+/   1250  Central Europe (Windows)
+/   775   Baltic (OEM)
+/   1257  Baltic (Windows)
+/   850   Multilingual Latin 1 (OEM)
+/   852   Latin 2 (OEM)
+/   1252  Latin 1 (Windows)
+/   855   Cyrillic (OEM)
+/   1251  Cyrillic (Windows)
+/   866   Russian (OEM)
+/   857   Turkish (OEM)
+/   1254  Turkish (Windows)
+/   858   Multilingual Latin 1 + Euro (OEM)
+/   862   Hebrew (OEM)
+/   1255  Hebrew (Windows)
+/   874   Thai (OEM, Windows)
+/   1258  Vietnam (OEM, Windows)
+*/
+
+#include "../ff.h"
+
+
+#if _CODE_PAGE == 437
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP437(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
+    0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+    0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 720
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP720(0x80-0xFF) to Unicode conversion table */
+    0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9,
+    0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,
+    0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
+    0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642,
+    0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,
+    0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0xO650, 0x2248,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 737
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP737(0x80-0xFF) to Unicode conversion table */
+    0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398,
+    0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
+    0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9,
+    0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
+    0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
+    0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD,
+    0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,
+    0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 775
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP775(0x80-0xFF) to Unicode conversion table */
+    0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107,
+    0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A,
+    0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,
+    0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6,
+    0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118,
+    0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,
+    0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B,
+    0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144,
+    0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,
+    0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E,
+    0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 850
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP850(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
+    0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,
+    0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+    0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
+    0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
+    0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
+    0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 852
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP852(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7,
+    0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
+    0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A,
+    0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E,
+    0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A,
+    0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE,
+    0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,
+    0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161,
+    0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,
+    0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8,
+    0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 855
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP855(0x80-0xFF) to Unicode conversion table */
+    0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404,
+    0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
+    0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C,
+    0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
+    0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414,
+    0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438,
+    0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E,
+    0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,
+    0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443,
+    0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,
+    0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D,
+    0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 857
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP857(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F,
+    0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
+    0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE,
+    0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+    0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000,
+    0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,
+    0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
+    0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 858
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP858(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
+    0x00A9, 0x2563, 0x2551, 0x2557, 0x2550, 0x00A2, 0x00A5, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE,
+    0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00C6, 0x00CC, 0x2580,
+    0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
+    0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
+    0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
+    0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 862
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP862(0x80-0xFF) to Unicode conversion table */
+    0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
+    0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+    0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
+    0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
+    0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+    0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 866
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP866(0x80-0xFF) to Unicode conversion table */
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+    0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+    0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+    0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+    0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+    0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 874
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP874(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07,
+    0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F,
+    0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17,
+    0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F,
+    0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
+    0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,
+    0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,
+    0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F,
+    0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47,
+    0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F,
+    0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57,
+    0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+#elif _CODE_PAGE == 1250
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1250(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A,
+    0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B,
+    0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C,
+    0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,
+    0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
+    0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,
+    0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
+    0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7,
+    0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
+    0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7,
+    0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
+};
+
+#elif _CODE_PAGE == 1251
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1251(0x80-0xFF) to Unicode conversion table */
+    0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
+    0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x2111, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
+    0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,
+    0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
+    0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,
+    0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+    0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+    0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+    0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+    0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F
+};
+
+#elif _CODE_PAGE == 1252
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1252(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178,
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+    0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+    0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
+};
+
+#elif _CODE_PAGE == 1253
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1253(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x0000, 0x2030, 0x0000, 0x2039, 0x000C, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7,
+    0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,
+    0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+    0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
+    0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7,
+    0x03A8, 0x03A9, 0x03AA, 0x03AD, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
+    0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
+    0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+    0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
+    0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000
+};
+
+#elif _CODE_PAGE == 1254
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1254(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x210A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+    0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+    0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x0130, 0x015E, 0x00DF,
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+    0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF
+};
+
+#elif _CODE_PAGE == 1255
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1255(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x02DC, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,
+    0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
+    0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3,
+    0x05F4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
+    0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+    0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
+    0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000
+};
+
+#elif _CODE_PAGE == 1256
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1256(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688,
+    0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA,
+    0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F,
+    0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627,
+    0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
+    0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7,
+    0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0640, 0x0642, 0x0643,
+    0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF,
+    0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7,
+    0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2
+}
+
+#elif _CODE_PAGE == 1257
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1257(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000,
+    0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x0000, 0x00A6, 0x00A7,
+    0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
+    0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112,
+    0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
+    0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7,
+    0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
+    0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113,
+    0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
+    0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7,
+    0x0173, 0x014E, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9
+};
+
+#elif _CODE_PAGE == 1258
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {    /*  CP1258(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0000, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x02DC, 0x2122, 0x0000, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF,
+    0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7,
+    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF,
+    0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF,
+    0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7,
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF
+};
+
+#endif
+
+
+#if !_TBLDEF || !_USE_LFN
+#error This file is not needed in current configuration. Remove from the project.
+#endif
+
+
+WCHAR ff_convert (    /* Converted character, Returns zero on error */
+    WCHAR    src,    /* Character code to be converted */
+    UINT    dir        /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */
+)
+{
+    WCHAR c;
+
+
+    if (src < 0x80) {    /* ASCII */
+        c = src;
+
+    } else {
+        if (dir) {        /* OEMCP to Unicode */
+            c = (src >= 0x100) ? 0 : Tbl[src - 0x80];
+
+        } else {        /* Unicode to OEMCP */
+            for (c = 0; c < 0x80; c++) {
+                if (src == Tbl[c]) break;
+            }
+            c = (c + 0x80) & 0xFF;
+        }
+    }
+
+    return c;
+}
+
+
+WCHAR ff_wtoupper (    /* Upper converted character */
+    WCHAR chr        /* Input character */
+)
+{
+    static const WCHAR tbl_lower[] = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xA1, 0x00A2, 0x00A3, 0x00A5, 0x00AC, 0x00AF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x0FF, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x10D, 0x10F, 0x111, 0x113, 0x115, 0x117, 0x119, 0x11B, 0x11D, 0x11F, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12B, 0x12D, 0x12F, 0x131, 0x133, 0x135, 0x137, 0x13A, 0x13C, 0x13E, 0x140, 0x142, 0x144, 0x146, 0x148, 0x14B, 0x14D, 0x14F, 0x151, 0x153, 0x155, 0x157, 0x159, 0x15B, 0x15D, 0x15F, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16B, 0x16D, 0x16F, 0x171, 0x173, 0x175, 0x177, 0x17A, 0x17C, 0x17E, 0x192, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45A, 0x45B, 0x45C, 0x45E, 0x45F, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0 };
+    static const WCHAR tbl_upper[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x21, 0xFFE0, 0xFFE1, 0xFFE5, 0xFFE2, 0xFFE3, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10A, 0x10C, 0x10E, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11A, 0x11C, 0x11E, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12A, 0x12C, 0x12E, 0x130, 0x132, 0x134, 0x136, 0x139, 0x13B, 0x13D, 0x13F, 0x141, 0x143, 0x145, 0x147, 0x14A, 0x14C, 0x14E, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15A, 0x15C, 0x15E, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16A, 0x16C, 0x16E, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17B, 0x17D, 0x191, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B, 0x40C, 0x40E, 0x40F, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A, 0x216B, 0x216C, 0x216D, 0x216E, 0x216F, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0 };
+    int i;
+
+
+    for (i = 0; tbl_lower[i] && chr != tbl_lower[i]; i++) ;
+
+    return tbl_lower[i] ? tbl_upper[i] : chr;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HighSpeedAnalogIn.lib	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/shintamainjp/code/HighSpeedAnalogIn/#db55359719e7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HighSpeedAnalogIn/HighSpeedAnalogIn.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,292 @@
+
+#include "HighSpeedAnalogIn.h"
+
+HighSpeedAnalogIn *HighSpeedAnalogIn::instance;
+int HighSpeedAnalogIn::refcnt = 0;
+
+HighSpeedAnalogIn::HighSpeedAnalogIn(PinName pin0, PinName pin1, PinName pin2, PinName pin3, PinName pin4, PinName pin5) {
+
+    refcnt++;
+    if (refcnt > 1) {
+        error("Please do not use over an object.");
+    }
+
+    static const int sample_rate = 200000;
+    static const int cclk_div = 1;
+
+    int adc_clk_freq = CLKS_PER_SAMPLE * sample_rate;
+    int m = (LPC_SC->PLL0CFG & 0xFFFF) + 1;
+    int n = (LPC_SC->PLL0CFG >> 16) + 1;
+    int cclkdiv = LPC_SC->CCLKCFG + 1;
+    int Fcco = (2 * m * XTAL_FREQ) / n;
+    int cclk = Fcco / cclkdiv;
+
+    LPC_SC->PCONP |= (1 << 12);
+    LPC_SC->PCLKSEL0 &= ~(0x3 << 24);
+    switch (cclk_div) {
+        case 1:
+            LPC_SC->PCLKSEL0 |= 0x1 << 24;
+            break;
+        case 2:
+            LPC_SC->PCLKSEL0 |= 0x2 << 24;
+            break;
+        case 4:
+            LPC_SC->PCLKSEL0 |= 0x0 << 24;
+            break;
+        case 8:
+            LPC_SC->PCLKSEL0 |= 0x3 << 24;
+            break;
+        default:
+            fprintf(stderr, "Warning: ADC CCLK clock divider must be 1, 2, 4 or 8. %u supplied.\n", cclk_div);
+            fprintf(stderr, "Defaulting to 1.\n");
+            LPC_SC->PCLKSEL0 |= 0x1 << 24;
+            break;
+    }
+    int pclk = cclk / cclk_div;
+    int clock_div = pclk / adc_clk_freq;
+
+    if (clock_div > 0xFF) {
+        fprintf(stderr, "Warning: Clock division is %u which is above 255 limit. Re-Setting at limit.\n", clock_div);
+        clock_div = 0xFF;
+    }
+    if (clock_div == 0) {
+        fprintf(stderr, "Warning: Clock division is 0. Re-Setting to 1.\n");
+        clock_div = 1;
+    }
+
+    int _adc_clk_freq = pclk / clock_div;
+    if (_adc_clk_freq > MAX_ADC_CLOCK) {
+        fprintf(stderr, "Warning: Actual ADC sample rate of %u which is above %u limit\n", _adc_clk_freq / CLKS_PER_SAMPLE, MAX_ADC_CLOCK / CLKS_PER_SAMPLE);
+        int max_div = 1;
+        while ((pclk / max_div) > MAX_ADC_CLOCK) {
+            max_div++;
+        }
+        fprintf(stderr, "Maximum recommended sample rate is %u\n", (pclk / max_div) / CLKS_PER_SAMPLE);
+    }
+
+    LPC_ADC->ADCR = ((clock_div - 1) << 8) | (1 << 21);
+    LPC_ADC->ADCR &= ~0xFF;
+
+    for (int i = 0; i < 8; i++) {
+        _adc_data[i] = 0;
+    }
+
+    // Attach IRQ
+    instance = this;
+    NVIC_SetVector(ADC_IRQn, (uint32_t)&static_adcisr);
+
+    // Disable global interrupt
+    LPC_ADC->ADINTEN &= ~0x100;
+
+    // Clock frequency.
+    printf("Clock frequency:%d\n", _adc_clk_freq);
+
+    // Actual sampling rate.
+    printf("Actual sampling rate:%d\n", _adc_clk_freq / CLKS_PER_SAMPLE);
+    
+    int tmp = LPC_ADC->ADCR & ~(0x0F << 24);
+    tmp |= ((0x0 & 7) << 24) | ((0x0 & 1) << 27);
+    LPC_ADC->ADCR = tmp;
+    LPC_ADC->ADCR |= (1 << 16);
+
+    if (pin0 != NC) setup(pin0, 1);
+    if (pin1 != NC) setup(pin1, 1);
+    if (pin2 != NC) setup(pin2, 1);
+    if (pin3 != NC) setup(pin3, 1);
+    if (pin4 != NC) setup(pin4, 1);
+    if (pin5 != NC) setup(pin5, 1);
+
+    interrupt_state(pin0, 1);
+}
+
+HighSpeedAnalogIn::~HighSpeedAnalogIn() {
+}
+
+void HighSpeedAnalogIn::static_adcisr(void) {
+    instance->adcisr();
+}
+
+void HighSpeedAnalogIn::adcisr(void) {
+    uint32_t stat = LPC_ADC->ADSTAT;
+    // Scan channels for over-run or done and update array
+    if (stat & 0x0101) _adc_data[0] = LPC_ADC->ADDR0;
+    if (stat & 0x0202) _adc_data[1] = LPC_ADC->ADDR1;
+    if (stat & 0x0404) _adc_data[2] = LPC_ADC->ADDR2;
+    if (stat & 0x0808) _adc_data[3] = LPC_ADC->ADDR3;
+    if (stat & 0x1010) _adc_data[4] = LPC_ADC->ADDR4;
+    if (stat & 0x2020) _adc_data[5] = LPC_ADC->ADDR5;
+    if (stat & 0x4040) _adc_data[6] = LPC_ADC->ADDR6;
+    if (stat & 0x8080) _adc_data[7] = LPC_ADC->ADDR7;
+}
+
+int HighSpeedAnalogIn::get_channel(PinName pin) {
+    int ch;
+    switch (pin) {
+        case p15:// =p0.23 of LPC1768
+            ch = 0;
+            break;
+        case p16:// =p0.24 of LPC1768
+            ch = 1;
+            break;
+        case p17:// =p0.25 of LPC1768
+            ch = 2;
+            break;
+        case p18:// =p0.26 of LPC1768
+            ch = 3;
+            break;
+        case p19:// =p1.30 of LPC1768
+            ch = 4;
+            break;
+        case p20:// =p1.31 of LPC1768
+            ch = 5;
+            break;
+        default:
+            ch = 0;
+            break;
+    }
+    return ch;
+}
+
+uint32_t HighSpeedAnalogIn::get_data(PinName pin) {
+    // If in burst mode and at least one interrupt enabled then
+    // take all values from _adc_data
+    if (LPC_ADC->ADINTEN & 0x3F) {
+        return (_adc_data[get_channel(pin)]);
+    } else {
+        // Return current register value or last value from interrupt
+        switch (pin) {
+            case p15:// =p0.23 of LPC1768
+                return ((LPC_ADC->ADINTEN & 0x01) ? _adc_data[0] : LPC_ADC->ADDR0);
+            case p16:// =p0.24 of LPC1768
+                return ((LPC_ADC->ADINTEN & 0x02) ? _adc_data[1] : LPC_ADC->ADDR1);
+            case p17:// =p0.25 of LPC1768
+                return ((LPC_ADC->ADINTEN & 0x04) ? _adc_data[2] : LPC_ADC->ADDR2);
+            case p18:// =p0.26 of LPC1768:
+                return ((LPC_ADC->ADINTEN & 0x08) ? _adc_data[3] : LPC_ADC->ADDR3);
+            case p19:// =p1.30 of LPC1768
+                return ((LPC_ADC->ADINTEN & 0x10) ? _adc_data[4] : LPC_ADC->ADDR4);
+            case p20:// =p1.31 of LPC1768
+                return ((LPC_ADC->ADINTEN & 0x20) ? _adc_data[5] : LPC_ADC->ADDR5);
+            default:
+                return 0;
+        }
+    }
+}
+
+// Enable or disable an HighSpeedAnalogIn pin
+void HighSpeedAnalogIn::setup(PinName pin, int state) {
+    int ch = get_channel(pin);
+    if ((state & 1) == 1) {
+        switch (pin) {
+            case p15:// =p0.23 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14);
+                LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 14;
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14);
+                LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 14;
+                break;
+            case p16:// =p0.24 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16);
+                LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 16;
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16);
+                LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 16;
+                break;
+            case p17:// =p0.25 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18);
+                LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 18;
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18);
+                LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 18;
+                break;
+            case p18:// =p0.26 of LPC1768:
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20);
+                LPC_PINCON->PINSEL1 |= (unsigned int)0x1 << 20;
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20);
+                LPC_PINCON->PINMODE1 |= (unsigned int)0x2 << 20;
+                break;
+            case p19:// =p1.30 of LPC1768
+                LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28);
+                LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 28;
+                LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28);
+                LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 28;
+                break;
+            case p20:// =p1.31 of LPC1768
+                LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30);
+                LPC_PINCON->PINSEL3 |= (unsigned int)0x3 << 30;
+                LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30);
+                LPC_PINCON->PINMODE3 |= (unsigned int)0x2 << 30;
+                break;
+            default:
+                error("Invalid pin.");
+                break;
+        }
+        // Select channel
+        LPC_ADC->ADCR |= (1 << ch);
+    } else {
+        switch (pin) {
+            case p15://=p0.23 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 14);
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 14);
+                break;
+            case p16://=p0.24 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 16);
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 16);
+                break;
+            case p17://=p0.25 of LPC1768
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 18);
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 18);
+                break;
+            case p18://=p0.26 of LPC1768:
+                LPC_PINCON->PINSEL1 &= ~((unsigned int)0x3 << 20);
+                LPC_PINCON->PINMODE1 &= ~((unsigned int)0x3 << 20);
+                break;
+            case p19://=p1.30 of LPC1768
+                LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 28);
+                LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 28);
+                break;
+            case p20://=p1.31 of LPC1768
+                LPC_PINCON->PINSEL3 &= ~((unsigned int)0x3 << 30);
+                LPC_PINCON->PINMODE3 &= ~((unsigned int)0x3 << 30);
+                break;
+            default:
+                error("Invalid pin.");
+                break;
+        }
+        LPC_ADC->ADCR &= ~(1 << ch);
+    }
+}
+
+void HighSpeedAnalogIn::interrupt_state(PinName pin, int state) {
+    int ch = get_channel(pin);
+    if (state == 1) {
+        LPC_ADC->ADINTEN &= ~0x100;
+        LPC_ADC->ADINTEN |= 1 << ch;
+        /* Enable the HighSpeedAnalogIn Interrupt */
+        NVIC_EnableIRQ(ADC_IRQn);
+    } else {
+        LPC_ADC->ADINTEN &= ~(1 << ch);
+        //Disable interrrupt if no active pins left
+        if ((LPC_ADC->ADINTEN & 0xFF) == 0)
+            NVIC_DisableIRQ(ADC_IRQn);
+    }
+}
+
+float HighSpeedAnalogIn::read(PinName pin) {
+    /*
+     * Reset DONE and OVERRUN.
+     *
+     * bit 31 : DONE
+     * bit 30 : OVERRUN
+     */
+    _adc_data[get_channel(pin)] &= ~(((uint32_t)0x01 << 31) | ((uint32_t)0x01 << 30));
+    return (float)((get_data(pin) >> 4) & 0xFFF) / (float)0xFFF;
+}
+
+unsigned short HighSpeedAnalogIn::read_u16(PinName pin) {
+    /*
+     * Reset DONE and OVERRUN.
+     *
+     * bit 31 : DONE
+     * bit 30 : OVERRUN
+     */
+    _adc_data[get_channel(pin)] &= ~(((uint32_t)0x01 << 31) | ((uint32_t)0x01 << 30));
+    return ((get_data(pin) >> 4) & 0xFFF);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HighSpeedAnalogIn/HighSpeedAnalogIn.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,35 @@
+#ifndef HIGH_SPEED_ANALOG_IN_H
+#define HIGH_SPEED_ANALOG_IN_H
+
+#include "mbed.h"
+
+class HighSpeedAnalogIn {
+public:
+
+    HighSpeedAnalogIn(PinName pin0, PinName pin1 = NC, PinName pin2 = NC, PinName pin3 = NC, PinName pin4 = NC, PinName pin5 = NC);
+    ~HighSpeedAnalogIn();
+    float read(PinName pin);
+    unsigned short read_u16(PinName pin);
+
+private:
+
+    HighSpeedAnalogIn();
+    uint32_t _adc_data[8];
+
+    static const int XTAL_FREQ = 12000000;
+    static const int MAX_ADC_CLOCK = 13000000;
+    static const int CLKS_PER_SAMPLE = 64;
+    
+    static HighSpeedAnalogIn *instance;
+    static int refcnt;
+
+    static void static_adcisr(void);
+
+    int get_channel(PinName pin);
+    uint32_t get_data(PinName pin);
+    void adcisr(void);
+    void setup(PinName pin, int state);
+    void interrupt_state(PinName pin, int state);
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TB6612FNG2.lib	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/jksoft/code/TB6612FNG2/#051a7ecff13e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TB6612FNG2/TB6612.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,55 @@
+/**
+ * Motor Driver TB6612 Control Library
+ *
+ * -- TB6612 is a device of the TOSHIBA. 
+ *
+ * Copyright (C) 2012 Junichi Katsu (JKSOFT) 
+ */
+
+
+#include "TB6612.h"
+
+// TB6612 Class Constructor
+TB6612::TB6612(PinName pwm, PinName fwd, PinName rev):
+        _pwm(pwm), _fwd(fwd), _rev(rev) {
+
+    _fwd = 0;
+    _rev = 0;
+    _pwm = 0.0;
+    _pwm.period(0.001);
+}
+
+// Speed Control
+//  arg
+//   int speed -100 -- 0 -- 100
+void TB6612::speed(int speed) {
+        
+    if( speed > 0 )
+    {
+        _pwm = ((float)speed) / 100.0;
+        _fwd = 1;
+        _rev = 0;
+    }
+    else if( speed < 0 )
+    {
+        _pwm = -((float)speed) / 100.0;
+        _fwd = 0;
+        _rev = 1;
+    }
+    else
+    {
+        _fwd = 1;
+        _rev = 1;
+    }
+}
+
+
+// Speed Control with time-out
+//  arg
+//   int speed -100 -- 0 -- 100
+//   int time  0
+void TB6612::move(int sspeed , int time)
+{
+    speed(sspeed);
+    wait_ms(time);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TB6612FNG2/TB6612.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,30 @@
+/**
+ * Motor Driver TB6612 Control Library
+ *
+ * -- TB6612 is a device of the rohm. 
+ *
+ * Copyright (C) 2012 Junichi Katsu (JKSOFT) 
+ */
+
+#ifndef MBED_TB6612_H
+#define MBED_TB6612_H
+
+#include "mbed.h"
+
+class TB6612 {
+public:
+    TB6612(PinName pwm, PinName fwd, PinName rev);
+    void speed(int speed);
+    void move(int speed , int time);
+    void operator= ( int value )
+    {
+        speed(value);
+    }
+    
+protected:
+    PwmOut _pwm;
+    DigitalOut _fwd;
+    DigitalOut _rev;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/att.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,913 @@
+/*
+ * Copyright (C) 2011-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "att.h"
+
+// from src/utils.
+#define READ_BT_16( buffer, pos) ( ((uint16_t) buffer[pos]) | (((uint16_t)buffer[pos+1]) << 8))
+
+// Buetooth Base UUID 00000000-0000-1000-8000-00805F9B34FB in little endian
+static const uint8_t bluetooth_base_uuid[] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static void bt_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){
+    buffer[pos++] = value;
+    buffer[pos++] = value >> 8;
+}
+
+static void hexdump2(void const *data, int size){
+    int i;
+    for (i=0; i<size;i++){
+        printf("%02X ", ((uint8_t *)data)[i]);
+    }
+    printf("\n");
+}
+
+static void printUUID128(const uint8_t * uuid){
+    int i;
+    for (i=15; i >= 0 ; i--){
+        printf("%02X", uuid[i]);
+        switch (i){
+            case 4:
+            case 6:
+            case 8:
+            case 10:
+                printf("-");
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+static int is_Bluetooth_Base_UUID(uint8_t const *uuid){
+    if (memcmp(&uuid[0],  &bluetooth_base_uuid[0], 12)) return 0;
+    if (memcmp(&uuid[14], &bluetooth_base_uuid[14], 2)) return 0;
+    return 1;
+    
+}
+
+// ATT Database
+static uint8_t const * att_db = NULL;
+static att_read_callback_t  att_read_callback  = NULL;
+static att_write_callback_t att_write_callback = NULL;
+
+// new java-style iterator
+typedef struct att_iterator {
+    // private
+    uint8_t const * att_ptr;
+    // public
+    uint16_t size;
+    uint16_t flags;
+    uint16_t handle;
+    uint8_t  const * uuid;
+    uint16_t value_len;
+    uint8_t  const * value;
+} att_iterator_t;
+
+void att_iterator_init(att_iterator_t *it){
+    it->att_ptr = att_db;
+}
+
+int att_iterator_has_next(att_iterator_t *it){
+    return it->att_ptr != NULL;
+}
+
+void att_iterator_fetch_next(att_iterator_t *it){
+    it->size   = READ_BT_16(it->att_ptr, 0);
+    if (it->size == 0){
+        it->flags = 0;
+        it->handle = 0;
+        it->uuid = NULL;
+        it->value_len = 0;
+        it->value = NULL;
+        it->att_ptr = NULL;
+        return;
+    }
+    it->flags  = READ_BT_16(it->att_ptr, 2);
+    it->handle = READ_BT_16(it->att_ptr, 4);
+    it->uuid   = &it->att_ptr[6];
+    // handle 128 bit UUIDs
+    if (it->flags & ATT_PROPERTY_UUID128){
+        it->value_len = it->size - 22;
+        it->value  = &it->att_ptr[22];
+    } else {
+        it->value_len = it->size - 8;
+        it->value  = &it->att_ptr[8];
+    }
+    // advance AFTER setting values
+    it->att_ptr += it->size;
+}
+
+int att_iterator_match_uuid16(att_iterator_t *it, uint16_t uuid){
+    if (it->handle == 0) return 0;
+    if (it->flags & ATT_PROPERTY_UUID128){
+        if (!is_Bluetooth_Base_UUID(it->uuid)) return 0;
+        return READ_BT_16(it->uuid, 12) == uuid;
+    }
+    return READ_BT_16(it->uuid, 0)  == uuid;
+}
+
+int att_iterator_match_uuid(att_iterator_t *it, uint8_t *uuid, uint16_t uuid_len){
+    if (it->handle == 0) return 0;
+    // input: UUID16
+    if (uuid_len == 2) {
+        return att_iterator_match_uuid16(it, READ_BT_16(uuid, 0));
+    }
+    // input and db: UUID128 
+    if (it->flags & ATT_PROPERTY_UUID128){
+        return memcmp(it->uuid, uuid, 16) == 0;
+    }
+    // input: UUID128, db: UUID16
+    if (!is_Bluetooth_Base_UUID(uuid)) return 0;
+    return READ_BT_16(uuid, 12) == READ_BT_16(it->uuid, 0);
+}
+
+
+int att_find_handle(att_iterator_t *it, uint16_t handle){
+    att_iterator_init(it);
+    while (att_iterator_has_next(it)){
+        att_iterator_fetch_next(it);
+        if (it->handle != handle) continue;
+        return 1;
+    }
+    return 0;
+}
+
+static void att_update_value_len(att_iterator_t *it){
+    if ((it->flags & ATT_PROPERTY_DYNAMIC) == 0 || !att_read_callback) return;
+    it->value_len = (*att_read_callback)(it->handle, 0, NULL, 0);
+    return;
+}
+
+static int att_copy_value(att_iterator_t *it, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
+    
+    // DYNAMIC 
+    if ((it->flags & ATT_PROPERTY_DYNAMIC) && att_read_callback) {
+        return (*att_read_callback)(it->handle, offset, buffer, buffer_size);
+    }
+    
+    // STATIC
+    uint16_t bytes_to_copy = it->value_len;
+    if (bytes_to_copy > buffer_size){
+        bytes_to_copy = buffer_size;
+    }
+    memcpy(buffer, it->value, bytes_to_copy);
+    return bytes_to_copy;
+}
+
+void att_set_db(uint8_t const * db){
+    att_db = db;
+}
+
+void att_set_read_callback(att_read_callback_t callback){
+    att_read_callback = callback;
+}
+
+void att_set_write_callback(att_write_callback_t callback){
+    att_write_callback = callback;
+}
+
+void att_dump_attributes(void){
+    att_iterator_t it;
+    att_iterator_init(&it);
+    while (att_iterator_has_next(&it)){
+        att_iterator_fetch_next(&it);
+        if (it.handle == 0) {
+            printf("Handle: END\n");
+            return;
+        }
+        printf("Handle: 0x%04x, flags: 0x%04x, uuid: ", it.handle, it.flags);
+        if (it.flags & ATT_PROPERTY_UUID128){
+            printUUID128(it.uuid);
+        } else {
+            printf("%04x", READ_BT_16(it.uuid, 0));
+        }
+        printf(", value_len: %u, value: ", it.value_len);
+        hexdump2(it.value, it.value_len);
+    }
+}
+
+static uint16_t setup_error(uint8_t * response_buffer, uint16_t request, uint16_t handle, uint8_t error_code){
+    response_buffer[0] = ATT_ERROR_RESPONSE;
+    response_buffer[1] = request;
+    bt_store_16(response_buffer, 2, handle);
+    response_buffer[4] = error_code;
+    return 5;
+}
+
+static uint16_t setup_error_atribute_not_found(uint8_t * response_buffer, uint16_t request, uint16_t start_handle){
+    return setup_error(response_buffer, request, start_handle, ATT_ERROR_ATTRIBUTE_NOT_FOUND);
+}
+
+static uint16_t setup_error_invalid_handle(uint8_t * response_buffer, uint16_t request, uint16_t handle){
+    return setup_error(response_buffer, request, handle, ATT_ERROR_ATTRIBUTE_INVALID);
+}
+
+static uint16_t setup_error_invalid_offset(uint8_t * response_buffer, uint16_t request, uint16_t handle){
+    return setup_error(response_buffer, request, handle, ATT_ERROR_INVALID_OFFSET);
+}
+
+//
+// MARK: ATT_EXCHANGE_MTU_REQUEST
+//
+static uint16_t handle_exchange_mtu_request(att_connection_t * att_connection, uint8_t * request_buffer,  uint16_t request_len,
+                                         uint8_t * response_buffer){
+
+    uint16_t client_rx_mtu = READ_BT_16(request_buffer, 1);
+    if (client_rx_mtu < att_connection->mtu){
+        att_connection->mtu = client_rx_mtu;
+    }
+    
+    response_buffer[0] = ATT_EXCHANGE_MTU_RESPONSE;
+    bt_store_16(response_buffer, 1, att_connection->mtu);
+    return 3;
+}
+
+
+//
+// MARK: ATT_FIND_INFORMATION_REQUEST
+//
+// TODO: handle other types then GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID
+//
+static uint16_t handle_find_information_request2(uint8_t * response_buffer, uint16_t response_buffer_size,
+                                           uint16_t start_handle, uint16_t end_handle){
+    
+    printf("ATT_FIND_INFORMATION_REQUEST: from %04X to %04X\n", start_handle, end_handle);
+    
+    uint16_t offset   = 1;
+    uint16_t pair_len = 0;
+    
+    att_iterator_t it;
+    att_iterator_init(&it);
+    while (att_iterator_has_next(&it)){
+        att_iterator_fetch_next(&it);
+        if (!it.handle) break;
+        if (it.handle > end_handle) break;
+        if (it.handle < start_handle) continue;
+        
+        att_update_value_len(&it);
+        
+        // printf("Handle 0x%04x\n", it.handle);
+        
+        // check if value has same len as last one
+        uint16_t this_pair_len = 2 + it.value_len;
+        if (offset > 1){
+            if (pair_len != this_pair_len) {
+                break;
+            }
+        }
+        
+        // first
+        if (offset == 1) {
+            pair_len = this_pair_len;
+            if (it.value_len == 2) {
+                response_buffer[offset] = 0x01; // format
+            } else {
+                response_buffer[offset] = 0x02;
+            }
+            offset++;
+        }
+        
+        // space?
+        if (offset + pair_len > response_buffer_size) {
+            if (offset > 2) break;
+            it.value_len = response_buffer_size - 4;
+        }
+        
+        // store
+        bt_store_16(response_buffer, offset, it.handle);
+        offset += 2;
+        uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len);
+        offset += bytes_copied;
+    }
+    
+    if (offset == 1){
+        return setup_error_atribute_not_found(response_buffer, ATT_FIND_INFORMATION_REQUEST, start_handle);
+    }
+    
+    response_buffer[0] = ATT_FIND_INFORMATION_REPLY;
+    return offset;
+}
+
+static uint16_t handle_find_information_request(uint8_t * request_buffer,  uint16_t request_len,
+                                         uint8_t * response_buffer, uint16_t response_buffer_size){
+    return handle_find_information_request2(response_buffer, response_buffer_size, READ_BT_16(request_buffer, 1), READ_BT_16(request_buffer, 3));
+}
+
+//
+// MARK: ATT_FIND_BY_TYPE_VALUE
+//
+// "Only attributes with attribute handles between and including the Starting Handle parameter
+// and the Ending Handle parameter that match the requested attri- bute type and the attribute
+// value that have sufficient permissions to allow reading will be returned" -> (1)
+//
+// TODO: handle other types then GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID
+//
+// NOTE: doesn't handle DYNAMIC values
+// NOTE: only supports 16 bit UUIDs
+// 
+static uint16_t handle_find_by_type_value_request2(uint8_t * response_buffer, uint16_t response_buffer_size,
+                                           uint16_t start_handle, uint16_t end_handle,
+                                           uint16_t attribute_type, uint16_t attribute_len, uint8_t* attribute_value){
+    
+    printf("ATT_FIND_BY_TYPE_VALUE_REQUEST: from %04X to %04X, type %04X, value: ", start_handle, end_handle, attribute_type);
+    hexdump2(attribute_value, attribute_len);
+    
+    uint16_t offset      = 1;
+    uint16_t in_group    = 0;
+    uint16_t prev_handle = 0;
+    
+    att_iterator_t it;
+    att_iterator_init(&it);
+    while (att_iterator_has_next(&it)){
+        att_iterator_fetch_next(&it);
+        
+        if (it.handle && it.handle < start_handle) continue;
+        if (it.handle > end_handle) break;  // (1)
+        
+        // close current tag, if within a group and a new service definition starts or we reach end of att db
+        if (in_group &&
+            (it.handle == 0 || att_iterator_match_uuid16(&it, GATT_PRIMARY_SERVICE_UUID) || att_iterator_match_uuid16(&it, GATT_SECONDARY_SERVICE_UUID))){
+            
+            printf("End of group, handle 0x%04x\n", prev_handle);
+            bt_store_16(response_buffer, offset, prev_handle);
+            offset += 2;
+            in_group = 0;
+            
+            // check if space for another handle pair available
+            if (offset + 4 > response_buffer_size){
+                break;
+            }
+        }
+        
+        // keep track of previous handle
+        prev_handle = it.handle;
+        
+        // does current attribute match
+        if (it.handle && att_iterator_match_uuid16(&it, attribute_type) && attribute_len == it.value_len && memcmp(attribute_value, it.value, it.value_len) == 0){
+            printf("Begin of group, handle 0x%04x\n", it.handle);
+            bt_store_16(response_buffer, offset, it.handle);
+            offset += 2;
+            in_group = 1;
+        }
+    }
+    
+    if (offset == 1){
+        return setup_error_atribute_not_found(response_buffer, ATT_FIND_BY_TYPE_VALUE_REQUEST, start_handle);
+    }
+    
+    response_buffer[0] = ATT_FIND_BY_TYPE_VALUE_RESPONSE;
+    return offset;
+}
+                                         
+static uint16_t handle_find_by_type_value_request(uint8_t * request_buffer,  uint16_t request_len,
+                                           uint8_t * response_buffer, uint16_t response_buffer_size){
+    int attribute_len = request_len - 7;
+    return handle_find_by_type_value_request2(response_buffer, response_buffer_size, READ_BT_16(request_buffer, 1),
+                                              READ_BT_16(request_buffer, 3), READ_BT_16(request_buffer, 5), attribute_len, &request_buffer[7]);
+}
+                                                                                  
+//
+// MARK: ATT_READ_BY_TYPE_REQUEST
+//
+static uint16_t handle_read_by_type_request2(uint8_t * response_buffer, uint16_t response_buffer_size,
+                                      uint16_t start_handle, uint16_t end_handle,
+                                      uint16_t attribute_type_len, uint8_t * attribute_type){
+    
+    printf("ATT_READ_BY_TYPE_REQUEST: from %04X to %04X, type: ", start_handle, end_handle); 
+    hexdump2(attribute_type, attribute_type_len);
+    
+    uint16_t offset   = 1;
+    uint16_t pair_len = 0;
+
+    att_iterator_t it;
+    att_iterator_init(&it);
+    while (att_iterator_has_next(&it)){
+        att_iterator_fetch_next(&it);
+        
+        if (!it.handle) break;
+        if (it.handle < start_handle) continue;
+        if (it.handle > end_handle) break;  // (1)
+
+        // does current attribute match
+        if (!att_iterator_match_uuid(&it, attribute_type, attribute_type_len)) continue;
+        
+        att_update_value_len(&it);
+        
+        // check if value has same len as last one
+        uint16_t this_pair_len = 2 + it.value_len;
+        if (offset > 1){
+            if (pair_len != this_pair_len) {
+                break;
+            }
+        }
+        
+        // first
+        if (offset == 1) {
+            pair_len = this_pair_len;
+            response_buffer[offset] = pair_len;
+            offset++;
+        }
+        
+        // space?
+        if (offset + pair_len > response_buffer_size) {
+            if (offset > 2) break;
+            it.value_len = response_buffer_size - 4;
+        }
+        
+        // store
+        bt_store_16(response_buffer, offset, it.handle);
+        offset += 2;
+        uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len);
+        offset += bytes_copied;
+    }
+    
+    if (offset == 1){
+        return setup_error_atribute_not_found(response_buffer, ATT_READ_BY_TYPE_REQUEST, start_handle);
+    }
+    
+    response_buffer[0] = ATT_READ_BY_TYPE_RESPONSE;
+    return offset;
+}
+
+static uint16_t handle_read_by_type_request(uint8_t * request_buffer,  uint16_t request_len,
+                                     uint8_t * response_buffer, uint16_t response_buffer_size){
+    int attribute_type_len;
+    if (request_len <= 7){
+        attribute_type_len = 2;
+    } else {
+        attribute_type_len = 16;
+    }
+    return handle_read_by_type_request2(response_buffer, response_buffer_size, READ_BT_16(request_buffer, 1), READ_BT_16(request_buffer, 3), attribute_type_len, &request_buffer[5]);
+}
+
+//
+// MARK: ATT_READ_BY_TYPE_REQUEST
+//
+static uint16_t handle_read_request2(uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t handle){
+    
+    printf("ATT_READ_REQUEST: handle %04x\n", handle);
+    
+    att_iterator_t it;
+    int ok = att_find_handle(&it, handle);
+    if (!ok){
+        return setup_error_atribute_not_found(response_buffer, ATT_READ_REQUEST, handle);
+    }
+    
+    att_update_value_len(&it);
+
+    uint16_t offset   = 1;
+    // limit data
+    if (offset + it.value_len > response_buffer_size) {
+        it.value_len = response_buffer_size - 1;
+    }
+    
+    // store
+    uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len);
+    offset += bytes_copied;
+    
+    response_buffer[0] = ATT_READ_RESPONSE;
+    return offset;
+}
+
+static uint16_t handle_read_request(uint8_t * request_buffer,  uint16_t request_len,
+                             uint8_t * response_buffer, uint16_t response_buffer_size){
+    return handle_read_request2(response_buffer, response_buffer_size, READ_BT_16(request_buffer, 1));
+}
+
+//
+// MARK: ATT_READ_BLOB_REQUEST 0x0c
+//
+static uint16_t handle_read_blob_request2(uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t handle, uint16_t value_offset){
+    printf("ATT_READ_BLOB_REQUEST: handle %04x, offset %u\n", handle, value_offset);
+
+    att_iterator_t it;
+    int ok = att_find_handle(&it, handle);
+    if (!ok){
+        return setup_error_atribute_not_found(response_buffer, ATT_READ_BLOB_REQUEST, handle);
+    }
+    
+    att_update_value_len(&it);
+
+    if (value_offset >= it.value_len){
+        return setup_error_invalid_offset(response_buffer, ATT_READ_BLOB_REQUEST, handle);
+    }
+    
+    // limit data
+    uint16_t offset   = 1;
+    if (offset + it.value_len - value_offset > response_buffer_size) {
+        it.value_len = response_buffer_size - 1 + value_offset;
+    }
+    
+    // store
+    uint16_t bytes_copied = att_copy_value(&it, value_offset, response_buffer + offset, it.value_len - value_offset);
+    offset += bytes_copied;
+    
+    response_buffer[0] = ATT_READ_BLOB_RESPONSE;
+    return offset;
+}
+
+uint16_t handle_read_blob_request(uint8_t * request_buffer,  uint16_t request_len,
+                                  uint8_t * response_buffer, uint16_t response_buffer_size){
+    return handle_read_blob_request2(response_buffer, response_buffer_size, READ_BT_16(request_buffer, 1), READ_BT_16(request_buffer, 3));
+}
+
+//
+// MARK: ATT_READ_MULTIPLE_REQUEST 0x0e
+//
+static uint16_t handle_read_multiple_request2(uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t num_handles, uint16_t * handles){
+    printf("ATT_READ_MULTIPLE_REQUEST: num handles %u\n", num_handles);
+    
+    uint16_t offset   = 1;
+
+    int i;
+    for (i=0;i<num_handles;i++){
+        uint16_t handle = handles[i];
+        
+        if (handle == 0){
+            return setup_error_invalid_handle(response_buffer, ATT_READ_MULTIPLE_REQUEST, handle);
+        }
+        
+        att_iterator_t it;
+
+        int ok = att_find_handle(&it, handle);
+        if (!ok){
+            return setup_error_invalid_handle(response_buffer, ATT_READ_MULTIPLE_REQUEST, handle);
+        }
+
+        att_update_value_len(&it);
+        
+        // limit data
+        if (offset + it.value_len > response_buffer_size) {
+            it.value_len = response_buffer_size - 1;
+        }
+        
+        // store
+        uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len);
+        offset += bytes_copied;
+    }
+    
+    response_buffer[0] = ATT_READ_MULTIPLE_RESPONSE;
+    return offset;
+}
+uint16_t handle_read_multiple_request(uint8_t * request_buffer,  uint16_t request_len,
+                                      uint8_t * response_buffer, uint16_t response_buffer_size){
+    int num_handles = (request_len - 1) >> 1;
+    return handle_read_multiple_request2(response_buffer, response_buffer_size, num_handles, (uint16_t*) &request_buffer[1]);
+}
+
+//
+// MARK: ATT_READ_BY_GROUP_TYPE_REQUEST 0x10
+//
+// TODO: handle other types then GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID
+//
+// NOTE: doesn't handle DYNAMIC values
+//
+static uint16_t handle_read_by_group_type_request2(uint8_t * response_buffer, uint16_t response_buffer_size,
+                                            uint16_t start_handle, uint16_t end_handle,
+                                            uint16_t attribute_type_len, uint8_t * attribute_type){
+    
+    printf("ATT_READ_BY_GROUP_TYPE_REQUEST: from %04X to %04X, buffer size %u, type: ", start_handle, end_handle, response_buffer_size);
+    hexdump2(attribute_type, attribute_type_len);
+    
+    uint16_t offset   = 1;
+    uint16_t pair_len = 0;
+    uint16_t in_group = 0;
+    uint16_t group_start_handle = 0;
+    uint8_t const * group_start_value = NULL;
+    uint16_t prev_handle = 0;
+
+    att_iterator_t it;
+    att_iterator_init(&it);
+    while (att_iterator_has_next(&it)){
+        att_iterator_fetch_next(&it);
+        
+        if (it.handle && it.handle < start_handle) continue;
+        if (it.handle > end_handle) break;  // (1)
+        
+        // close current tag, if within a group and a new service definition starts or we reach end of att db
+        if (in_group &&
+            (it.handle == 0 || att_iterator_match_uuid16(&it, GATT_PRIMARY_SERVICE_UUID) || att_iterator_match_uuid16(&it, GATT_SECONDARY_SERVICE_UUID))){
+            // TODO: check if handle is included in start/end range
+            // printf("End of group, handle 0x%04x, val_len: %u\n", prev_handle, pair_len - 4);
+            
+            bt_store_16(response_buffer, offset, group_start_handle);
+            offset += 2;
+            bt_store_16(response_buffer, offset, prev_handle);
+            offset += 2;
+            memcpy(response_buffer + offset, group_start_value, pair_len - 4);
+            offset += pair_len - 4;
+            in_group = 0;
+            
+            // check if space for another handle pair available
+            if (offset + pair_len > response_buffer_size){
+                break;
+            }
+        }
+        
+        // keep track of previous handle
+        prev_handle = it.handle;
+        
+        // does current attribute match
+        // printf("compare: %04x == %04x\n", *(uint16_t*) context->attribute_type, *(uint16_t*) uuid);
+        if (it.handle && att_iterator_match_uuid(&it, attribute_type, attribute_type_len)) {
+            
+            // check if value has same len as last one
+            uint16_t this_pair_len = 4 + it.value_len;
+            if (offset > 1){
+                if (this_pair_len != pair_len) {
+                    break;
+                }
+            }
+            
+            // printf("Begin of group, handle 0x%04x\n", it.handle);
+            
+            // first
+            if (offset == 1) {
+                pair_len = this_pair_len;
+                response_buffer[offset] = this_pair_len;
+                offset++;
+            }
+            
+            group_start_handle = it.handle;
+            group_start_value  = it.value;
+            in_group = 1;
+        }
+    }        
+    
+    if (offset == 1){
+        return setup_error_atribute_not_found(response_buffer, ATT_READ_BY_GROUP_TYPE_REQUEST, start_handle);
+    }
+    
+    response_buffer[0] = ATT_READ_BY_GROUP_TYPE_RESPONSE;
+    return offset;
+}
+uint16_t handle_read_by_group_type_request(uint8_t * request_buffer,  uint16_t request_len,
+                                           uint8_t * response_buffer, uint16_t response_buffer_size){
+    int attribute_type_len;
+    if (request_len <= 7){
+        attribute_type_len = 2;
+    } else {
+        attribute_type_len = 16;
+    }
+    return handle_read_by_group_type_request2(response_buffer, response_buffer_size, READ_BT_16(request_buffer, 1), READ_BT_16(request_buffer, 3), attribute_type_len, &request_buffer[5]);
+}
+
+//
+// MARK: ATT_WRITE_REQUEST 0x12
+static uint16_t handle_write_request(uint8_t * request_buffer,  uint16_t request_len,
+                              uint8_t * response_buffer, uint16_t response_buffer_size){
+    uint16_t handle = READ_BT_16(request_buffer, 1);
+    if (!att_write_callback) {
+        // TODO: Use "Write Not Permitted"
+        return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle);
+    }
+    att_iterator_t it;
+    int ok = att_find_handle(&it, handle);
+    if (!ok) {
+        return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle);
+    }
+    if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) {
+        // TODO: Use "Write Not Permitted"
+        return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle);
+    }
+    (*att_write_callback)(handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3, NULL);
+    response_buffer[0] = ATT_WRITE_RESPONSE;
+    return 1;
+}
+
+//
+// MARK: ATT_PREPARE_WRITE_REQUEST 0x16
+static uint16_t handle_prepare_write_request(uint8_t * request_buffer,  uint16_t request_len,
+                                      uint8_t * response_buffer, uint16_t response_buffer_size){
+    uint16_t handle = READ_BT_16(request_buffer, 1);
+    if (!att_write_callback) {
+        // TODO: Use "Write Not Permitted"
+        return setup_error_atribute_not_found(response_buffer, ATT_PREPARE_WRITE_REQUEST, handle);
+    }
+    att_iterator_t it;
+    int ok = att_find_handle(&it, handle);
+    if (!ok) {
+        return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle);
+    }
+    if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) {
+        // TODO: Use "Write Not Permitted"
+        return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle);
+    }
+    (*att_write_callback)(handle, ATT_TRANSACTION_MODE_ACTIVE, 0, request_buffer + 3, request_len - 3, NULL);
+    
+    // response: echo request
+    memcpy(response_buffer, request_buffer, request_len);
+    response_buffer[0] = ATT_PREPARE_WRITE_RESPONSE;
+    return request_len;
+}
+
+// MARK: ATT_EXECUTE_WRITE_REQUEST 0x18
+static uint16_t handle_execute_write_request(uint8_t * request_buffer,  uint16_t request_len,
+                                      uint8_t * response_buffer, uint16_t response_buffer_size){
+    if (!att_write_callback) {
+        // TODO: Use "Write Not Permitted"
+        return setup_error_atribute_not_found(response_buffer, ATT_EXECUTE_WRITE_REQUEST, 0);
+    }
+    if (request_buffer[1]) {
+        (*att_write_callback)(0, ATT_TRANSACTION_MODE_EXECUTE, 0, request_buffer + 3, request_len - 3, NULL);
+    } else {
+        (*att_write_callback)(0, ATT_TRANSACTION_MODE_CANCEL, 0, request_buffer + 3, request_len - 3, NULL);
+    }
+    response_buffer[0] = ATT_EXECUTE_WRITE_RESPONSE;
+    return 1;
+}
+
+// MARK: ATT_WRITE_COMMAND 0x52
+static void handle_write_command(uint8_t * request_buffer,  uint16_t request_len,
+                                           uint8_t * response_buffer, uint16_t response_buffer_size){
+    if (!att_write_callback) return;
+    uint16_t handle = READ_BT_16(request_buffer, 1);
+    att_iterator_t it;
+    int ok = att_find_handle(&it, handle);
+    if (!ok) return;
+    if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) return;
+    (*att_write_callback)(handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3, NULL);
+}
+
+// MARK: ATT_SIGNED_WRITE_COMAND 0xD2
+static void handle_signed_write_command(uint8_t * request_buffer,  uint16_t request_len,
+                                 uint8_t * response_buffer, uint16_t response_buffer_size){
+
+    if (request_len < 15) return;
+    if (!att_write_callback) return;
+    uint16_t handle = READ_BT_16(request_buffer, 1);
+    att_iterator_t it;
+    int ok = att_find_handle(&it, handle);
+    if (!ok) return;
+    if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) return;
+    (*att_write_callback)(handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3 - 12, (signature_t *) request_buffer + request_len - 12);
+}
+
+// MARK: helper for ATT_HANDLE_VALUE_NOTIFICATION and ATT_HANDLE_VALUE_INDICATION
+static uint16_t prepare_handle_value(att_connection_t * att_connection,
+                                     uint16_t handle,
+                                     uint8_t *value,
+                                     uint16_t value_len, 
+                                     uint8_t * response_buffer){
+    bt_store_16(response_buffer, 1, handle);
+    if (value_len > att_connection->mtu - 3){
+        value_len = att_connection->mtu - 3;
+    }
+    memcpy(&response_buffer[3], value, value_len);
+    return value_len + 3;
+}
+
+// MARK: ATT_HANDLE_VALUE_NOTIFICATION 0x1b
+uint16_t att_prepare_handle_value_notification(att_connection_t * att_connection,
+                                               uint16_t handle,
+                                               uint8_t *value,
+                                               uint16_t value_len, 
+                                               uint8_t * response_buffer){
+
+    response_buffer[0] = ATT_HANDLE_VALUE_NOTIFICATION;
+    return prepare_handle_value(att_connection, handle, value, value_len, response_buffer);
+}
+
+// MARK: ATT_HANDLE_VALUE_INDICATION 0x1d
+uint16_t att_prepare_handle_value_indication(att_connection_t * att_connection,
+                                             uint16_t handle,
+                                             uint8_t *value,
+                                             uint16_t value_len, 
+                                             uint8_t * response_buffer){
+
+    response_buffer[0] = ATT_HANDLE_VALUE_INDICATION;
+    return prepare_handle_value(att_connection, handle, value, value_len, response_buffer);
+}
+    
+// MARK: Dispatcher
+uint16_t att_handle_request(att_connection_t * att_connection,
+                            uint8_t * request_buffer,
+                            uint16_t request_len,
+                            uint8_t * response_buffer){
+    uint16_t response_len = 0;
+    uint16_t response_buffer_size = att_connection->mtu;
+    
+    switch (request_buffer[0]){
+        case ATT_EXCHANGE_MTU_REQUEST:
+            response_len = handle_exchange_mtu_request(att_connection, request_buffer, request_len, response_buffer);
+            break;
+        case ATT_FIND_INFORMATION_REQUEST:
+            response_len = handle_find_information_request(request_buffer, request_len,response_buffer, response_buffer_size);
+            break;
+        case ATT_FIND_BY_TYPE_VALUE_REQUEST:
+            response_len = handle_find_by_type_value_request(request_buffer, request_len, response_buffer, response_buffer_size);
+            break;
+        case ATT_READ_BY_TYPE_REQUEST:  
+            response_len = handle_read_by_type_request(request_buffer, request_len, response_buffer, response_buffer_size);
+            break;
+        case ATT_READ_REQUEST:  
+            response_len = handle_read_request(request_buffer, request_len, response_buffer, response_buffer_size);
+            break;
+        case ATT_READ_BLOB_REQUEST:  
+            response_len = handle_read_blob_request(request_buffer, request_len, response_buffer, response_buffer_size);
+            break;
+        case ATT_READ_MULTIPLE_REQUEST:  
+            response_len = handle_read_multiple_request(request_buffer, request_len, response_buffer, response_buffer_size);
+            break;
+        case ATT_READ_BY_GROUP_TYPE_REQUEST:  
+            response_len = handle_read_by_group_type_request(request_buffer, request_len, response_buffer, response_buffer_size);
+            break;
+        case ATT_WRITE_REQUEST:
+            response_len = handle_write_request(request_buffer, request_len, response_buffer, response_buffer_size);
+            break;
+        case ATT_PREPARE_WRITE_REQUEST:
+            response_len = handle_prepare_write_request(request_buffer, request_len, response_buffer, response_buffer_size);
+            break;
+        case ATT_EXECUTE_WRITE_REQUEST:
+            response_len = handle_execute_write_request(request_buffer, request_len, response_buffer, response_buffer_size);
+            break;
+        case ATT_WRITE_COMMAND:
+            handle_write_command(request_buffer, request_len, response_buffer, response_buffer_size);
+            break;
+        case ATT_SIGNED_WRITE_COMAND:
+            handle_signed_write_command(request_buffer, request_len, response_buffer, response_buffer_size);
+            break;
+        default:
+            printf("Unhandled ATT Command: %02X, DATA: ", request_buffer[0]);
+            hexdump2(&request_buffer[9], request_len-9);
+            break;
+    }
+    return response_len;
+}
+
+#if 0
+
+// test profile
+#include "profile.h"
+
+int main(){
+    int acl_buffer_size;
+    uint8_t acl_buffer[27];
+    att_set_db(profile_data);
+    att_dump_attributes();
+
+    uint8_t uuid_1[] = { 0x00, 0x18};
+    acl_buffer_size = handle_find_by_type_value_request2(acl_buffer, 19, 0, 0xffff, 0x2800, 2, (uint8_t *) &uuid_1);
+    hexdump2(acl_buffer, acl_buffer_size);
+    
+    uint8_t uuid_3[] = { 0x00, 0x2a};
+    acl_buffer_size = handle_read_by_type_request2(acl_buffer, 19, 0, 0xffff, 2, (uint8_t *) &uuid_3);
+    hexdump2(acl_buffer, acl_buffer_size);
+        
+    acl_buffer_size = handle_find_by_type_value_request2(acl_buffer, 19, 0, 0xffff, 0x2800, 2, (uint8_t *) &uuid_1);
+    hexdump2(acl_buffer, acl_buffer_size);
+
+    uint8_t uuid_4[] = { 0x00, 0x28};
+    acl_buffer_size = handle_read_by_group_type_request2(acl_buffer, 20, 0, 0xffff, 2, (uint8_t *) &uuid_4);
+    hexdump2(acl_buffer, acl_buffer_size);
+    
+    acl_buffer_size = handle_find_information_request2(acl_buffer, 20, 0, 0xffff);
+    hexdump2(acl_buffer, acl_buffer_size);
+    acl_buffer_size = handle_find_information_request2(acl_buffer, 20, 3, 0xffff);
+    hexdump2(acl_buffer, acl_buffer_size);
+    acl_buffer_size = handle_find_information_request2(acl_buffer, 20, 5, 0xffff);
+    hexdump2(acl_buffer, acl_buffer_size);
+
+    acl_buffer_size = handle_read_request2(acl_buffer, 19, 0x0003);
+    hexdump2(acl_buffer, acl_buffer_size);
+
+    return 0;
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/att.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2011-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+// MARK: Attribute PDU Opcodes 
+#define ATT_ERROR_RESPONSE              0x01
+
+#define ATT_EXCHANGE_MTU_REQUEST        0x02
+#define ATT_EXCHANGE_MTU_RESPONSE       0x03
+
+#define ATT_FIND_INFORMATION_REQUEST    0x04
+#define ATT_FIND_INFORMATION_REPLY      0x05
+#define ATT_FIND_BY_TYPE_VALUE_REQUEST  0x06
+#define ATT_FIND_BY_TYPE_VALUE_RESPONSE 0x07
+
+#define ATT_READ_BY_TYPE_REQUEST        0x08
+#define ATT_READ_BY_TYPE_RESPONSE       0x09
+#define ATT_READ_REQUEST                0x0a
+#define ATT_READ_RESPONSE               0x0b
+#define ATT_READ_BLOB_REQUEST           0x0c
+#define ATT_READ_BLOB_RESPONSE          0x0d
+#define ATT_READ_MULTIPLE_REQUEST       0x0e
+#define ATT_READ_MULTIPLE_RESPONSE      0x0f
+#define ATT_READ_BY_GROUP_TYPE_REQUEST  0x10
+#define ATT_READ_BY_GROUP_TYPE_RESPONSE 0x11
+
+#define ATT_WRITE_REQUEST               0x12
+#define ATT_WRITE_RESPONSE              0x13
+
+#define ATT_PREPARE_WRITE_REQUEST       0x16
+#define ATT_PREPARE_WRITE_RESPONSE      0x17
+#define ATT_EXECUTE_WRITE_REQUEST       0x18
+#define ATT_EXECUTE_WRITE_RESPONSE      0x19
+
+#define ATT_HANDLE_VALUE_NOTIFICATION   0x1b
+#define ATT_HANDLE_VALUE_CONFIRMATION   0x1c
+#define ATT_HANDLE_VALUE_INDICATION     0x1d
+
+
+#define ATT_WRITE_COMMAND               0x52
+#define ATT_SIGNED_WRITE_COMAND         0xD2
+
+// MARK: ATT Error Codes
+#define ATT_ERROR_ATTRIBUTE_INVALID      0x01
+#define ATT_ERROR_INVALID_OFFSET         0x07
+#define ATT_ERROR_ATTRIBUTE_NOT_FOUND    0x0a
+#define ATT_ERROR_UNSUPPORTED_GROUP_TYPE 0x10
+
+// MARK: Attribute Property Flags
+#define ATT_PROPERTY_BROADCAST           0x01
+#define ATT_PROPERTY_READ                0x02
+#define ATT_PROPERTY_WRITE_WITHOUT_RESPONSE 0x04
+#define ATT_PROPERTY_WRITE               0x08
+#define ATT_PROPERTY_NOTIFY              0x10
+#define ATT_PROPERTY_INDICATE            0x20
+#define ATT_PROPERTY_AUTHENTICATED_SIGNED_WRITE 0x40
+#define ATT_PROPERTY_EXTENDED_PROPERTIES 0x80
+
+// MARK: Attribute Property Flag, BTstack extension
+// value is asked from client
+#define ATT_PROPERTY_DYNAMIC             0x100
+// 128 bit UUID used
+#define ATT_PROPERTY_UUID128             0x200
+
+// MARK: GATT UUIDs
+#define GATT_PRIMARY_SERVICE_UUID      0x2800
+#define GATT_SECONDARY_SERVICE_UUID    0x2801
+#define GATT_CHARACTERISTICS_UUID      0x2803
+
+#define GAP_SERVICE_UUID          0x1800
+#define GAP_DEVICE_NAME_UUID      0x2a00
+
+#define ATT_TRANSACTION_MODE_NONE      0x0
+#define ATT_TRANSACTION_MODE_ACTIVE    0x1
+#define ATT_TRANSACTION_MODE_EXECUTE   0x2
+#define ATT_TRANSACTION_MODE_CANCEL    0x3
+
+typedef struct att_connection {
+    uint16_t mtu;
+} att_connection_t;
+
+typedef uint8_t signature_t[12];
+
+// ATT Client Read Callback for Dynamic Data
+// - if buffer == NULL, don't copy data, just return size of value
+// - if buffer != NULL, copy data and return number bytes copied
+// @param offset defines start of attribute value
+typedef uint16_t (*att_read_callback_t)(uint16_t handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size);
+
+// ATT Client Write Callback for Dynamic Data
+// @param handle to be written
+// @param transaction - ATT_TRANSACTION_MODE_NONE for regular writes, ATT_TRANSACTION_MODE_ACTIVE for prepared writes and ATT_TRANSACTION_MODE_EXECUTE
+// @param offset into the value - used for queued writes and long attributes
+// @param buffer 
+// @param buffer_size
+// @Param signature used for signed write commmands
+typedef void (*att_write_callback_t)(uint16_t handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size, signature_t * signature);
+
+// MARK: ATT Operations
+
+void att_set_db(uint8_t const * db);
+
+void att_set_read_callback(att_read_callback_t callback);
+
+void att_set_write_callback(att_write_callback_t callback);
+
+void att_dump_attributes(void);
+
+// response buffer size = att_connection->mtu
+uint16_t att_handle_request(att_connection_t * att_connection,
+                            uint8_t * request_buffer,
+                            uint16_t request_len,
+                            uint8_t * response_buffer);
+
+uint16_t att_prepare_handle_value_notification(att_connection_t * att_connection,
+                                               uint16_t handle,
+                                               uint8_t *value,
+                                               uint16_t value_len, 
+                                               uint8_t * response_buffer);
+
+uint16_t att_prepare_handle_value_indication(att_connection_t * att_connection,
+                                             uint16_t handle,
+                                             uint8_t *value,
+                                             uint16_t value_len, 
+                                             uint8_t * response_buffer);
+
+    
+    
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/bt_control.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2009 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  bt_control.h
+ *
+ *  BT Control API -- allows BT Daemon to initialize and control differnt hardware
+ *
+ *  Created by Matthias Ringwald on 5/19/09.
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+typedef enum {
+    POWER_WILL_SLEEP = 1,
+    POWER_WILL_WAKE_UP
+} POWER_NOTIFICATION_t;
+
+typedef struct {
+    int          (*on)   (void *config);  // <-- turn BT module on and configure
+    int          (*off)  (void *config);  // <-- turn BT module off
+    int          (*sleep)(void *config);  // <-- put BT module to sleep    - only to be called after ON
+    int          (*wake) (void *config);  // <-- wake BT module from sleep - only to be called after SLEEP
+    int          (*valid)(void *config);  // <-- test if hardware can be supported
+    const char * (*name) (void *config);  // <-- return hardware name
+
+    /** support for UART baud rate changes - cmd has to be stored in hci_cmd_buffer
+     * @return have command
+     */
+    int          (*baudrate_cmd)(void * config, uint32_t baudrate, uint8_t *hci_cmd_buffer); 
+    
+    /** support custom init sequences after RESET command - cmd has to be stored in hci_cmd_buffer
+      * @return have command
+      */
+    int          (*next_cmd)(void *config, uint8_t * hci_cmd_buffer); 
+
+    void         (*register_for_power_notifications)(void (*cb)(POWER_NOTIFICATION_t event));
+
+    void         (*hw_error)(void); 
+} bt_control_t;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/btstack.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2009 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  btstack.h
+ *
+ *  Created by Matthias Ringwald on 7/1/09.
+ *
+ *  BTstack client API
+ *  
+ */
+
+#pragma once
+
+#include <btstack/hci_cmds.h>
+#include <btstack/run_loop.h>
+#include <btstack/utils.h>
+
+#include <stdint.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+    
+// Default TCP port for BTstack daemon
+#define BTSTACK_PORT            13333
+
+// UNIX domain socket for BTstack */
+#define BTSTACK_UNIX            "/tmp/BTstack"
+
+// packet handler
+typedef void (*btstack_packet_handler_t) (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
+
+// optional: if called before bt_open, TCP socket is used instead of local unix socket
+//           note: address is not copied and must be valid during bt_open
+void bt_use_tcp(const char * address, uint16_t port); 
+
+// init BTstack library
+int bt_open(void);
+
+// stop using BTstack library
+int bt_close(void);
+
+// send hci cmd packet
+int bt_send_cmd(const hci_cmd_t *cmd, ...);
+
+// register packet handler -- channel only valid for l2cap and rfcomm packets
+// @returns old packet handler
+btstack_packet_handler_t bt_register_packet_handler(btstack_packet_handler_t handler);
+
+void bt_send_acl(uint8_t * data, uint16_t len);
+
+void bt_send_l2cap(uint16_t local_cid, uint8_t *data, uint16_t len);
+void bt_send_rfcomm(uint16_t rfcom_cid, uint8_t *data, uint16_t len);
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/btstack_memory.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2011 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  btstsack_memory.h
+ *
+ *  @brief BTstack memory management via configurable memory pools
+ *
+ *  @note code semi-atuomatically generated by btstack_memory_generator.py
+ *
+ */
+
+#include "btstack_memory.h"
+#include <btstack/memory_pool.h>
+
+#include <stdlib.h>
+
+#include "config.h"
+#include "hci.h"
+#include "l2cap.h"
+#include "rfcomm.h"
+
+// MARK: hci_connection_t
+#ifdef MAX_NO_HCI_CONNECTIONS
+static hci_connection_t hci_connection_storage[MAX_NO_HCI_CONNECTIONS];
+static memory_pool_t hci_connection_pool;
+void * btstack_memory_hci_connection_get(void){
+    return memory_pool_get(&hci_connection_pool);
+}
+void   btstack_memory_hci_connection_free(void *hci_connection){
+    memory_pool_free(&hci_connection_pool, hci_connection);
+}
+#elif defined(HAVE_MALLOC)
+void * btstack_memory_hci_connection_get(void){
+    return malloc(sizeof(hci_connection_t));
+}
+void  btstack_memory_hci_connection_free(void *hci_connection){
+    free(hci_connection);
+}
+#endif
+
+
+// MARK: l2cap_service_t
+#ifdef MAX_NO_L2CAP_SERVICES
+static l2cap_service_t l2cap_service_storage[MAX_NO_L2CAP_SERVICES];
+static memory_pool_t l2cap_service_pool;
+void * btstack_memory_l2cap_service_get(void){
+    return memory_pool_get(&l2cap_service_pool);
+}
+void   btstack_memory_l2cap_service_free(void *l2cap_service){
+    memory_pool_free(&l2cap_service_pool, l2cap_service);
+}
+#elif defined(HAVE_MALLOC)
+void * btstack_memory_l2cap_service_get(void){
+    return malloc(sizeof(l2cap_service_t));
+}
+void  btstack_memory_l2cap_service_free(void *l2cap_service){
+    free(l2cap_service);
+}
+#endif
+
+
+// MARK: l2cap_channel_t
+#ifdef MAX_NO_L2CAP_CHANNELS
+static l2cap_channel_t l2cap_channel_storage[MAX_NO_L2CAP_CHANNELS];
+static memory_pool_t l2cap_channel_pool;
+void * btstack_memory_l2cap_channel_get(void){
+    return memory_pool_get(&l2cap_channel_pool);
+}
+void   btstack_memory_l2cap_channel_free(void *l2cap_channel){
+    memory_pool_free(&l2cap_channel_pool, l2cap_channel);
+}
+#elif defined(HAVE_MALLOC)
+void * btstack_memory_l2cap_channel_get(void){
+    return malloc(sizeof(l2cap_channel_t));
+}
+void  btstack_memory_l2cap_channel_free(void *l2cap_channel){
+    free(l2cap_channel);
+}
+#endif
+
+
+// MARK: rfcomm_multiplexer_t
+#ifdef MAX_NO_RFCOMM_MULTIPLEXERS
+static rfcomm_multiplexer_t rfcomm_multiplexer_storage[MAX_NO_RFCOMM_MULTIPLEXERS];
+static memory_pool_t rfcomm_multiplexer_pool;
+void * btstack_memory_rfcomm_multiplexer_get(void){
+    return memory_pool_get(&rfcomm_multiplexer_pool);
+}
+void   btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){
+    memory_pool_free(&rfcomm_multiplexer_pool, rfcomm_multiplexer);
+}
+#elif defined(HAVE_MALLOC)
+void * btstack_memory_rfcomm_multiplexer_get(void){
+    return malloc(sizeof(rfcomm_multiplexer_t));
+}
+void  btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){
+    free(rfcomm_multiplexer);
+}
+#endif
+
+
+// MARK: rfcomm_service_t
+#ifdef MAX_NO_RFCOMM_SERVICES
+static rfcomm_service_t rfcomm_service_storage[MAX_NO_RFCOMM_SERVICES];
+static memory_pool_t rfcomm_service_pool;
+void * btstack_memory_rfcomm_service_get(void){
+    return memory_pool_get(&rfcomm_service_pool);
+}
+void   btstack_memory_rfcomm_service_free(void *rfcomm_service){
+    memory_pool_free(&rfcomm_service_pool, rfcomm_service);
+}
+#elif defined(HAVE_MALLOC)
+void * btstack_memory_rfcomm_service_get(void){
+    return malloc(sizeof(rfcomm_service_t));
+}
+void  btstack_memory_rfcomm_service_free(void *rfcomm_service){
+    free(rfcomm_service);
+}
+#endif
+
+
+// MARK: rfcomm_channel_t
+#ifdef MAX_NO_RFCOMM_CHANNELS
+static rfcomm_channel_t rfcomm_channel_storage[MAX_NO_RFCOMM_CHANNELS];
+static memory_pool_t rfcomm_channel_pool;
+void * btstack_memory_rfcomm_channel_get(void){
+    return memory_pool_get(&rfcomm_channel_pool);
+}
+void   btstack_memory_rfcomm_channel_free(void *rfcomm_channel){
+    memory_pool_free(&rfcomm_channel_pool, rfcomm_channel);
+}
+#elif defined(HAVE_MALLOC)
+void * btstack_memory_rfcomm_channel_get(void){
+    return malloc(sizeof(rfcomm_channel_t));
+}
+void  btstack_memory_rfcomm_channel_free(void *rfcomm_channel){
+    free(rfcomm_channel);
+}
+#endif
+
+
+// MARK: db_mem_device_name_t
+#ifdef MAX_NO_DB_MEM_DEVICE_NAMES
+static db_mem_device_name_t db_mem_device_name_storage[MAX_NO_DB_MEM_DEVICE_NAMES];
+static memory_pool_t db_mem_device_name_pool;
+void * btstack_memory_db_mem_device_name_get(void){
+    return memory_pool_get(&db_mem_device_name_pool);
+}
+void   btstack_memory_db_mem_device_name_free(void *db_mem_device_name){
+    memory_pool_free(&db_mem_device_name_pool, db_mem_device_name);
+}
+#elif defined(HAVE_MALLOC)
+void * btstack_memory_db_mem_device_name_get(void){
+    return malloc(sizeof(db_mem_device_name_t));
+}
+void  btstack_memory_db_mem_device_name_free(void *db_mem_device_name){
+    free(db_mem_device_name);
+}
+#endif
+
+
+// MARK: db_mem_device_link_key_t
+#ifdef MAX_NO_DB_MEM_DEVICE_LINK_KEYS
+static db_mem_device_link_key_t db_mem_device_link_key_storage[MAX_NO_DB_MEM_DEVICE_LINK_KEYS];
+static memory_pool_t db_mem_device_link_key_pool;
+void * btstack_memory_db_mem_device_link_key_get(void){
+    return memory_pool_get(&db_mem_device_link_key_pool);
+}
+void   btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){
+    memory_pool_free(&db_mem_device_link_key_pool, db_mem_device_link_key);
+}
+#elif defined(HAVE_MALLOC)
+void * btstack_memory_db_mem_device_link_key_get(void){
+    return malloc(sizeof(db_mem_device_link_key_t));
+}
+void  btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){
+    free(db_mem_device_link_key);
+}
+#endif
+
+
+// MARK: db_mem_service_t
+#ifdef MAX_NO_DB_MEM_SERVICES
+static db_mem_service_t db_mem_service_storage[MAX_NO_DB_MEM_SERVICES];
+static memory_pool_t db_mem_service_pool;
+void * btstack_memory_db_mem_service_get(void){
+    return memory_pool_get(&db_mem_service_pool);
+}
+void   btstack_memory_db_mem_service_free(void *db_mem_service){
+    memory_pool_free(&db_mem_service_pool, db_mem_service);
+}
+#elif defined(HAVE_MALLOC)
+void * btstack_memory_db_mem_service_get(void){
+    return malloc(sizeof(db_mem_service_t));
+}
+void  btstack_memory_db_mem_service_free(void *db_mem_service){
+    free(db_mem_service);
+}
+#endif
+
+// init
+void btstack_memory_init(void){
+#ifdef MAX_NO_HCI_CONNECTIONS
+    memory_pool_create(&hci_connection_pool, hci_connection_storage, MAX_NO_HCI_CONNECTIONS, sizeof(hci_connection_t));
+#endif
+#ifdef MAX_NO_L2CAP_SERVICES
+    memory_pool_create(&l2cap_service_pool, l2cap_service_storage, MAX_NO_L2CAP_SERVICES, sizeof(l2cap_service_t));
+#endif
+#ifdef MAX_NO_L2CAP_CHANNELS
+    memory_pool_create(&l2cap_channel_pool, l2cap_channel_storage, MAX_NO_L2CAP_CHANNELS, sizeof(l2cap_channel_t));
+#endif
+#ifdef MAX_NO_RFCOMM_MULTIPLEXERS
+    memory_pool_create(&rfcomm_multiplexer_pool, rfcomm_multiplexer_storage, MAX_NO_RFCOMM_MULTIPLEXERS, sizeof(rfcomm_multiplexer_t));
+#endif
+#ifdef MAX_NO_RFCOMM_SERVICES
+    memory_pool_create(&rfcomm_service_pool, rfcomm_service_storage, MAX_NO_RFCOMM_SERVICES, sizeof(rfcomm_service_t));
+#endif
+#ifdef MAX_NO_RFCOMM_CHANNELS
+    memory_pool_create(&rfcomm_channel_pool, rfcomm_channel_storage, MAX_NO_RFCOMM_CHANNELS, sizeof(rfcomm_channel_t));
+#endif
+#ifdef MAX_NO_DB_MEM_DEVICE_NAMES
+    memory_pool_create(&db_mem_device_name_pool, db_mem_device_name_storage, MAX_NO_DB_MEM_DEVICE_NAMES, sizeof(db_mem_device_name_t));
+#endif
+#ifdef MAX_NO_DB_MEM_DEVICE_LINK_KEYS
+    memory_pool_create(&db_mem_device_link_key_pool, db_mem_device_link_key_storage, MAX_NO_DB_MEM_DEVICE_LINK_KEYS, sizeof(db_mem_device_link_key_t));
+#endif
+#ifdef MAX_NO_DB_MEM_SERVICES
+    memory_pool_create(&db_mem_service_pool, db_mem_service_storage, MAX_NO_DB_MEM_SERVICES, sizeof(db_mem_service_t));
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/btstack_memory.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  btstsack_memory.h
+ *
+ *  @brief BTstack memory management via configurable memory pools
+ *
+ */
+
+#pragma once
+
+#if defined __cplusplus
+extern "C" {
+#endif
+    
+void btstack_memory_init(void);
+
+void * btstack_memory_hci_connection_get(void);
+void   btstack_memory_hci_connection_free(void *hci_connection);
+void * btstack_memory_l2cap_service_get(void);
+void   btstack_memory_l2cap_service_free(void *l2cap_service);
+void * btstack_memory_l2cap_channel_get(void);
+void   btstack_memory_l2cap_channel_free(void *l2cap_channel);
+void * btstack_memory_rfcomm_multiplexer_get(void);
+void   btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer);
+void * btstack_memory_rfcomm_service_get(void);
+void   btstack_memory_rfcomm_service_free(void *rfcomm_service);
+void * btstack_memory_rfcomm_channel_get(void);
+void   btstack_memory_rfcomm_channel_free(void *rfcomm_channel);
+void * btstack_memory_db_mem_device_name_get(void);
+void   btstack_memory_db_mem_device_name_free(void *db_mem_device_name);
+void * btstack_memory_db_mem_device_link_key_get(void);
+void   btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key);
+void * btstack_memory_db_mem_service_get(void);
+void   btstack_memory_db_mem_service_free(void *db_mem_service);
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/config.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,32 @@
+#define EMBEDDED
+
+#if    __DEBUG
+#define    ENABLE_LOG_DEBUG
+#define    ENABLE_LOG_INFO
+#define    ENABLE_LOG_ERROR
+#endif
+
+//#define HAVE_INIT_SCRIPT
+#define HAVE_BZERO
+#define HAVE_TICK
+
+//#define HAVE_EHCILL
+#define HAVE_BLE
+
+#define ASYNC_BUFFERS 1
+
+
+#define HCI_ACL_PAYLOAD_SIZE 52
+
+// 
+#define MAX_NO_HCI_CONNECTIONS 1
+#define MAX_NO_L2CAP_SERVICES  0
+#define MAX_NO_L2CAP_CHANNELS  0
+#define MAX_NO_RFCOMM_MULTIPLEXERS 0
+#define MAX_NO_RFCOMM_SERVICES 0
+#define MAX_NO_RFCOMM_CHANNELS 0
+#define MAX_NO_DB_MEM_DEVICE_LINK_KEYS  2
+#define MAX_NO_DB_MEM_DEVICE_NAMES 0
+#define MAX_NO_DB_MEM_SERVICES 0
+
+//#include "xprintf.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/debug.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  debug.h
+ *
+ *  allow to funnel debug & error messages 
+ */
+
+#include "config.h"
+#include "hci_dump.h"
+
+#include <stdio.h>
+
+#ifdef ENABLE_LOG_DEBUG
+#ifdef HAVE_HCI_DUMP
+#define log_debug(format, ...)  hci_dump_log(format,  ## __VA_ARGS__)
+#else
+#define log_debug(format, ...)  printf(format,  ## __VA_ARGS__)
+#endif
+#else
+#define log_debug(...)
+#endif
+
+#ifdef ENABLE_LOG_INFO
+#ifdef HAVE_HCI_DUMP
+#define log_info(format, ...)  hci_dump_log(format,  ## __VA_ARGS__)
+#else
+#define log_info(format, ...)  printf(format,  ## __VA_ARGS__)
+#endif
+#else
+#define log_info(...)
+#endif
+
+#ifdef ENABLE_LOG_ERROR
+#ifdef HAVE_HCI_DUMP
+#define log_error(format, ...)  hci_dump_log(format,  ## __VA_ARGS__)
+#else
+#define log_error(format, ...)  printf(format,  ## __VA_ARGS__)
+#endif
+#else
+#define log_error(...)
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/hal_cpu.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  hal_cpu.c
+ *
+ *  Implementation for mbed
+ *
+ */
+
+#include <btstack/hal_cpu.h>
+
+void hal_cpu_disable_irqs(){
+
+}
+
+void hal_cpu_enable_irqs(){
+
+}
+
+void hal_cpu_enable_irqs_and_sleep(){
+    
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/hal_cpu.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  hal_cpu.h
+ *
+ *  Low power mode for MCU requires that IRQs can be first blocked 
+ *  and then unblocked while entering low power mode atomically
+ */
+ 
+void hal_cpu_disable_irqs(void);
+void hal_cpu_enable_irqs(void);
+void hal_cpu_enable_irqs_and_sleep(void);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/hal_tick.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2011 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  hal_tick.c
+ *
+ *  Implementation for mbed
+ *
+ */
+
+#include <btstack/hal_tick.h>
+#include "mbed.h"
+static Ticker tick;
+
+static void dummy_handler(void){};
+
+static void (*tick_handler)(void) = &dummy_handler;
+
+void hal_tick_init(void){
+
+}
+
+void hal_tick_set_handler(void (*handler)(void)){
+    if (handler == NULL){
+        tick_handler = &dummy_handler;
+        return;
+    }
+    tick_handler = handler;
+    tick.attach(tick_handler, 0.25);
+}
+
+int  hal_tick_get_tick_period_in_ms(void){
+    return 250;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/hal_tick.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2011 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  hal_tick.h
+ *
+ *  Hardware abstraction layer for periodic ticks
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+void hal_tick_init(void);
+void hal_tick_set_handler(void (*tick_handler)(void));
+int  hal_tick_get_tick_period_in_ms(void);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/hci.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,1420 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  hci.c
+ *
+ *  Created by Matthias Ringwald on 4/29/09.
+ *
+ */
+
+#include "config.h"
+
+#include "hci.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef EMBEDDED
+#include <unistd.h> // gethostbyname
+#include <btstack/version.h>
+#endif
+
+#include "btstack_memory.h"
+#include "debug.h"
+#include "hci_dump.h"
+
+#include <btstack/hci_cmds.h>
+
+#define HCI_CONNECTION_TIMEOUT_MS 10000
+
+#ifdef USE_BLUETOOL
+#include "bt_control_iphone.h"
+#endif
+
+static void hci_update_scan_enable(void);
+
+// the STACK is here
+static hci_stack_t       hci_stack;
+
+/**
+ * get connection for a given handle
+ *
+ * @return connection OR NULL, if not found
+ */
+hci_connection_t * connection_for_handle(hci_con_handle_t con_handle){
+    linked_item_t *it;
+    for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){
+        if ( ((hci_connection_t *) it)->con_handle == con_handle){
+            return (hci_connection_t *) it;
+        }
+    }
+    return NULL;
+}
+
+static void hci_connection_timeout_handler(timer_source_t *timer){
+    hci_connection_t * connection = (hci_connection_t *) linked_item_get_user(&timer->item);
+#ifdef HAVE_TIME
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    if (tv.tv_sec >= connection->timestamp.tv_sec + HCI_CONNECTION_TIMEOUT_MS/1000) {
+        // connections might be timed out
+        hci_emit_l2cap_check_timeout(connection);
+    }
+#endif
+#ifdef HAVE_TICK
+    if (embedded_get_ticks() > connection->timestamp + embedded_ticks_for_ms(HCI_CONNECTION_TIMEOUT_MS)){
+        // connections might be timed out
+        hci_emit_l2cap_check_timeout(connection);
+    }
+#endif
+    run_loop_set_timer(timer, HCI_CONNECTION_TIMEOUT_MS);
+    run_loop_add_timer(timer);
+}
+
+static void hci_connection_timestamp(hci_connection_t *connection){
+#ifdef HAVE_TIME
+    gettimeofday(&connection->timestamp, NULL);
+#endif
+#ifdef HAVE_TICK
+    connection->timestamp = embedded_get_ticks();
+#endif
+}
+
+/**
+ * create connection for given address
+ *
+ * @return connection OR NULL, if no memory left
+ */
+static hci_connection_t * create_connection_for_addr(bd_addr_t addr){
+    hci_connection_t * conn = (hci_connection_t *) btstack_memory_hci_connection_get();
+    if (!conn) return NULL;
+    BD_ADDR_COPY(conn->address, addr);
+    conn->con_handle = 0xffff;
+    conn->authentication_flags = AUTH_FLAGS_NONE;
+    linked_item_set_user(&conn->timeout.item, conn);
+    conn->timeout.process = hci_connection_timeout_handler;
+    hci_connection_timestamp(conn);
+    conn->acl_recombination_length = 0;
+    conn->acl_recombination_pos = 0;
+    conn->num_acl_packets_sent = 0;
+    linked_list_add(&hci_stack.connections, (linked_item_t *) conn);
+    return conn;
+}
+
+/**
+ * get connection for given address
+ *
+ * @return connection OR NULL, if not found
+ */
+static hci_connection_t * connection_for_address(bd_addr_t address){
+    linked_item_t *it;
+    for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){
+        if ( ! BD_ADDR_CMP( ((hci_connection_t *) it)->address, address) ){
+            return (hci_connection_t *) it;
+        }
+    }
+    return NULL;
+}
+
+inline static void connectionSetAuthenticationFlags(hci_connection_t * conn, hci_authentication_flags_t flags){
+    conn->authentication_flags = (hci_authentication_flags_t)(conn->authentication_flags | flags);
+}
+
+inline static void connectionClearAuthenticationFlags(hci_connection_t * conn, hci_authentication_flags_t flags){
+    conn->authentication_flags = (hci_authentication_flags_t)(conn->authentication_flags & ~flags);
+}
+
+
+/**
+ * add authentication flags and reset timer
+ */
+static void hci_add_connection_flags_for_flipped_bd_addr(uint8_t *bd_addr, hci_authentication_flags_t flags){
+    bd_addr_t addr;
+    bt_flip_addr(addr, *(bd_addr_t *) bd_addr);
+    hci_connection_t * conn = connection_for_address(addr);
+    if (conn) {
+        connectionSetAuthenticationFlags(conn, flags);
+        hci_connection_timestamp(conn);
+    }
+}
+
+int  hci_authentication_active_for_handle(hci_con_handle_t handle){
+    hci_connection_t * conn = connection_for_handle(handle);
+    if (!conn) return 0;
+    if (!conn->authentication_flags) return 0;
+    if (conn->authentication_flags & SENT_LINK_KEY_REPLY) return 0;
+    if (conn->authentication_flags & RECV_LINK_KEY_NOTIFICATION) return 0;
+    return 1;
+}
+
+void hci_drop_link_key_for_bd_addr(bd_addr_t *addr){
+    if (hci_stack.remote_device_db) {
+        hci_stack.remote_device_db->delete_link_key(addr);
+    }
+}
+
+
+/**
+ * count connections
+ */
+static int nr_hci_connections(void){
+    int count = 0;
+    linked_item_t *it;
+    for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next, count++);
+    return count;
+}
+
+/** 
+ * Dummy handler called by HCI
+ */
+static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
+}
+
+uint8_t hci_number_outgoing_packets(hci_con_handle_t handle){
+    hci_connection_t * connection = connection_for_handle(handle);
+    if (!connection) {
+        log_error("hci_number_outgoing_packets connectino for handle %u does not exist!\n", handle);
+        return 0;
+    }
+    return connection->num_acl_packets_sent;
+}
+
+uint8_t hci_number_free_acl_slots(){
+    uint8_t free_slots = hci_stack.total_num_acl_packets;
+    linked_item_t *it;
+    for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){
+        hci_connection_t * connection = (hci_connection_t *) it;
+        if (free_slots < connection->num_acl_packets_sent) {
+            log_error("hci_number_free_acl_slots: sum of outgoing packets > total acl packets!\n");
+            return 0;
+        }
+        free_slots -= connection->num_acl_packets_sent;
+    }
+    return free_slots;
+}
+
+int hci_can_send_packet_now(uint8_t packet_type){
+
+    // check for async hci transport implementations
+    if (hci_stack.hci_transport->can_send_packet_now){
+        if (!hci_stack.hci_transport->can_send_packet_now(packet_type)){
+            return 0;
+        }
+    }
+    
+    // check regular Bluetooth flow control
+    switch (packet_type) {
+        case HCI_ACL_DATA_PACKET:
+            return hci_number_free_acl_slots();
+        case HCI_COMMAND_DATA_PACKET:
+            return hci_stack.num_cmd_packets;
+        default:
+            return 0;
+    }
+}
+
+int hci_send_acl_packet(uint8_t *packet, int size){
+
+    // check for free places on BT module
+    if (!hci_number_free_acl_slots()) return BTSTACK_ACL_BUFFERS_FULL;
+    
+    hci_con_handle_t con_handle = READ_ACL_CONNECTION_HANDLE(packet);
+    hci_connection_t *connection = connection_for_handle( con_handle);
+    if (!connection) return 0;
+    hci_connection_timestamp(connection);
+    
+    // count packet
+    connection->num_acl_packets_sent++;
+    // log_info("hci_send_acl_packet - handle %u, sent %u\n", connection->con_handle, connection->num_acl_packets_sent);
+
+    // send packet 
+    int err = hci_stack.hci_transport->send_packet(HCI_ACL_DATA_PACKET, packet, size);
+    
+    return err;
+}
+
+static void acl_handler(uint8_t *packet, int size){
+
+    // get info
+    hci_con_handle_t con_handle = READ_ACL_CONNECTION_HANDLE(packet);
+    hci_connection_t *conn      = connection_for_handle(con_handle);
+    uint8_t  acl_flags          = READ_ACL_FLAGS(packet);
+    uint16_t acl_length         = READ_ACL_LENGTH(packet);
+
+    // ignore non-registered handle
+    if (!conn){
+        log_error( "hci.c: acl_handler called with non-registered handle %u!\n" , con_handle);
+        return;
+    }
+    
+    // update idle timestamp
+    hci_connection_timestamp(conn);
+    
+    // handle different packet types
+    switch (acl_flags & 0x03) {
+            
+        case 0x01: // continuation fragment
+            
+            // sanity check
+            if (conn->acl_recombination_pos == 0) {
+                log_error( "ACL Cont Fragment but no first fragment for handle 0x%02x\n", con_handle);
+                return;
+            }
+            
+            // append fragment payload (header already stored)
+            memcpy(&conn->acl_recombination_buffer[conn->acl_recombination_pos], &packet[4], acl_length );
+            conn->acl_recombination_pos += acl_length;
+            
+            // log_error( "ACL Cont Fragment: acl_len %u, combined_len %u, l2cap_len %u\n", acl_length,
+            //        conn->acl_recombination_pos, conn->acl_recombination_length);  
+            
+            // forward complete L2CAP packet if complete. 
+            if (conn->acl_recombination_pos >= conn->acl_recombination_length + 4 + 4){ // pos already incl. ACL header
+                
+                hci_stack.packet_handler(HCI_ACL_DATA_PACKET, conn->acl_recombination_buffer, conn->acl_recombination_pos);
+                // reset recombination buffer
+                conn->acl_recombination_length = 0;
+                conn->acl_recombination_pos = 0;
+            }
+            break;
+            
+        case 0x02: { // first fragment
+            
+            // sanity check
+            if (conn->acl_recombination_pos) {
+                log_error( "ACL First Fragment but data in buffer for handle 0x%02x\n", con_handle);
+                return;
+            }
+
+            // peek into L2CAP packet!
+            uint16_t l2cap_length = READ_L2CAP_LENGTH( packet );
+
+            // log_error( "ACL First Fragment: acl_len %u, l2cap_len %u\n", acl_length, l2cap_length);
+
+            // compare fragment size to L2CAP packet size
+            if (acl_length >= l2cap_length + 4){
+                
+                // forward fragment as L2CAP packet
+                hci_stack.packet_handler(HCI_ACL_DATA_PACKET, packet, acl_length + 4);
+            
+            } else {
+                // store first fragment and tweak acl length for complete package
+                memcpy(conn->acl_recombination_buffer, packet, acl_length + 4);
+                conn->acl_recombination_pos    = acl_length + 4;
+                conn->acl_recombination_length = l2cap_length;
+                bt_store_16(conn->acl_recombination_buffer, 2, l2cap_length +4);
+            }
+            break;
+            
+        } 
+        default:
+            log_error( "hci.c: acl_handler called with invalid packet boundary flags %u\n", acl_flags & 0x03);
+            return;
+    }
+    
+    // execute main loop
+    hci_run();
+}
+
+static void hci_shutdown_connection(hci_connection_t *conn){
+    log_info("Connection closed: handle %u, %s\n", conn->con_handle, bd_addr_to_str(conn->address));
+
+    // cancel all l2cap connections
+    hci_emit_disconnection_complete(conn->con_handle, 0x16);    // terminated by local host
+
+    run_loop_remove_timer(&conn->timeout);
+    
+    linked_list_remove(&hci_stack.connections, (linked_item_t *) conn);
+    btstack_memory_hci_connection_free( conn );
+    
+    // now it's gone
+    hci_emit_nr_connections_changed();
+}
+
+static const uint16_t packet_type_sizes[] = {
+    0, HCI_ACL_2DH1_SIZE, HCI_ACL_3DH1_SIZE, HCI_ACL_DM1_SIZE,
+    HCI_ACL_DH1_SIZE, 0, 0, 0,
+    HCI_ACL_2DH3_SIZE, HCI_ACL_3DH3_SIZE, HCI_ACL_DM3_SIZE, HCI_ACL_DH3_SIZE,
+    HCI_ACL_2DH5_SIZE, HCI_ACL_3DH5_SIZE, HCI_ACL_DM5_SIZE, HCI_ACL_DH5_SIZE
+};
+
+static uint16_t hci_acl_packet_types_for_buffer_size(uint16_t buffer_size){
+    uint16_t packet_types = 0;
+    int i;
+    for (i=0;i<16;i++){
+        if (packet_type_sizes[i] == 0) continue;
+        if (packet_type_sizes[i] <= buffer_size){
+            packet_types |= 1 << i;
+        }
+    }
+    // flip bits for "may not be used"
+    packet_types ^= 0x3306;
+    return packet_types;
+}
+
+uint16_t hci_usable_acl_packet_types(void){
+    return hci_stack.packet_types;
+}
+
+uint8_t* hci_get_outgoing_acl_packet_buffer(void){
+    // hci packet buffer is >= acl data packet length
+    return hci_stack.hci_packet_buffer;
+}
+
+uint16_t hci_max_acl_data_packet_length(){
+    return hci_stack.acl_data_packet_length;
+}
+
+// avoid huge local variables
+#ifndef EMBEDDED
+static device_name_t device_name;
+#endif
+static void event_handler(uint8_t *packet, int size){
+    bd_addr_t addr;
+    uint8_t link_type;
+    hci_con_handle_t handle;
+    hci_connection_t * conn;
+    int i;
+        
+    // printf("HCI:EVENT:%02x\n", packet[0]);
+    
+    switch (packet[0]) {
+                        
+        case HCI_EVENT_COMMAND_COMPLETE:
+            // get num cmd packets
+            // log_info("HCI_EVENT_COMMAND_COMPLETE cmds old %u - new %u\n", hci_stack.num_cmd_packets, packet[2]);
+            hci_stack.num_cmd_packets = packet[2];
+            
+            if (COMMAND_COMPLETE_EVENT(packet, hci_read_buffer_size)){
+                // from offset 5
+                // status 
+                // "The HC_ACL_Data_Packet_Length return parameter will be used to determine the size of the L2CAP segments contained in ACL Data Packets"
+                hci_stack.acl_data_packet_length = READ_BT_16(packet, 6);
+                // ignore: SCO data packet len (8)
+                hci_stack.total_num_acl_packets  = packet[9];
+                // ignore: total num SCO packets
+                if (hci_stack.state == HCI_STATE_INITIALIZING){
+                    // determine usable ACL payload size
+                    if (HCI_ACL_PAYLOAD_SIZE < hci_stack.acl_data_packet_length){
+                        hci_stack.acl_data_packet_length = HCI_ACL_PAYLOAD_SIZE;
+                    }
+                    // determine usable ACL packet types
+                    hci_stack.packet_types = hci_acl_packet_types_for_buffer_size(hci_stack.acl_data_packet_length);
+                    
+                    log_info("hci_read_buffer_size: used size %u, count %u, packet types %04x\n",
+                             hci_stack.acl_data_packet_length, hci_stack.total_num_acl_packets, hci_stack.packet_types); 
+                }
+            }
+            // Dump local address
+            if (COMMAND_COMPLETE_EVENT(packet, hci_read_bd_addr)) {
+                bd_addr_t addr;
+                bt_flip_addr(addr, &packet[OFFSET_OF_DATA_IN_COMMAND_COMPLETE + 1]);
+                log_info("Local Address, Status: 0x%02x: Addr: %s\n",
+                    packet[OFFSET_OF_DATA_IN_COMMAND_COMPLETE], bd_addr_to_str(addr));
+            }
+            if (COMMAND_COMPLETE_EVENT(packet, hci_write_scan_enable)){
+                hci_emit_discoverable_enabled(hci_stack.discoverable);
+            }
+            break;
+            
+        case HCI_EVENT_COMMAND_STATUS:
+            // get num cmd packets
+            // log_info("HCI_EVENT_COMMAND_STATUS cmds - old %u - new %u\n", hci_stack.num_cmd_packets, packet[3]);
+            hci_stack.num_cmd_packets = packet[3];
+            break;
+            
+        case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS:
+            for (i=0; i<packet[2];i++){
+                handle = READ_BT_16(packet, 3 + 2*i);
+                uint16_t num_packets = READ_BT_16(packet, 3 + packet[2]*2 + 2*i);
+                conn = connection_for_handle(handle);
+                if (!conn){
+                    log_error("hci_number_completed_packet lists unused con handle %u\n", handle);
+                    continue;
+                }
+                conn->num_acl_packets_sent -= num_packets;
+                // log_info("hci_number_completed_packet %u processed for handle %u, outstanding %u\n", num_packets, handle, conn->num_acl_packets_sent);
+            }
+            break;
+            
+        case HCI_EVENT_CONNECTION_REQUEST:
+            bt_flip_addr(addr, &packet[2]);
+            // TODO: eval COD 8-10
+            link_type = packet[11];
+            log_info("Connection_incoming: %s, type %u\n", bd_addr_to_str(addr), link_type);
+            if (link_type == 1) { // ACL
+                conn = connection_for_address(addr);
+                if (!conn) {
+                    conn = create_connection_for_addr(addr);
+                }
+                if (!conn) {
+                    // CONNECTION REJECTED DUE TO LIMITED RESOURCES (0X0D)
+                    hci_stack.decline_reason = 0x0d;
+                    BD_ADDR_COPY(hci_stack.decline_addr, addr);
+                    break;
+                }
+                conn->state = RECEIVED_CONNECTION_REQUEST;
+                hci_run();
+            } else {
+                // SYNCHRONOUS CONNECTION LIMIT TO A DEVICE EXCEEDED (0X0A)
+                hci_stack.decline_reason = 0x0a;
+                BD_ADDR_COPY(hci_stack.decline_addr, addr);
+            }
+            break;
+            
+        case HCI_EVENT_CONNECTION_COMPLETE:
+            // Connection management
+            bt_flip_addr(addr, &packet[5]);
+            log_info("Connection_complete (status=%u) %s\n", packet[2], bd_addr_to_str(addr));
+            conn = connection_for_address(addr);
+            if (conn) {
+                if (!packet[2]){
+                    conn->state = OPEN;
+                    conn->con_handle = READ_BT_16(packet, 3);
+                    
+                    // restart timer
+                    run_loop_set_timer(&conn->timeout, HCI_CONNECTION_TIMEOUT_MS);
+                    run_loop_add_timer(&conn->timeout);
+                    
+                    log_info("New connection: handle %u, %s\n", conn->con_handle, bd_addr_to_str(conn->address));
+                    
+                    hci_emit_nr_connections_changed();
+                } else {
+                    // connection failed, remove entry
+                    linked_list_remove(&hci_stack.connections, (linked_item_t *) conn);
+                    btstack_memory_hci_connection_free( conn );
+                    
+                    // if authentication error, also delete link key
+                    if (packet[2] == 0x05) {
+                        hci_drop_link_key_for_bd_addr(&addr);
+                    }
+                }
+            }
+            break;
+
+        case HCI_EVENT_LINK_KEY_REQUEST:
+            log_info("HCI_EVENT_LINK_KEY_REQUEST\n");
+            hci_add_connection_flags_for_flipped_bd_addr(&packet[2], RECV_LINK_KEY_REQUEST);
+            if (!hci_stack.remote_device_db) break;
+            hci_add_connection_flags_for_flipped_bd_addr(&packet[2], HANDLE_LINK_KEY_REQUEST);
+            hci_run();
+            // request handled by hci_run() as HANDLE_LINK_KEY_REQUEST gets set
+            return;
+            
+        case HCI_EVENT_LINK_KEY_NOTIFICATION:
+            hci_add_connection_flags_for_flipped_bd_addr(&packet[2], RECV_LINK_KEY_NOTIFICATION);
+            if (!hci_stack.remote_device_db) break;
+            bt_flip_addr(addr, &packet[2]);
+            hci_stack.remote_device_db->put_link_key(&addr, (link_key_t *) &packet[8]);
+            // still forward event to allow dismiss of pairing dialog
+            break;
+            
+        case HCI_EVENT_PIN_CODE_REQUEST:
+            hci_add_connection_flags_for_flipped_bd_addr(&packet[2], RECV_PIN_CODE_REQUEST);
+            // PIN CODE REQUEST means the link key request didn't succee -> delete stored link key
+            if (!hci_stack.remote_device_db) break;
+            bt_flip_addr(addr, &packet[2]);
+            hci_stack.remote_device_db->delete_link_key(&addr);
+            break;
+            
+#ifndef EMBEDDED
+        case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE:
+            if (!hci_stack.remote_device_db) break;
+            if (packet[2]) break; // status not ok
+            bt_flip_addr(addr, &packet[3]);
+            // fix for invalid remote names - terminate on 0xff
+            for (i=0; i<248;i++){
+                if (packet[9+i] == 0xff){
+                    packet[9+i] = 0;
+                    break;
+                }
+            }
+            memset(&device_name, 0, sizeof(device_name_t));
+            strncpy((char*) device_name, (char*) &packet[9], 248);
+            hci_stack.remote_device_db->put_name(&addr, &device_name);
+            break;
+            
+        case HCI_EVENT_INQUIRY_RESULT:
+        case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI:
+            if (!hci_stack.remote_device_db) break;
+            // first send inq result packet
+            hci_stack.packet_handler(HCI_EVENT_PACKET, packet, size);
+            // then send cached remote names
+            for (i=0; i<packet[2];i++){
+                bt_flip_addr(addr, &packet[3+i*6]);
+                if (hci_stack.remote_device_db->get_name(&addr, &device_name)){
+                    hci_emit_remote_name_cached(&addr, &device_name);
+                }
+            }
+            return;
+#endif
+            
+        case HCI_EVENT_DISCONNECTION_COMPLETE:
+            if (!packet[2]){
+                handle = READ_BT_16(packet, 3);
+                hci_connection_t * conn = connection_for_handle(handle);
+                if (conn) {
+                    hci_shutdown_connection(conn);
+                }
+            }
+            break;
+            
+        case HCI_EVENT_HARDWARE_ERROR:
+            if(hci_stack.control->hw_error){
+                (*hci_stack.control->hw_error)();
+            }
+            break;
+
+#ifdef HAVE_BLE
+        case HCI_EVENT_LE_META:
+            switch (packet[2]) {
+                case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
+                    // Connection management
+                    bt_flip_addr(addr, &packet[8]);
+                    log_info("LE Connection_complete (status=%u) %s\n", packet[3], bd_addr_to_str(addr));
+                    // LE connections are auto-accepted, so just create a connection if there isn't one already
+                    conn = connection_for_address(addr);
+                    if (packet[3]){
+                        if (conn){
+                            // outgoing connection failed, remove entry
+                            linked_list_remove(&hci_stack.connections, (linked_item_t *) conn);
+                            btstack_memory_hci_connection_free( conn );
+                            
+                        }
+                        // if authentication error, also delete link key
+                        if (packet[3] == 0x05) {
+                            hci_drop_link_key_for_bd_addr(&addr);
+                        }
+                        break;
+                    }
+                    if (!conn){
+                        conn = create_connection_for_addr(addr);
+                    }
+                    if (!conn){
+                        // no memory
+                        break;
+                    }
+                    
+                    conn->state = OPEN;
+                    conn->con_handle = READ_BT_16(packet, 4);
+                    
+                    // TODO: store - role, peer address type, conn_interval, conn_latency, supervision timeout, master clock
+
+                    // restart timer
+                    // run_loop_set_timer(&conn->timeout, HCI_CONNECTION_TIMEOUT_MS);
+                    // run_loop_add_timer(&conn->timeout);
+                    
+                    log_info("New connection: handle %u, %s\n", conn->con_handle, bd_addr_to_str(conn->address));
+                    
+                    hci_emit_nr_connections_changed();
+                    break;
+                    
+                default:
+                    break;
+            }
+            break;
+#endif            
+            
+        default:
+            break;
+    }
+
+    // handle BT initialization
+    if (hci_stack.state == HCI_STATE_INITIALIZING){
+        // handle H4 synchronization loss on restart
+        // if (hci_stack.substate == 1 && packet[0] == HCI_EVENT_HARDWARE_ERROR){
+        //    hci_stack.substate = 0;
+        // }
+        // handle normal init sequence
+        if (hci_stack.substate % 2){
+            // odd: waiting for event
+            if (packet[0] == HCI_EVENT_COMMAND_COMPLETE){
+                hci_stack.substate++;
+            }
+        }
+    }
+    
+    // help with BT sleep
+    if (hci_stack.state == HCI_STATE_FALLING_ASLEEP
+        && hci_stack.substate == 1
+        && COMMAND_COMPLETE_EVENT(packet, hci_write_scan_enable)){
+        hci_stack.substate++;
+    }
+    
+    hci_stack.packet_handler(HCI_EVENT_PACKET, packet, size);
+    
+    // execute main loop
+    hci_run();
+}
+
+void packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
+    switch (packet_type) {
+        case HCI_EVENT_PACKET:
+            event_handler(packet, size);
+            break;
+        case HCI_ACL_DATA_PACKET:
+            acl_handler(packet, size);
+            break;
+        default:
+            break;
+    }
+}
+
+/** Register HCI packet handlers */
+void hci_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
+    hci_stack.packet_handler = handler;
+}
+
+void hci_init(hci_transport_t *transport, void *config, bt_control_t *control, remote_device_db_t const* remote_device_db){
+    
+    // reference to use transport layer implementation
+    hci_stack.hci_transport = transport;
+    
+    // references to used control implementation
+    hci_stack.control = control;
+    
+    // reference to used config
+    hci_stack.config = config;
+    
+    // no connections yet
+    hci_stack.connections = NULL;
+    hci_stack.discoverable = 0;
+    hci_stack.connectable = 0;
+    
+    // no pending cmds
+    hci_stack.decline_reason = 0;
+    hci_stack.new_scan_enable_value = 0xff;
+    
+    // higher level handler
+    hci_stack.packet_handler = dummy_handler;
+
+    // store and open remote device db
+    hci_stack.remote_device_db = remote_device_db;
+    if (hci_stack.remote_device_db) {
+        hci_stack.remote_device_db->open();
+    }
+    
+    // max acl payload size defined in config.h
+    hci_stack.acl_data_packet_length = HCI_ACL_PAYLOAD_SIZE;
+    
+    // register packet handlers with transport
+    transport->register_packet_handler(&packet_handler);
+
+    hci_stack.state = HCI_STATE_OFF;
+}
+
+void hci_close(){
+    // close remote device db
+    if (hci_stack.remote_device_db) {
+        hci_stack.remote_device_db->close();
+    }
+    while (hci_stack.connections) {
+        hci_shutdown_connection((hci_connection_t *) hci_stack.connections);
+}
+    hci_power_control(HCI_POWER_OFF);
+}
+
+// State-Module-Driver overview
+// state                    module  low-level 
+// HCI_STATE_OFF             off      close
+// HCI_STATE_INITIALIZING,   on       open
+// HCI_STATE_WORKING,        on       open
+// HCI_STATE_HALTING,        on       open
+// HCI_STATE_SLEEPING,    off/sleep   close
+// HCI_STATE_FALLING_ASLEEP  on       open
+
+static int hci_power_control_on(void){
+    
+    // power on
+    int err = 0;
+    if (hci_stack.control && hci_stack.control->on){
+        err = (*hci_stack.control->on)(hci_stack.config);
+    }
+    if (err){
+        log_error( "POWER_ON failed\n");
+        hci_emit_hci_open_failed();
+        return err;
+    }
+    
+    // open low-level device
+    err = hci_stack.hci_transport->open(hci_stack.config);
+    if (err){
+        log_error( "HCI_INIT failed, turning Bluetooth off again\n");
+        if (hci_stack.control && hci_stack.control->off){
+            (*hci_stack.control->off)(hci_stack.config);
+        }
+        hci_emit_hci_open_failed();
+        return err;
+    }
+    return 0;
+}
+
+static void hci_power_control_off(void){
+    
+    log_info("hci_power_control_off\n");
+
+    // close low-level device
+    hci_stack.hci_transport->close(hci_stack.config);
+
+    log_info("hci_power_control_off - hci_transport closed\n");
+    
+    // power off
+    if (hci_stack.control && hci_stack.control->off){
+        (*hci_stack.control->off)(hci_stack.config);
+    }
+    
+    log_info("hci_power_control_off - control closed\n");
+
+    hci_stack.state = HCI_STATE_OFF;
+}
+
+static void hci_power_control_sleep(void){
+    
+    log_info("hci_power_control_sleep\n");
+    
+#if 0
+    // don't close serial port during sleep
+    
+    // close low-level device
+    hci_stack.hci_transport->close(hci_stack.config);
+#endif
+    
+    // sleep mode
+    if (hci_stack.control && hci_stack.control->sleep){
+        (*hci_stack.control->sleep)(hci_stack.config);
+    }
+    
+    hci_stack.state = HCI_STATE_SLEEPING;
+}
+
+static int hci_power_control_wake(void){
+    
+    log_info("hci_power_control_wake\n");
+
+    // wake on
+    if (hci_stack.control && hci_stack.control->wake){
+        (*hci_stack.control->wake)(hci_stack.config);
+    }
+    
+#if 0
+    // open low-level device
+    int err = hci_stack.hci_transport->open(hci_stack.config);
+    if (err){
+        log_error( "HCI_INIT failed, turning Bluetooth off again\n");
+        if (hci_stack.control && hci_stack.control->off){
+            (*hci_stack.control->off)(hci_stack.config);
+        }
+        hci_emit_hci_open_failed();
+        return err;
+    }
+#endif
+    
+    return 0;
+}
+
+
+int hci_power_control(HCI_POWER_MODE power_mode){
+    
+    log_info("hci_power_control: %u, current mode %u\n", power_mode, hci_stack.state);
+    
+    int err = 0;
+    switch (hci_stack.state){
+            
+        case HCI_STATE_OFF:
+            switch (power_mode){
+                case HCI_POWER_ON:
+                    err = hci_power_control_on();
+                    if (err) return err;
+                    // set up state machine
+                    hci_stack.num_cmd_packets = 1; // assume that one cmd can be sent
+                    hci_stack.state = HCI_STATE_INITIALIZING;
+                    hci_stack.substate = 0;
+                    break;
+                case HCI_POWER_OFF:
+                    // do nothing
+                    break;  
+                case HCI_POWER_SLEEP:
+                    // do nothing (with SLEEP == OFF)
+                    break;
+            }
+            break;
+            
+        case HCI_STATE_INITIALIZING:
+            switch (power_mode){
+                case HCI_POWER_ON:
+                    // do nothing
+                    break;
+                case HCI_POWER_OFF:
+                    // no connections yet, just turn it off
+                    hci_power_control_off();
+                    break;  
+                case HCI_POWER_SLEEP:
+                    // no connections yet, just turn it off
+                    hci_power_control_sleep();
+                    break;
+            }
+            break;
+            
+        case HCI_STATE_WORKING:
+            switch (power_mode){
+                case HCI_POWER_ON:
+                    // do nothing
+                    break;
+                case HCI_POWER_OFF:
+                    // see hci_run
+                    hci_stack.state = HCI_STATE_HALTING;
+                    break;  
+                case HCI_POWER_SLEEP:
+                    // see hci_run
+                    hci_stack.state = HCI_STATE_FALLING_ASLEEP;
+                    hci_stack.substate = 0;
+                    break;
+            }
+            break;
+            
+        case HCI_STATE_HALTING:
+            switch (power_mode){
+                case HCI_POWER_ON:
+                    // set up state machine
+                    hci_stack.state = HCI_STATE_INITIALIZING;
+                    hci_stack.substate = 0;
+                    break;
+                case HCI_POWER_OFF:
+                    // do nothing
+                    break;  
+                case HCI_POWER_SLEEP:
+                    // see hci_run
+                    hci_stack.state = HCI_STATE_FALLING_ASLEEP;
+                    hci_stack.substate = 0;
+                    break;
+            }
+            break;
+            
+        case HCI_STATE_FALLING_ASLEEP:
+            switch (power_mode){
+                case HCI_POWER_ON:
+
+#if defined(USE_POWERMANAGEMENT) && defined(USE_BLUETOOL)
+                    // nothing to do, if H4 supports power management
+                    if (bt_control_iphone_power_management_enabled()){
+                        hci_stack.state = HCI_STATE_INITIALIZING;
+                        hci_stack.substate = 6;
+                        break;
+                    }
+#endif
+                    // set up state machine
+                    hci_stack.num_cmd_packets = 1; // assume that one cmd can be sent
+                    hci_stack.state = HCI_STATE_INITIALIZING;
+                    hci_stack.substate = 0;
+                    break;
+                case HCI_POWER_OFF:
+                    // see hci_run
+                    hci_stack.state = HCI_STATE_HALTING;
+                    break;  
+                case HCI_POWER_SLEEP:
+                    // do nothing
+                    break;
+            }
+            break;
+            
+        case HCI_STATE_SLEEPING:
+            switch (power_mode){
+                case HCI_POWER_ON:
+                    
+#if defined(USE_POWERMANAGEMENT) && defined(USE_BLUETOOL)
+                    // nothing to do, if H4 supports power management
+                    if (bt_control_iphone_power_management_enabled()){
+                        hci_stack.state = HCI_STATE_INITIALIZING;
+                        hci_stack.substate = 6;
+                        hci_update_scan_enable();
+                        break;
+                    }
+#endif
+                    err = hci_power_control_wake();
+                    if (err) return err;
+                    // set up state machine
+                    hci_stack.num_cmd_packets = 1; // assume that one cmd can be sent
+                    hci_stack.state = HCI_STATE_INITIALIZING;
+                    hci_stack.substate = 0;
+                    break;
+                case HCI_POWER_OFF:
+                    hci_stack.state = HCI_STATE_HALTING;
+                    break;  
+                case HCI_POWER_SLEEP:
+                    // do nothing
+                    break;
+            }
+            break;
+    }
+
+    // create internal event
+    hci_emit_state();
+    
+    // trigger next/first action
+    hci_run();
+    
+    return 0;
+}
+
+static void hci_update_scan_enable(void){
+    // 2 = page scan, 1 = inq scan
+    hci_stack.new_scan_enable_value  = hci_stack.connectable << 1 | hci_stack.discoverable;
+    hci_run();
+}
+
+void hci_discoverable_control(uint8_t enable){
+    if (enable) enable = 1; // normalize argument
+    
+    if (hci_stack.discoverable == enable){
+        hci_emit_discoverable_enabled(hci_stack.discoverable);
+        return;
+    }
+
+    hci_stack.discoverable = enable;
+    hci_update_scan_enable();
+}
+
+void hci_connectable_control(uint8_t enable){
+    if (enable) enable = 1; // normalize argument
+    
+    // don't emit event
+    if (hci_stack.connectable == enable) return;
+
+    hci_stack.connectable = enable;
+    hci_update_scan_enable();
+}
+
+void hci_run(){
+        
+    hci_connection_t * connection;
+    linked_item_t * it;
+    
+    if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+
+    // global/non-connection oriented commands
+    
+    // decline incoming connections
+    if (hci_stack.decline_reason){
+        uint8_t reason = hci_stack.decline_reason;
+        hci_stack.decline_reason = 0;
+        hci_send_cmd(&hci_reject_connection_request, hci_stack.decline_addr, reason);
+    }
+
+    if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+
+    // send scan enable
+    if (hci_stack.state == HCI_STATE_WORKING && hci_stack.new_scan_enable_value != 0xff){
+        hci_send_cmd(&hci_write_scan_enable, hci_stack.new_scan_enable_value);
+        hci_stack.new_scan_enable_value = 0xff;
+    }
+    
+    // send pending HCI commands
+    for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){
+
+        if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+
+        connection = (hci_connection_t *) it;
+        
+        if (connection->state == RECEIVED_CONNECTION_REQUEST){
+            log_info("sending hci_accept_connection_request\n");
+            hci_send_cmd(&hci_accept_connection_request, connection->address, 1);
+            connection->state = ACCEPTED_CONNECTION_REQUEST;
+        }
+
+        if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+        
+        if (connection->authentication_flags & HANDLE_LINK_KEY_REQUEST){
+            link_key_t link_key;
+            log_info("responding to link key request\n");
+            if ( hci_stack.remote_device_db->get_link_key( &connection->address, &link_key)){
+               hci_send_cmd(&hci_link_key_request_reply, connection->address, &link_key);
+            } else {
+               hci_send_cmd(&hci_link_key_request_negative_reply, connection->address);
+            }
+            connectionClearAuthenticationFlags(connection, HANDLE_LINK_KEY_REQUEST);
+        }
+    }
+
+    if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+        
+    switch (hci_stack.state){
+        case HCI_STATE_INITIALIZING:
+            // log_info("hci_init: substate %u\n", hci_stack.substate);
+            if (hci_stack.substate % 2) {
+                // odd: waiting for command completion
+                return;
+            }
+            switch (hci_stack.substate >> 1){
+                case 0: // RESET
+                    hci_send_cmd(&hci_reset);
+                    if (hci_stack.config == 0 || ((hci_uart_config_t *)hci_stack.config)->baudrate_main == 0){
+                        // skip baud change
+                        hci_stack.substate = 4; // >> 1 = 2
+                    }
+                    break;
+                case 1: // SEND BAUD CHANGE
+                    hci_stack.control->baudrate_cmd(hci_stack.config, ((hci_uart_config_t *)hci_stack.config)->baudrate_main, hci_stack.hci_packet_buffer);
+                    hci_send_cmd_packet(hci_stack.hci_packet_buffer, 3 + hci_stack.hci_packet_buffer[2]);
+                    break;
+                case 2: // LOCAL BAUD CHANGE
+                    hci_stack.hci_transport->set_baudrate(((hci_uart_config_t *)hci_stack.config)->baudrate_main);
+                    hci_stack.substate += 2;
+                    // break missing here for fall through
+                    
+                case 3:
+                    // custom initialization
+                    if (hci_stack.control && hci_stack.control->next_cmd){
+                        int valid_cmd = (*hci_stack.control->next_cmd)(hci_stack.config, hci_stack.hci_packet_buffer);
+                        if (valid_cmd){
+                            int size = 3 + hci_stack.hci_packet_buffer[2];
+                            hci_stack.hci_transport->send_packet(HCI_COMMAND_DATA_PACKET, hci_stack.hci_packet_buffer, size);
+                            hci_stack.substate = 4; // more init commands
+                            break;
+                        }
+                        log_info("hci_run: init script done\n\r");
+                    }
+                    // otherwise continue
+                    hci_send_cmd(&hci_read_bd_addr);
+                    break;
+                case 4:
+                    hci_send_cmd(&hci_read_buffer_size);
+                    break;
+                case 5:
+                    // ca. 15 sec
+                    hci_send_cmd(&hci_write_page_timeout, 0x6000);
+                    break;
+                case 6:
+                    hci_send_cmd(&hci_write_scan_enable, (hci_stack.connectable << 1) | hci_stack.discoverable); // page scan
+                    break;
+                case 7:
+#ifndef EMBEDDED
+                {
+                    char hostname[30];
+                    gethostname(hostname, 30);
+                    hostname[29] = '\0';
+                    hci_send_cmd(&hci_write_local_name, hostname);
+                    break;
+                }
+                case 8:
+#ifdef USE_BLUETOOL
+                    hci_send_cmd(&hci_write_class_of_device, 0x007a020c); // Smartphone
+                    break;
+                    
+                case 9:
+#endif
+#endif
+                    // done.
+                    hci_stack.state = HCI_STATE_WORKING;
+                    hci_emit_state();
+                    break;
+                default:
+                    break;
+            }
+            hci_stack.substate++;
+            break;
+            
+        case HCI_STATE_HALTING:
+
+            log_info("HCI_STATE_HALTING\n");
+            // close all open connections
+            connection =  (hci_connection_t *) hci_stack.connections;
+            if (connection){
+                
+                // send disconnect
+                if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+                
+                log_info("HCI_STATE_HALTING, connection %p, handle %u\n", connection, (uint16_t)connection->con_handle);
+                hci_send_cmd(&hci_disconnect, connection->con_handle, 0x13);  // remote closed connection
+
+                // send disconnected event right away - causes higher layer connections to get closed, too.
+                hci_shutdown_connection(connection);
+                return;
+            }
+            log_info("HCI_STATE_HALTING, calling off\n");
+            
+            // switch mode
+            hci_power_control_off();
+            
+            log_info("HCI_STATE_HALTING, emitting state\n");
+            hci_emit_state();
+            log_info("HCI_STATE_HALTING, done\n");
+            break;
+            
+        case HCI_STATE_FALLING_ASLEEP:
+            switch(hci_stack.substate) {
+                case 0:
+                    log_info("HCI_STATE_FALLING_ASLEEP\n");
+                    // close all open connections
+                    connection =  (hci_connection_t *) hci_stack.connections;
+
+#if defined(USE_POWERMANAGEMENT) && defined(USE_BLUETOOL)
+                    // don't close connections, if H4 supports power management
+                    if (bt_control_iphone_power_management_enabled()){
+                        connection = NULL;
+                    }
+#endif
+                    if (connection){
+                        
+                        // send disconnect
+                        if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+
+                        log_info("HCI_STATE_FALLING_ASLEEP, connection %p, handle %u\n", connection, (uint16_t)connection->con_handle);
+                        hci_send_cmd(&hci_disconnect, connection->con_handle, 0x13);  // remote closed connection
+                        
+                        // send disconnected event right away - causes higher layer connections to get closed, too.
+                        hci_shutdown_connection(connection);
+                        return;
+                    }
+                    
+                    // disable page and inquiry scan
+                    if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+                    
+                    log_info("HCI_STATE_HALTING, disabling inq cans\n");
+                    hci_send_cmd(&hci_write_scan_enable, hci_stack.connectable << 1); // drop inquiry scan but keep page scan
+                    
+                    // continue in next sub state
+                    hci_stack.substate++;
+                    break;
+                case 1:
+                    // wait for command complete "hci_write_scan_enable" in event_handler();
+                    break;
+                case 2:
+                    log_info("HCI_STATE_HALTING, calling sleep\n");
+#if defined(USE_POWERMANAGEMENT) && defined(USE_BLUETOOL)
+                    // don't actually go to sleep, if H4 supports power management
+                    if (bt_control_iphone_power_management_enabled()){
+                        // SLEEP MODE reached
+                        hci_stack.state = HCI_STATE_SLEEPING; 
+                        hci_emit_state();
+                        break;
+                    }
+#endif
+                    // switch mode
+                    hci_power_control_sleep();  // changes hci_stack.state to SLEEP
+                    hci_emit_state();
+                    break;
+                    
+                default:
+                    break;
+            }
+            break;
+            
+        default:
+            break;
+    }
+}
+
+int hci_send_cmd_packet(uint8_t *packet, int size){
+    bd_addr_t addr;
+    hci_connection_t * conn;
+    // house-keeping
+    
+    // create_connection?
+    if (IS_COMMAND(packet, hci_create_connection)){
+        bt_flip_addr(addr, &packet[3]);
+        log_info("Create_connection to %s\n", bd_addr_to_str(addr));
+        conn = connection_for_address(addr);
+        if (conn) {
+            // if connection exists
+            if (conn->state == OPEN) {
+                // and OPEN, emit connection complete command
+                hci_emit_connection_complete(conn, 0);
+            }
+            //    otherwise, just ignore as it is already in the open process
+            return 0; // don't sent packet to controller
+            
+        }
+        // create connection struct and register, state = SENT_CREATE_CONNECTION
+        conn = create_connection_for_addr(addr);
+        if (!conn){
+            // notify client that alloc failed
+            hci_emit_connection_complete(conn, BTSTACK_MEMORY_ALLOC_FAILED);
+            return 0; // don't sent packet to controller
+        }
+        conn->state = SENT_CREATE_CONNECTION;
+    }
+    
+    if (IS_COMMAND(packet, hci_link_key_request_reply)){
+        hci_add_connection_flags_for_flipped_bd_addr(&packet[3], SENT_LINK_KEY_REPLY);
+    }
+    if (IS_COMMAND(packet, hci_link_key_request_negative_reply)){
+        hci_add_connection_flags_for_flipped_bd_addr(&packet[3], SENT_LINK_KEY_NEGATIVE_REQUEST);
+    }
+    if (IS_COMMAND(packet, hci_pin_code_request_reply)){
+        hci_add_connection_flags_for_flipped_bd_addr(&packet[3], SENT_PIN_CODE_REPLY);
+    }
+    if (IS_COMMAND(packet, hci_pin_code_request_negative_reply)){
+        hci_add_connection_flags_for_flipped_bd_addr(&packet[3], SENT_PIN_CODE_NEGATIVE_REPLY);
+    }
+    
+    if (IS_COMMAND(packet, hci_delete_stored_link_key)){
+        if (hci_stack.remote_device_db){
+            bt_flip_addr(addr, &packet[3]);
+            hci_stack.remote_device_db->delete_link_key(&addr);
+        }
+    }
+    
+    hci_stack.num_cmd_packets--;
+    return hci_stack.hci_transport->send_packet(HCI_COMMAND_DATA_PACKET, packet, size);
+}
+
+/**
+ * pre: numcmds >= 0 - it's allowed to send a command to the controller
+ */
+int hci_send_cmd(const hci_cmd_t *cmd, ...){
+    va_list argptr;
+    va_start(argptr, cmd);
+    uint16_t size = hci_create_cmd_internal(hci_stack.hci_packet_buffer, cmd, argptr);
+    va_end(argptr);
+    return hci_send_cmd_packet(hci_stack.hci_packet_buffer, size);
+}
+
+// Create various non-HCI events. 
+// TODO: generalize, use table similar to hci_create_command
+
+void hci_emit_state(){
+    log_info("BTSTACK_EVENT_STATE %u\n", hci_stack.state);
+    uint8_t event[3];
+    event[0] = BTSTACK_EVENT_STATE;
+    event[1] = sizeof(event) - 2;
+    event[2] = hci_stack.state;
+    hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+    hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_connection_complete(hci_connection_t *conn, uint8_t status){
+    uint8_t event[13];
+    event[0] = HCI_EVENT_CONNECTION_COMPLETE;
+    event[1] = sizeof(event) - 2;
+    event[2] = status;
+    bt_store_16(event, 3, conn->con_handle);
+    bt_flip_addr(&event[5], conn->address);
+    event[11] = 1; // ACL connection
+    event[12] = 0; // encryption disabled
+    hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+    hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_disconnection_complete(uint16_t handle, uint8_t reason){
+    uint8_t event[6];
+    event[0] = HCI_EVENT_DISCONNECTION_COMPLETE;
+    event[1] = sizeof(event) - 2;
+    event[2] = 0; // status = OK
+    bt_store_16(event, 3, handle);
+    event[5] = reason;
+    hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+    hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_l2cap_check_timeout(hci_connection_t *conn){
+    log_info("L2CAP_EVENT_TIMEOUT_CHECK\n");
+    uint8_t event[4];
+    event[0] = L2CAP_EVENT_TIMEOUT_CHECK;
+    event[1] = sizeof(event) - 2;
+    bt_store_16(event, 2, conn->con_handle);
+    hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+    hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_nr_connections_changed(){
+    log_info("BTSTACK_EVENT_NR_CONNECTIONS_CHANGED %u\n", nr_hci_connections());
+    uint8_t event[3];
+    event[0] = BTSTACK_EVENT_NR_CONNECTIONS_CHANGED;
+    event[1] = sizeof(event) - 2;
+    event[2] = nr_hci_connections();
+    hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+    hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_hci_open_failed(){
+    log_info("BTSTACK_EVENT_POWERON_FAILED\n");
+    uint8_t event[2];
+    event[0] = BTSTACK_EVENT_POWERON_FAILED;
+    event[1] = sizeof(event) - 2;
+    hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+    hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+#ifndef EMBEDDED
+void hci_emit_btstack_version() {
+    log_info("BTSTACK_EVENT_VERSION %u.%u\n", BTSTACK_MAJOR, BTSTACK_MINOR);
+    uint8_t event[6];
+    event[0] = BTSTACK_EVENT_VERSION;
+    event[1] = sizeof(event) - 2;
+    event[2] = BTSTACK_MAJOR;
+    event[3] = BTSTACK_MINOR;
+    bt_store_16(event, 4, BTSTACK_REVISION);
+    hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+    hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+#endif
+
+void hci_emit_system_bluetooth_enabled(uint8_t enabled){
+    log_info("BTSTACK_EVENT_SYSTEM_BLUETOOTH_ENABLED %u\n", enabled);
+    uint8_t event[3];
+    event[0] = BTSTACK_EVENT_SYSTEM_BLUETOOTH_ENABLED;
+    event[1] = sizeof(event) - 2;
+    event[2] = enabled;
+    hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+    hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_remote_name_cached(bd_addr_t *addr, device_name_t *name){
+    uint8_t event[2+1+6+248+1]; // +1 for \0 in log_info
+    event[0] = BTSTACK_EVENT_REMOTE_NAME_CACHED;
+    event[1] = sizeof(event) - 2 - 1;
+    event[2] = 0;   // just to be compatible with HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE
+    bt_flip_addr(&event[3], *addr);
+    memcpy(&event[9], name, 248);
+    
+    event[9+248] = 0;   // assert \0 for log_info
+    log_info("BTSTACK_EVENT_REMOTE_NAME_CACHED %s = '%s'\n", bd_addr_to_str(*addr), &event[9]);
+
+    hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)-1);
+    hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event)-1);
+}
+
+void hci_emit_discoverable_enabled(uint8_t enabled){
+    log_info("BTSTACK_EVENT_DISCOVERABLE_ENABLED %u\n", enabled);
+    uint8_t event[3];
+    event[0] = BTSTACK_EVENT_DISCOVERABLE_ENABLED;
+    event[1] = sizeof(event) - 2;
+    event[2] = enabled;
+    hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+    hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/hci.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  hci.h
+ *
+ *  Created by Matthias Ringwald on 4/29/09.
+ *
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include <btstack/hci_cmds.h>
+#include <btstack/utils.h>
+#include "hci_transport.h"
+#include "bt_control.h"
+#include "remote_device_db.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+    
+// packet header sizes
+#define HCI_CMD_HEADER_SIZE          3
+#define HCI_ACL_HEADER_SIZE            4
+#define HCI_SCO_HEADER_SIZE           3
+#define HCI_EVENT_HEADER_SIZE        2
+
+// packet sizes (max payload)
+#define HCI_ACL_DM1_SIZE            17
+#define HCI_ACL_DH1_SIZE            27
+#define HCI_ACL_2DH1_SIZE           54
+#define HCI_ACL_3DH1_SIZE           83
+#define HCI_ACL_DM3_SIZE           121
+#define HCI_ACL_DH3_SIZE           183
+#define HCI_ACL_DM5_SIZE           224
+#define HCI_ACL_DH5_SIZE           339
+#define HCI_ACL_2DH3_SIZE          367
+#define HCI_ACL_3DH3_SIZE          552
+#define HCI_ACL_2DH5_SIZE          679
+#define HCI_ACL_3DH5_SIZE         1021
+       
+#define HCI_EVENT_PAYLOAD_SIZE     255
+#define HCI_CMD_PAYLOAD_SIZE       255
+    
+// packet buffer sizes
+// HCI_ACL_PAYLOAD_SIZE is configurable and defined in config.h
+#define HCI_EVENT_BUFFER_SIZE      (HCI_EVENT_HEADER_SIZE + HCI_EVENT_PAYLOAD_SIZE)
+#define HCI_CMD_BUFFER_SIZE        (HCI_CMD_HEADER_SIZE   + HCI_CMD_PAYLOAD_SIZE)
+#define HCI_ACL_BUFFER_SIZE        (HCI_ACL_HEADER_SIZE   + HCI_ACL_PAYLOAD_SIZE)
+    
+// size of hci buffers, big enough for command, event, or acl packet without H4 packet type
+// @note cmd buffer is bigger than event buffer
+#if HCI_ACL_BUFFER_SIZE > HCI_CMD_BUFFER_SIZE
+#define HCI_PACKET_BUFFER_SIZE HCI_ACL_BUFFER_SIZE
+#else
+#define HCI_PACKET_BUFFER_SIZE HCI_CMD_BUFFER_SIZE
+#endif
+    
+// OGFs
+#define OGF_LINK_CONTROL          0x01
+#define OGF_LINK_POLICY           0x02
+#define OGF_CONTROLLER_BASEBAND   0x03
+#define OGF_INFORMATIONAL_PARAMETERS 0x04
+#define OGF_STATUS_PARAMETERS     0x05    //sibu
+#define OGF_LE_CONTROLLER 0x08
+#define OGF_BTSTACK 0x3d
+#define OGF_VENDOR  0x3f
+
+// cmds for BTstack 
+// get state: @returns HCI_STATE
+#define BTSTACK_GET_STATE                                  0x01
+
+// set power mode: @param HCI_POWER_MODE
+#define BTSTACK_SET_POWER_MODE                             0x02
+
+// set capture mode: @param on
+#define BTSTACK_SET_ACL_CAPTURE_MODE                       0x03
+
+// get BTstack version
+#define BTSTACK_GET_VERSION                                0x04
+
+// get system Bluetooth state
+#define BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED               0x05
+
+// set system Bluetooth state
+#define BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED               0x06
+
+// enable inquiry scan for this client
+#define BTSTACK_SET_DISCOVERABLE                           0x07
+
+// set global Bluetooth state
+#define BTSTACK_SET_BLUETOOTH_ENABLED                      0x08
+
+// create l2cap channel: @param bd_addr(48), psm (16)
+#define L2CAP_CREATE_CHANNEL                               0x20
+
+// disconnect l2cap disconnect, @param channel(16), reason(8)
+#define L2CAP_DISCONNECT                                   0x21
+
+// register l2cap service: @param psm(16), mtu (16)
+#define L2CAP_REGISTER_SERVICE                             0x22
+
+// unregister l2cap disconnect, @param psm(16)
+#define L2CAP_UNREGISTER_SERVICE                           0x23
+
+// accept connection @param bd_addr(48), dest cid (16)
+#define L2CAP_ACCEPT_CONNECTION                            0x24
+
+// decline l2cap disconnect,@param bd_addr(48), dest cid (16), reason(8)
+#define L2CAP_DECLINE_CONNECTION                           0x25
+
+// create l2cap channel: @param bd_addr(48), psm (16), mtu (16)
+#define L2CAP_CREATE_CHANNEL_MTU                           0x26
+
+// register SDP Service Record: service record (size)
+#define SDP_REGISTER_SERVICE_RECORD                        0x30
+
+// unregister SDP Service Record
+#define SDP_UNREGISTER_SERVICE_RECORD                      0x31
+
+// RFCOMM "HCI" Commands
+#define RFCOMM_CREATE_CHANNEL       0x40
+#define RFCOMM_DISCONNECT            0x41
+#define RFCOMM_REGISTER_SERVICE     0x42
+#define RFCOMM_UNREGISTER_SERVICE   0x43
+#define RFCOMM_ACCEPT_CONNECTION    0x44
+#define RFCOMM_DECLINE_CONNECTION   0x45
+#define RFCOMM_PERSISTENT_CHANNEL   0x46
+#define RFCOMM_CREATE_CHANNEL_WITH_CREDITS   0x47
+#define RFCOMM_REGISTER_SERVICE_WITH_CREDITS 0x48
+#define RFCOMM_GRANT_CREDITS                 0x49
+    
+// 
+#define IS_COMMAND(packet, command) (READ_BT_16(packet,0) == command.opcode)
+
+// data: event(8)
+#define DAEMON_EVENT_CONNECTION_OPENED                     0x50
+
+// data: event(8)
+#define DAEMON_EVENT_CONNECTION_CLOSED                     0x51
+
+// data: event(8), nr_connections(8)
+#define DAEMON_NR_CONNECTIONS_CHANGED                      0x52
+
+// data: event(8)
+#define DAEMON_EVENT_NEW_RFCOMM_CREDITS                    0x53
+
+// data: event()
+#define DAEMON_EVENT_HCI_PACKET_SENT                       0x54
+    
+/**
+ * Connection State 
+ */
+typedef enum {
+    AUTH_FLAGS_NONE                = 0x00,
+    RECV_LINK_KEY_REQUEST          = 0x01,
+    HANDLE_LINK_KEY_REQUEST        = 0x02,
+    SENT_LINK_KEY_REPLY            = 0x04,
+    SENT_LINK_KEY_NEGATIVE_REQUEST = 0x08,
+    RECV_LINK_KEY_NOTIFICATION     = 0x10,
+    RECV_PIN_CODE_REQUEST          = 0x20,
+    SENT_PIN_CODE_REPLY            = 0x40, 
+    SENT_PIN_CODE_NEGATIVE_REPLY   = 0x80 
+} hci_authentication_flags_t;
+
+typedef enum {
+    SENT_CREATE_CONNECTION = 1,
+    RECEIVED_CONNECTION_REQUEST,
+    ACCEPTED_CONNECTION_REQUEST,
+    REJECTED_CONNECTION_REQUEST,
+    OPEN,
+    SENT_DISCONNECT
+} CONNECTION_STATE;
+
+typedef enum {
+    BLUETOOTH_OFF = 1,
+    BLUETOOTH_ON,
+    BLUETOOTH_ACTIVE
+} BLUETOOTH_STATE;
+
+typedef struct {
+    // linked list - assert: first field
+    linked_item_t    item;
+    
+    // remote side
+    bd_addr_t address;
+    
+    // module handle
+    hci_con_handle_t con_handle;
+
+    // state
+    CONNECTION_STATE state;
+    
+    // errands
+    hci_authentication_flags_t authentication_flags;
+
+    timer_source_t timeout;
+    
+#ifdef HAVE_TIME
+    // timer
+    struct timeval timestamp;
+#endif
+#ifdef HAVE_TICK
+    uint32_t timestamp; // timeout in system ticks
+#endif
+    
+    // ACL packet recombination - ACL Header + ACL payload
+    uint8_t  acl_recombination_buffer[4 + HCI_ACL_BUFFER_SIZE];
+    uint16_t acl_recombination_pos;
+    uint16_t acl_recombination_length;
+    
+    // number ACL packets sent to controller
+    uint8_t num_acl_packets_sent;
+    
+} hci_connection_t;
+
+/**
+ * main data structure
+ */
+typedef struct {
+    // transport component with configuration
+    hci_transport_t  * hci_transport;
+    void             * config;
+    
+    // hardware power controller
+    bt_control_t     * control;
+    
+    // list of existing baseband connections
+    linked_list_t     connections;
+
+    // single buffer for HCI Command assembly
+    uint8_t          hci_packet_buffer[HCI_PACKET_BUFFER_SIZE]; // opcode (16), len(8)
+    
+    /* host to controller flow control */
+    uint8_t  num_cmd_packets;
+    // uint8_t  total_num_cmd_packets;
+    uint8_t  total_num_acl_packets;
+    uint16_t acl_data_packet_length;
+
+    // usable packet types given acl_data_packet_length and HCI_ACL_BUFFER_SIZE
+    uint16_t packet_types;
+    
+    /* callback to L2CAP layer */
+    void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size);
+
+    /* remote device db */
+    remote_device_db_t const*remote_device_db;
+    
+    /* hci state machine */
+    HCI_STATE state;
+    uint8_t   substate;
+    uint8_t   cmds_ready;
+    
+    uint8_t   discoverable;
+    uint8_t   connectable;
+    
+    /* buffer for scan enable cmd - 0xff no change */
+    uint8_t   new_scan_enable_value;
+    
+    // buffer for single connection decline
+    uint8_t   decline_reason;
+    bd_addr_t decline_addr;
+    
+} hci_stack_t;
+
+// create and send hci command packets based on a template and a list of parameters
+uint16_t hci_create_cmd(uint8_t *hci_cmd_buffer, hci_cmd_t *cmd, ...);
+uint16_t hci_create_cmd_internal(uint8_t *hci_cmd_buffer, const hci_cmd_t *cmd, va_list argptr);
+
+// set up HCI
+void hci_init(hci_transport_t *transport, void *config, bt_control_t *control, remote_device_db_t const* remote_device_db);
+void hci_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
+void hci_close(void);
+
+// power and inquriy scan control
+int  hci_power_control(HCI_POWER_MODE mode);
+void hci_discoverable_control(uint8_t enable);
+void hci_connectable_control(uint8_t enable);
+
+/**
+ * run the hci control loop once
+ */
+void hci_run(void);
+
+// create and send hci command packets based on a template and a list of parameters
+int hci_send_cmd(const hci_cmd_t *cmd, ...);
+
+// send complete CMD packet
+int hci_send_cmd_packet(uint8_t *packet, int size);
+
+// send ACL packet
+int hci_send_acl_packet(uint8_t *packet, int size);
+
+// non-blocking UART driver needs
+int hci_can_send_packet_now(uint8_t packet_type);
+    
+hci_connection_t * connection_for_handle(hci_con_handle_t con_handle);
+uint8_t  hci_number_outgoing_packets(hci_con_handle_t handle);
+uint8_t  hci_number_free_acl_slots(void);
+int      hci_authentication_active_for_handle(hci_con_handle_t handle);
+void     hci_drop_link_key_for_bd_addr(bd_addr_t *addr);
+uint16_t hci_max_acl_data_packet_length(void);
+uint16_t hci_usable_acl_packet_types(void);
+uint8_t* hci_get_outgoing_acl_packet_buffer(void);
+
+// 
+void hci_emit_state(void);
+void hci_emit_connection_complete(hci_connection_t *conn, uint8_t status);
+void hci_emit_l2cap_check_timeout(hci_connection_t *conn);
+void hci_emit_disconnection_complete(uint16_t handle, uint8_t reason);
+void hci_emit_nr_connections_changed(void);
+void hci_emit_hci_open_failed(void);
+void hci_emit_btstack_version(void);
+void hci_emit_system_bluetooth_enabled(uint8_t enabled);
+void hci_emit_remote_name_cached(bd_addr_t *addr, device_name_t *name);
+void hci_emit_discoverable_enabled(uint8_t enabled);
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/hci_cmds.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,692 @@
+/*
+ * Copyright (C) 2009 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  hci_cmds.c
+ *
+ *  Created by Matthias Ringwald on 7/23/09.
+ */
+
+#include <btstack/hci_cmds.h>
+
+#include <string.h>
+
+#include <btstack/sdp_util.h>
+#include "config.h"
+#include "hci.h"
+
+// calculate combined ogf/ocf value
+#define OPCODE(ogf, ocf) (ocf | ogf << 10)
+
+/**
+ * construct HCI Command based on template
+ *
+ * Format:
+ *   1,2,3,4: one to four byte value
+ *   H: HCI connection handle
+ *   B: Bluetooth Baseband Address (BD_ADDR)
+ *   E: Extended Inquiry Result
+ *   N: Name up to 248 chars, \0 terminated
+ *   P: 16 byte Pairing code
+ *   S: Service Record (Data Element Sequence)
+ */
+uint16_t hci_create_cmd_internal(uint8_t *hci_cmd_buffer, const hci_cmd_t *cmd, va_list argptr){
+    
+    hci_cmd_buffer[0] = cmd->opcode & 0xff;
+    hci_cmd_buffer[1] = cmd->opcode >> 8;
+    int pos = 3;
+    
+    const char *format = cmd->format;
+    uint16_t word;
+    uint32_t longword;
+    uint8_t * ptr;
+    while (*format) {
+        switch(*format) {
+            case '1': //  8 bit value
+            case '2': // 16 bit value
+            case 'H': // hci_handle
+                word = va_arg(argptr, int);  // minimal va_arg is int: 2 bytes on 8+16 bit CPUs
+                hci_cmd_buffer[pos++] = word & 0xff;
+                if (*format == '2') {
+                    hci_cmd_buffer[pos++] = word >> 8;
+                } else if (*format == 'H') {
+                    // TODO implement opaque client connection handles
+                    //      pass module handle for now
+                    hci_cmd_buffer[pos++] = word >> 8;
+                } 
+                break;
+            case '3':
+            case '4':
+                longword = va_arg(argptr, uint32_t);
+                // longword = va_arg(argptr, int);
+                hci_cmd_buffer[pos++] = longword;
+                hci_cmd_buffer[pos++] = longword >> 8;
+                hci_cmd_buffer[pos++] = longword >> 16;
+                if (*format == '4'){
+                    hci_cmd_buffer[pos++] = longword >> 24;
+                }
+                break;
+            case 'B': // bt-addr
+                ptr = va_arg(argptr, uint8_t *);
+                hci_cmd_buffer[pos++] = ptr[5];
+                hci_cmd_buffer[pos++] = ptr[4];
+                hci_cmd_buffer[pos++] = ptr[3];
+                hci_cmd_buffer[pos++] = ptr[2];
+                hci_cmd_buffer[pos++] = ptr[1];
+                hci_cmd_buffer[pos++] = ptr[0];
+                break;
+            case 'E': // Extended Inquiry Information 240 octets
+                ptr = va_arg(argptr, uint8_t *);
+                memcpy(&hci_cmd_buffer[pos], ptr, 240);
+                pos += 240;
+                break;
+            case 'N': { // UTF-8 string, null terminated
+                ptr = va_arg(argptr, uint8_t *);
+                uint16_t len = strlen((const char*) ptr);
+                if (len > 248) {
+                    len = 248;
+                }
+                memcpy(&hci_cmd_buffer[pos], ptr, len);
+                if (len < 248) {
+                    // fill remaining space with zeroes
+                    memset(&hci_cmd_buffer[pos+len], 0, 248-len);
+                }
+                pos += 248;
+                break;
+            }
+            case 'P': // 16 byte PIN code or link key
+                ptr = va_arg(argptr, uint8_t *);
+                memcpy(&hci_cmd_buffer[pos], ptr, 16);
+                pos += 16;
+                break;
+#ifdef HAVE_BLE
+            case 'A': // 31 bytes advertising data
+                ptr = va_arg(argptr, uint8_t *);
+                memcpy(&hci_cmd_buffer[pos], ptr, 31);
+                pos += 31;
+                break;
+#endif
+#ifdef HAVE_SDP
+            case 'S': { // Service Record (Data Element Sequence)
+                ptr = va_arg(argptr, uint8_t *);
+                uint16_t len = de_get_len(ptr);
+                memcpy(&hci_cmd_buffer[pos], ptr, len);
+                pos += len;
+                break;
+            }
+#endif
+            default:
+                break;
+        }
+        format++;
+    };
+    hci_cmd_buffer[2] = pos - 3;
+    return pos;
+}
+
+/**
+ * construct HCI Command based on template
+ *
+ * mainly calls hci_create_cmd_internal
+ */
+uint16_t hci_create_cmd(uint8_t *hci_cmd_buffer, hci_cmd_t *cmd, ...){
+    va_list argptr;
+    va_start(argptr, cmd);
+    uint16_t len = hci_create_cmd_internal(hci_cmd_buffer, cmd, argptr);
+    va_end(argptr);
+    return len;
+}
+
+
+/**
+ *  Link Control Commands 
+ */
+const hci_cmd_t hci_inquiry = {
+OPCODE(OGF_LINK_CONTROL, 0x01), "311"
+// LAP, Inquiry length, Num_responses
+};
+const hci_cmd_t hci_inquiry_cancel = {
+OPCODE(OGF_LINK_CONTROL, 0x02), ""
+// no params
+};
+const hci_cmd_t hci_create_connection = {
+OPCODE(OGF_LINK_CONTROL, 0x05), "B21121"
+// BD_ADDR, Packet_Type, Page_Scan_Repetition_Mode, Reserved, Clock_Offset, Allow_Role_Switch
+};
+const hci_cmd_t hci_disconnect = {
+OPCODE(OGF_LINK_CONTROL, 0x06), "H1"
+// Handle, Reason: 0x05, 0x13-0x15, 0x1a, 0x29
+// see Errors Codes in BT Spec Part D
+};
+const hci_cmd_t hci_create_connection_cancel = {
+OPCODE(OGF_LINK_CONTROL, 0x08), "B"
+// BD_ADDR
+};
+const hci_cmd_t hci_accept_connection_request = {
+OPCODE(OGF_LINK_CONTROL, 0x09), "B1"
+// BD_ADDR, Role: become master, stay slave
+};
+const hci_cmd_t hci_reject_connection_request = {
+OPCODE(OGF_LINK_CONTROL, 0x0a), "B1"
+// BD_ADDR, reason e.g. CONNECTION REJECTED DUE TO LIMITED RESOURCES (0x0d)
+};
+const hci_cmd_t hci_link_key_request_reply = {
+OPCODE(OGF_LINK_CONTROL, 0x0b), "BP"
+// BD_ADDR, LINK_KEY
+};
+const hci_cmd_t hci_link_key_request_negative_reply = {
+OPCODE(OGF_LINK_CONTROL, 0x0c), "B"
+// BD_ADDR
+};
+const hci_cmd_t hci_pin_code_request_reply = {
+OPCODE(OGF_LINK_CONTROL, 0x0d), "B1P"
+// BD_ADDR, pin length, PIN: c-string
+};
+const hci_cmd_t hci_pin_code_request_negative_reply = {
+OPCODE(OGF_LINK_CONTROL, 0x0e), "B"
+// BD_ADDR
+};
+const hci_cmd_t hci_authentication_requested = {
+OPCODE(OGF_LINK_CONTROL, 0x11), "H"
+// Handle
+};
+const hci_cmd_t hci_set_connection_encryption = {
+OPCODE(OGF_LINK_CONTROL, 0x13), "H1"
+// Handle, Encryption_Enable
+};
+const hci_cmd_t hci_change_connection_link_key = {
+OPCODE(OGF_LINK_CONTROL, 0x15), "H"
+// Handle
+};
+const hci_cmd_t hci_remote_name_request = {
+OPCODE(OGF_LINK_CONTROL, 0x19), "B112"
+// BD_ADDR, Page_Scan_Repetition_Mode, Reserved, Clock_Offset
+};
+const hci_cmd_t hci_remote_name_request_cancel = {
+OPCODE(OGF_LINK_CONTROL, 0x1A), "B"
+// BD_ADDR
+};
+
+/**
+ *  Link Policy Commands 
+ */
+const hci_cmd_t hci_sniff_mode = {
+OPCODE(OGF_LINK_POLICY, 0x03), "H2222"
+// handle, Sniff_Max_Interval, Sniff_Min_Interval, Sniff_Attempt, Sniff_Timeout:
+};
+const hci_cmd_t hci_qos_setup = {
+OPCODE(OGF_LINK_POLICY, 0x07), "H114444"
+// handle, flags, service_type, token rate (bytes/s), peak bandwith (bytes/s),
+// latency (us), delay_variation (us)
+};
+const hci_cmd_t hci_role_discovery = {
+OPCODE(OGF_LINK_POLICY, 0x09), "H"
+// handle
+};
+const hci_cmd_t hci_switch_role_command= {
+OPCODE(OGF_LINK_POLICY, 0x0b), "B1"
+// BD_ADDR, role: {0=master,1=slave}
+};
+const hci_cmd_t hci_read_link_policy_settings = {
+OPCODE(OGF_LINK_POLICY, 0x0c), "H"
+// handle 
+};
+const hci_cmd_t hci_write_link_policy_settings = {
+OPCODE(OGF_LINK_POLICY, 0x0d), "H2"
+// handle, settings
+};
+
+/**
+ *  Controller & Baseband Commands 
+ */
+const hci_cmd_t hci_set_event_mask = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x01), "44"
+// event_mask lower 4 octets, higher 4 bytes
+};
+const hci_cmd_t hci_reset = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x03), ""
+// no params
+};
+const hci_cmd_t hci_write_stored_link_key = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x11), "1BP"
+// Num_Keys_To_Write, BD_ADDR, Link_key
+};
+const hci_cmd_t hci_delete_stored_link_key = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x12), "B1"
+// BD_ADDR, Delete_All_Flag
+};
+const hci_cmd_t hci_write_local_name = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x13), "N"
+// Local name (UTF-8, Null Terminated, max 248 octets)
+};
+const hci_cmd_t hci_write_page_timeout = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x18), "2"
+// Page_Timeout * 0.625 ms
+};
+const hci_cmd_t hci_write_scan_enable = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x1A), "1"
+// Scan_enable: no, inq, page, inq+page
+};
+const hci_cmd_t hci_write_authentication_enable = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x20), "1"
+// Authentication_Enable
+};
+const hci_cmd_t hci_write_class_of_device = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x24), "3"
+// Class of Device
+};
+const hci_cmd_t hci_read_num_broadcast_retransmissions = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x29), ""
+};
+const hci_cmd_t hci_write_num_broadcast_retransmissions = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x2a), "1"
+// Num broadcast retransmissions (e.g. 0 for a single broadcast)
+};
+const hci_cmd_t hci_host_buffer_size = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x33), "2122"
+// Host_ACL_Data_Packet_Length:, Host_Synchronous_Data_Packet_Length:, Host_Total_Num_ACL_Data_Packets:, Host_Total_Num_Synchronous_Data_Packets:
+};
+const hci_cmd_t hci_read_link_supervision_timeout = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x36), "H"
+// handle
+};
+const hci_cmd_t hci_write_link_supervision_timeout = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x37), "H2"
+// handle, Range for N: 0x0001 Ð 0xFFFF Time (Range: 0.625ms Ð 40.9 sec)
+};
+const hci_cmd_t hci_write_inquiry_mode = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x45), "1"
+// Inquiry mode: 0x00 = standard, 0x01 = with RSSI, 0x02 = extended
+};
+const hci_cmd_t hci_write_extended_inquiry_response = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x52), "1E"
+// FEC_Required, Exstended Inquiry Response
+};
+const hci_cmd_t hci_write_simple_pairing_mode = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x56), "1"
+// mode: 0 = off, 1 = on
+};
+const hci_cmd_t hci_read_le_host_supported = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x6c), ""
+// params: none
+// return: status, le supported host, simultaneous le host
+};
+const hci_cmd_t hci_write_le_host_supported = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x6d), "11"
+// param: le supported host, simultaneous le host
+// return: status
+};
+
+/**
+ * Informational Parameters
+ */
+const hci_cmd_t hci_read_local_supported_features = {
+OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x03), ""
+// no params
+};
+const hci_cmd_t hci_read_buffer_size = {
+OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x05), ""
+// no params
+};
+const hci_cmd_t hci_read_bd_addr = {
+OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x09), ""
+// no params
+};
+
+//sibu
+const hci_cmd_t hci_read_local_supprted_commands = {
+OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x02), ""
+// no params
+};
+/**
+ * Status Parameters
+ */
+const hci_cmd_t hci_read_rssi = {
+OPCODE(OGF_STATUS_PARAMETERS, 0x05), "H"
+// handle
+// return: handle,RSSI
+};
+
+#ifdef HAVE_BLE
+/**
+ * Low Energy Commands
+ */
+const hci_cmd_t hci_le_set_event_mask = {
+OPCODE(OGF_LE_CONTROLLER, 0x01), "44"
+// params: event_mask lower 4 octets, higher 4 bytes
+// return: status
+};
+const hci_cmd_t hci_le_read_buffer_size = {
+OPCODE(OGF_LE_CONTROLLER, 0x02), ""
+// params: none
+// return: status, le acl data packet len (16), total num le acl data packets(8)
+};
+const hci_cmd_t hci_le_read_supported_features = {
+OPCODE(OGF_LE_CONTROLLER, 0x03), ""
+// params: none
+// return: LE_Features See [Vol 6] Part B, Section 4.6
+};
+const hci_cmd_t hci_le_set_random_address = {
+OPCODE(OGF_LE_CONTROLLER, 0x05), "B"
+// params: random device address
+// return: status
+};
+const hci_cmd_t hci_le_set_advertising_parameters = {
+OPCODE(OGF_LE_CONTROLLER, 0x06), "22111B11"
+// param: min advertising interval, [0x0020,0x4000], default: 0x0800, unit: 0.625 msec
+// param: max advertising interval, [0x0020,0x4000], default: 0x0800, unit: 0.625 msec
+// param: advertising type (enum from 0): ADV_IND, ADC_DIRECT_IND, ADV_SCAN_IND, ADV_NONCONN_IND
+// param: own address type (enum from 0): public device address, random device address
+// param: direct address type (enum from 0): public device address, random device address
+// param: direct address - public or random address of device to be connecteed
+// param: advertising channel map (flags): chan_37(1), chan_38(2), chan_39(4)
+// param: advertising filter policy (enum from 0): scan any conn any, scan whitelist, con any, scan any conn whitelist, scan whitelist, con whitelist
+// return: status
+};
+const hci_cmd_t hci_le_read_advertising_channel_tx_power = {
+OPCODE(OGF_LE_CONTROLLER, 0x07), ""
+// params: none
+// return: status, level [-20,10] signed int (8), units dBm
+};
+const hci_cmd_t hci_le_set_advertising_data= {
+OPCODE(OGF_LE_CONTROLLER, 0x08), "1A"
+// param: advertising data len
+// param: advertising data (31 bytes)
+// return: status
+};
+const hci_cmd_t hci_le_set_scan_response_data= {
+OPCODE(OGF_LE_CONTROLLER, 0x09), "1A"
+// param: scan response data len
+// param: scan response data (31 bytes)
+// return: status
+};
+const hci_cmd_t hci_le_set_advertise_enable = {
+OPCODE(OGF_LE_CONTROLLER, 0x0a), "1"
+// params: avertise enable: off (0), on (1)
+// return: status
+};
+const hci_cmd_t hci_le_set_scan_parameters = {
+OPCODE(OGF_LE_CONTROLLER, 0x0b), "12211"
+// param: le scan type: passive (0), active (1)
+// param: le scan interval [0x0004,0x4000], unit: 0.625 msec
+// param: le scan window   [0x0004,0x4000], unit: 0.625 msec
+// param: own address type: public (0), random (1)
+// param: scanning filter policy: any (0), only whitelist (1)
+// return: status
+};
+const hci_cmd_t hci_le_set_scan_enable = {
+OPCODE(OGF_LE_CONTROLLER, 0x0c), "11"
+// param: le scan enable:  disabled (0), enabled (1)
+// param: filter duplices: disabled (0), enabled (1)
+// return: status
+};
+const hci_cmd_t hci_le_create_connection= {
+OPCODE(OGF_LE_CONTROLLER, 0x0d), "2211B1222222"
+// param: le scan interval, [0x0004, 0x4000], unit: 0.625 msec
+// param: le scan window, [0x0004, 0x4000], unit: 0.625 msec
+// param: initiator filter policy: peer address type + peer address (0), whitelist (1)
+// param: peer address type: public (0), random (1)
+// param: peer address
+// param: own address type: public (0), random (1)
+// param: conn interval min, [0x0006, 0x0c80], unit: 1.25 msec
+// param: conn interval max, [0x0006, 0x0c80], unit: 1.25 msec
+// param: conn latency, number of connection events [0x0000, 0x01f4]
+// param: supervision timeout, [0x000a, 0x0c80], unit: 10 msec
+// param: minimum CE length, [0x0000, 0xffff], unit: 0.625 msec
+// return: none -> le create connection complete event
+};
+const hci_cmd_t hci_le_create_connection_cancel = {
+OPCODE(OGF_LE_CONTROLLER, 0x0e), ""
+// params: none
+// return: status
+};
+const hci_cmd_t hci_le_read_white_list_size = {
+OPCODE(OGF_LE_CONTROLLER, 0x0f), ""
+// params: none
+// return: status, number of entries in controller whitelist
+};
+const hci_cmd_t hci_le_clear_white_list = {
+OPCODE(OGF_LE_CONTROLLER, 0x10), ""
+// params: none
+// return: status
+};
+const hci_cmd_t hci_le_add_device_to_whitelist = {
+OPCODE(OGF_LE_CONTROLLER, 0x11), "1B"
+// param: address type: public (0), random (1)
+// param: address
+// return: status
+};
+const hci_cmd_t hci_le_remove_device_from_whitelist = {
+OPCODE(OGF_LE_CONTROLLER, 0x12), "1B"
+// param: address type: public (0), random (1)
+// param: address
+// return: status
+};
+const hci_cmd_t hci_le_connection_update = {
+OPCODE(OGF_LE_CONTROLLER, 0x13), "H222222"
+// param: conn handle
+// param: conn interval min, [0x0006,0x0c80], unit: 1.25 msec
+// param: conn interval max, [0x0006,0x0c80], unit: 1.25 msec
+// param: conn latency, [0x0000,0x03e8], number of connection events
+// param: supervision timeout, [0x000a,0x0c80], unit: 10 msec
+// param: minimum CE length, [0x0000,0xffff], unit: 0.625 msec
+// param: maximum CE length, [0x0000,0xffff], unit: 0.625 msec
+// return: none -> le connection update complete event
+};
+const hci_cmd_t hci_le_set_host_channel_classification = {
+OPCODE(OGF_LE_CONTROLLER, 0x14), "41"
+// param: channel map 37 bit, split into first 32 and higher 5 bits
+// return: status
+};
+const hci_cmd_t hci_le_read_channel_map = {
+OPCODE(OGF_LE_CONTROLLER, 0x15), "H"
+// params: connection handle
+// return: status, connection handle, channel map (5 bytes, 37 used)
+};
+const hci_cmd_t hci_le_read_remote_used_features = {
+OPCODE(OGF_LE_CONTROLLER, 0x16), "H"
+// params: connection handle
+// return: none -> le read remote used features complete event
+};
+const hci_cmd_t hci_le_encrypt = {
+OPCODE(OGF_LE_CONTROLLER, 0x17), "PP"
+// param: key (128) for AES-128
+// param: plain text (128) 
+// return: status, encrypted data (128)
+};
+const hci_cmd_t hci_le_rand = {
+OPCODE(OGF_LE_CONTROLLER, 0x18), ""
+// params: none
+// return: status, random number (64)
+};
+const hci_cmd_t hci_le_start_encryption = {
+OPCODE(OGF_LE_CONTROLLER, 0x19), "H442P"
+// param: connection handle
+// param: 64 bit random number lower  32 bit
+// param: 64 bit random number higher 32 bit
+// param: encryption diversifier (16)
+// param: long term key (128)
+// return: none -> encryption changed or encryption key refresh complete event
+};
+const hci_cmd_t hci_le_long_term_key_request_reply = {
+OPCODE(OGF_LE_CONTROLLER, 0x1a), "HP"
+// param: connection handle
+// param: long term key (128)
+// return: status, connection handle
+};
+const hci_cmd_t hci_le_long_term_key_negative_reply = {
+OPCODE(OGF_LE_CONTROLLER, 0x1b), "H"
+// param: connection handle
+// return: status, connection handle
+};
+const hci_cmd_t hci_le_read_supported_states = {
+OPCODE(OGF_LE_CONTROLLER, 0x1c), "H"
+// param: none
+// return: status, LE states (64)
+};
+const hci_cmd_t hci_le_receiver_test = {
+OPCODE(OGF_LE_CONTROLLER, 0x1d), "1"
+// param: rx frequency, [0x00 0x27], frequency (MHz): 2420 + N*2
+// return: status
+};
+const hci_cmd_t hci_le_transmitter_test = {
+    OPCODE(OGF_LE_CONTROLLER, 0x1e), "111"
+    // param: tx frequency, [0x00 0x27], frequency (MHz): 2420 + N*2
+    // param: lengh of test payload [0x00,0x25]
+    // param: packet payload [0,7] different patterns
+    // return: status
+};
+const hci_cmd_t hci_le_test_end = {
+    OPCODE(OGF_LE_CONTROLLER, 0x1f), "1"
+    // params: none
+    // return: status, number of packets (8)
+};
+#endif
+
+// BTstack commands
+const hci_cmd_t btstack_get_state = {
+OPCODE(OGF_BTSTACK, BTSTACK_GET_STATE), ""
+// no params -> 
+};
+
+const hci_cmd_t btstack_set_power_mode = {
+OPCODE(OGF_BTSTACK, BTSTACK_SET_POWER_MODE), "1"
+// mode: 0 = off, 1 = on
+};
+
+const hci_cmd_t btstack_set_acl_capture_mode = {
+OPCODE(OGF_BTSTACK, BTSTACK_SET_ACL_CAPTURE_MODE), "1"
+// mode: 0 = off, 1 = on
+};
+
+const hci_cmd_t btstack_get_version = {
+OPCODE(OGF_BTSTACK, BTSTACK_GET_VERSION), ""
+};
+
+const hci_cmd_t btstack_get_system_bluetooth_enabled = {
+OPCODE(OGF_BTSTACK, BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED), ""
+};
+
+const hci_cmd_t btstack_set_system_bluetooth_enabled = {
+OPCODE(OGF_BTSTACK, BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED), "1"
+};
+
+const hci_cmd_t btstack_set_discoverable = {
+OPCODE(OGF_BTSTACK, BTSTACK_SET_DISCOVERABLE), "1"
+};
+
+const hci_cmd_t btstack_set_bluetooth_enabled = {
+// only used by btstack config
+OPCODE(OGF_BTSTACK, BTSTACK_SET_BLUETOOTH_ENABLED), "1"
+};
+
+const hci_cmd_t l2cap_create_channel = {
+OPCODE(OGF_BTSTACK, L2CAP_CREATE_CHANNEL), "B2"
+// @param bd_addr(48), psm (16)
+};
+const hci_cmd_t l2cap_create_channel_mtu = {
+OPCODE(OGF_BTSTACK, L2CAP_CREATE_CHANNEL_MTU), "B22"
+// @param bd_addr(48), psm (16), mtu (16)
+};
+const hci_cmd_t l2cap_disconnect = {
+OPCODE(OGF_BTSTACK, L2CAP_DISCONNECT), "21"
+// @param channel(16), reason(8)
+};
+const hci_cmd_t l2cap_register_service = {
+OPCODE(OGF_BTSTACK, L2CAP_REGISTER_SERVICE), "22"
+// @param psm (16), mtu (16)
+};
+const hci_cmd_t l2cap_unregister_service = {
+OPCODE(OGF_BTSTACK, L2CAP_UNREGISTER_SERVICE), "2"
+// @param psm (16)
+};
+const hci_cmd_t l2cap_accept_connection = {
+OPCODE(OGF_BTSTACK, L2CAP_ACCEPT_CONNECTION), "2"
+// @param source cid (16)
+};
+const hci_cmd_t l2cap_decline_connection = {
+OPCODE(OGF_BTSTACK, L2CAP_DECLINE_CONNECTION), "21"
+// @param source cid (16), reason(8)
+};
+const hci_cmd_t sdp_register_service_record = {
+OPCODE(OGF_BTSTACK, SDP_REGISTER_SERVICE_RECORD), "S"
+// @param service record handle (DES)
+};
+const hci_cmd_t sdp_unregister_service_record = {
+OPCODE(OGF_BTSTACK, SDP_UNREGISTER_SERVICE_RECORD), "4"
+// @param service record handle (32)
+};
+
+// create rfcomm channel: @param bd_addr(48), channel (8)
+const hci_cmd_t rfcomm_create_channel = {
+    OPCODE(OGF_BTSTACK, RFCOMM_CREATE_CHANNEL), "B1"
+};
+// create rfcomm channel: @param bd_addr(48), channel (8), mtu (16), credits (8)
+const hci_cmd_t rfcomm_create_channel_with_initial_credits = {
+    OPCODE(OGF_BTSTACK, RFCOMM_CREATE_CHANNEL_WITH_CREDITS), "B121"
+};
+// grant credits: @param rfcomm_cid(16), credits (8)
+const hci_cmd_t rfcomm_grants_credits= {
+    OPCODE(OGF_BTSTACK, RFCOMM_GRANT_CREDITS), "21"
+};
+// disconnect rfcomm disconnect, @param rfcomm_cid(16), reason(8)
+const  hci_cmd_t rfcomm_disconnect = {
+    OPCODE(OGF_BTSTACK, RFCOMM_DISCONNECT), "21"
+};
+
+// register rfcomm service: @param channel(8), mtu (16)
+const hci_cmd_t rfcomm_register_service = {
+    OPCODE(OGF_BTSTACK, RFCOMM_REGISTER_SERVICE), "12"
+};
+// register rfcomm service: @param channel(8), mtu (16), initial credits (8)
+const hci_cmd_t rfcomm_register_service_with_initial_credits = {
+    OPCODE(OGF_BTSTACK, RFCOMM_REGISTER_SERVICE_WITH_CREDITS), "121"
+};
+
+// unregister rfcomm service, @param service_channel(16)
+const hci_cmd_t rfcomm_unregister_service = {
+    OPCODE(OGF_BTSTACK, RFCOMM_UNREGISTER_SERVICE), "2"
+};
+// accept connection @param source cid (16)
+const hci_cmd_t rfcomm_accept_connection = {
+    OPCODE(OGF_BTSTACK, RFCOMM_ACCEPT_CONNECTION), "2"
+};
+// decline connection @param source cid (16)
+const hci_cmd_t rfcomm_decline_connection = {
+    OPCODE(OGF_BTSTACK, RFCOMM_DECLINE_CONNECTION), "21"
+};
+// request persisten rfcomm channel number for named service
+const hci_cmd_t rfcomm_persistent_channel_for_service = {
+    OPCODE(OGF_BTSTACK, RFCOMM_PERSISTENT_CHANNEL), "N"
+};
+
+// register rfcomm service: @param channel(8), mtu (16), initial credits (8)
+extern const hci_cmd_t rfcomm_register_service_with_initial_credits;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/hci_cmds.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2009 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  hci_cmds.h
+ *
+ *  Created by Matthias Ringwald on 7/23/09.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+    
+/**
+ * packet types - used in BTstack and over the H4 UART interface
+ */
+#define HCI_COMMAND_DATA_PACKET    0x01
+#define HCI_ACL_DATA_PACKET        0x02
+#define HCI_SCO_DATA_PACKET        0x03
+#define HCI_EVENT_PACKET        0x04
+
+// extension for client/server communication
+#define DAEMON_EVENT_PACKET     0x05
+    
+// L2CAP data
+#define L2CAP_DATA_PACKET       0x06
+
+// RFCOMM data
+#define RFCOMM_DATA_PACKET      0x07
+
+// Attribute protocol data
+#define ATT_DATA_PACKET         0x08
+
+// Security Manager protocol data
+#define SM_DATA_PACKET          0x09
+    
+// debug log messages
+#define LOG_MESSAGE_PACKET      0xfc
+
+    
+// Fixed PSM numbers
+#define PSM_SDP    0x01
+#define PSM_RFCOMM 0x03
+#define PSM_HID_CONTROL 0x11
+#define PSM_HID_INTERRUPT 0x13
+
+// Events from host controller to host
+#define HCI_EVENT_INQUIRY_COMPLETE                           0x01
+#define HCI_EVENT_INQUIRY_RESULT                           0x02
+#define HCI_EVENT_CONNECTION_COMPLETE                       0x03
+#define HCI_EVENT_CONNECTION_REQUEST                       0x04
+#define HCI_EVENT_DISCONNECTION_COMPLETE                     0x05
+#define HCI_EVENT_AUTHENTICATION_COMPLETE_EVENT            0x06
+#define HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE               0x07
+#define HCI_EVENT_ENCRYPTION_CHANGE                        0x08
+#define HCI_EVENT_CHANGE_CONNECTION_LINK_KEY_COMPLETE      0x09
+#define HCI_EVENT_MASTER_LINK_KEY_COMPLETE                 0x0A
+#define HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE  0x0B
+#define HCI_EVENT_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C
+#define HCI_EVENT_QOS_SETUP_COMPLETE                       0x0D
+#define HCI_EVENT_COMMAND_COMPLETE                           0x0E
+#define HCI_EVENT_COMMAND_STATUS                           0x0F
+#define HCI_EVENT_HARDWARE_ERROR                           0x10
+#define HCI_EVENT_FLUSH_OCCURED                            0x11
+#define HCI_EVENT_ROLE_CHANGE                               0x12
+#define HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS                 0x13
+#define HCI_EVENT_MODE_CHANGE_EVENT                        0x14
+#define HCI_EVENT_RETURN_LINK_KEYS                         0x15
+#define HCI_EVENT_PIN_CODE_REQUEST                         0x16
+#define HCI_EVENT_LINK_KEY_REQUEST                         0x17
+#define HCI_EVENT_LINK_KEY_NOTIFICATION                    0x18
+#define HCI_EVENT_DATA_BUFFER_OVERFLOW                     0x1A
+#define HCI_EVENT_MAX_SLOTS_CHANGED                           0x1B
+#define HCI_EVENT_READ_CLOCK_OFFSET_COMPLETE               0x1C
+#define HCI_EVENT_PACKET_TYPE_CHANGED                      0x1D
+#define HCI_EVENT_INQUIRY_RESULT_WITH_RSSI                     0x22
+#define HCI_EVENT_EXTENDED_INQUIRY_RESPONSE                0x2F
+#define HCI_EVENT_LE_META                                  0x3E
+#define HCI_EVENT_VENDOR_SPECIFIC                           0xFF
+
+#define HCI_SUBEVENT_LE_CONNECTION_COMPLETE                0x01
+#define HCI_SUBEVENT_LE_ADVERTISING_REPORT                 0x02
+#define HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE         0x03
+#define HCI_SUBEVENT_LE_READ_REMOTE_USED_FEATURES_COMPLETE 0x04
+#define HCI_SUBEVENT_LE_LONG_TERM_KEY_REQUEST              0x05
+    
+// last used HCI_EVENT in 2.1 is 0x3d
+
+// events 0x50-0x5f are used internally
+
+// BTSTACK DAEMON EVENTS
+
+// events from BTstack for application/client lib
+#define BTSTACK_EVENT_STATE                                0x60
+
+// data: event(8), len(8), nr hci connections
+#define BTSTACK_EVENT_NR_CONNECTIONS_CHANGED               0x61
+
+// data: none
+#define BTSTACK_EVENT_POWERON_FAILED                       0x62
+
+// data: majot (8), minor (8), revision(16)
+#define BTSTACK_EVENT_VERSION                               0x63
+
+// data: system bluetooth on/off (bool)
+#define BTSTACK_EVENT_SYSTEM_BLUETOOTH_ENABLED               0x64
+
+// data: event (8), len(8), status (8) == 0, address (48), name (1984 bits = 248 bytes)
+#define BTSTACK_EVENT_REMOTE_NAME_CACHED                    0x65
+
+// data: discoverable enabled (bool)
+#define BTSTACK_EVENT_DISCOVERABLE_ENABLED                   0x66
+
+// L2CAP EVENTS
+    
+// data: event (8), len(8), status (8), address(48), handle (16), psm (16), local_cid(16), remote_cid (16), local_mtu(16), remote_mtu(16) 
+#define L2CAP_EVENT_CHANNEL_OPENED                         0x70
+
+// data: event (8), len(8), channel (16)
+#define L2CAP_EVENT_CHANNEL_CLOSED                         0x71
+
+// data: event (8), len(8), address(48), handle (16), psm (16), local_cid(16), remote_cid (16) 
+#define L2CAP_EVENT_INCOMING_CONNECTION                       0x72
+
+// data: event(8), len(8), handle(16)
+#define L2CAP_EVENT_TIMEOUT_CHECK                          0x73
+
+// data: event(8), len(8), local_cid(16), credits(8)
+#define L2CAP_EVENT_CREDITS                                   0x74
+
+// data: event(8), len(8), status (8), psm (16)
+#define L2CAP_EVENT_SERVICE_REGISTERED                     0x75
+
+
+// RFCOMM EVENTS
+    
+// data: event(8), len(8), status (8), address (48), handle (16), server channel(8), rfcomm_cid(16), max frame size(16)
+#define RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE                 0x80
+    
+// data: event(8), len(8), rfcomm_cid(16)
+#define RFCOMM_EVENT_CHANNEL_CLOSED                        0x81
+    
+// data: event (8), len(8), address(48), channel (8), rfcomm_cid (16)
+#define RFCOMM_EVENT_INCOMING_CONNECTION                   0x82
+    
+// data: event (8), len(8), rfcommid (16), ...
+#define RFCOMM_EVENT_REMOTE_LINE_STATUS                    0x83
+    
+// data: event(8), len(8), rfcomm_cid(16), credits(8)
+#define RFCOMM_EVENT_CREDITS                               0x84
+    
+// data: event(8), len(8), status (8), rfcomm server channel id (8) 
+#define RFCOMM_EVENT_SERVICE_REGISTERED                    0x85
+    
+// data: event(8), len(8), status (8), rfcomm server channel id (8) 
+#define RFCOMM_EVENT_PERSISTENT_CHANNEL                    0x86
+    
+    
+// data: event(8), len(8), status(8), service_record_handle(32)
+#define SDP_SERVICE_REGISTERED                             0x90
+
+    
+// last error code in 2.1 is 0x38 - we start with 0x50 for BTstack errors
+
+#define BTSTACK_CONNECTION_TO_BTDAEMON_FAILED              0x50
+#define BTSTACK_ACTIVATION_FAILED_SYSTEM_BLUETOOTH           0x51
+#define BTSTACK_ACTIVATION_POWERON_FAILED                  0x52
+#define BTSTACK_ACTIVATION_FAILED_UNKNOWN                  0x53
+#define BTSTACK_NOT_ACTIVATED                               0x54
+#define BTSTACK_BUSY                                       0x55
+#define BTSTACK_MEMORY_ALLOC_FAILED                        0x56
+#define BTSTACK_ACL_BUFFERS_FULL                           0x57
+
+// l2cap errors - enumeration by the command that created them
+#define L2CAP_COMMAND_REJECT_REASON_COMMAND_NOT_UNDERSTOOD 0x60
+#define L2CAP_COMMAND_REJECT_REASON_SIGNALING_MTU_EXCEEDED 0x61
+#define L2CAP_COMMAND_REJECT_REASON_INVALID_CID_IN_REQUEST 0x62
+
+#define L2CAP_CONNECTION_RESPONSE_RESULT_SUCCESSFUL        0x63
+#define L2CAP_CONNECTION_RESPONSE_RESULT_PENDING           0x64
+#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_PSM       0x65
+#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_SECURITY  0x66
+#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_RESOURCES 0x65
+
+#define L2CAP_CONFIG_RESPONSE_RESULT_SUCCESSFUL            0x66
+#define L2CAP_CONFIG_RESPONSE_RESULT_UNACCEPTABLE_PARAMS   0x67
+#define L2CAP_CONFIG_RESPONSE_RESULT_REJECTED              0x68
+#define L2CAP_CONFIG_RESPONSE_RESULT_UNKNOWN_OPTIONS       0x69
+#define L2CAP_SERVICE_ALREADY_REGISTERED                   0x6a
+    
+#define RFCOMM_MULTIPLEXER_STOPPED                         0x70
+#define RFCOMM_CHANNEL_ALREADY_REGISTERED                  0x71
+#define RFCOMM_NO_OUTGOING_CREDITS                         0x72
+
+#define SDP_HANDLE_ALREADY_REGISTERED                      0x80
+ 
+/**
+ * Default INQ Mode
+ */
+#define HCI_INQUIRY_LAP 0x9E8B33L  // 0x9E8B33: General/Unlimited Inquiry Access Code (GIAC)
+/**
+ *  Hardware state of Bluetooth controller 
+ */
+typedef enum {
+    HCI_POWER_OFF = 0,
+    HCI_POWER_ON,
+    HCI_POWER_SLEEP
+} HCI_POWER_MODE;
+
+/**
+ * State of BTstack 
+ */
+typedef enum {
+    HCI_STATE_OFF = 0,
+    HCI_STATE_INITIALIZING,
+    HCI_STATE_WORKING,
+    HCI_STATE_HALTING,
+    HCI_STATE_SLEEPING,
+    HCI_STATE_FALLING_ASLEEP
+} HCI_STATE;
+
+/** 
+ * compact HCI Command packet description
+ */
+ typedef struct {
+    uint16_t    opcode;
+    const char *format;
+} hci_cmd_t;
+
+
+// HCI Commands - see hci_cmds.c for info on parameters
+extern const hci_cmd_t btstack_get_state;
+extern const hci_cmd_t btstack_set_power_mode;
+extern const hci_cmd_t btstack_set_acl_capture_mode;
+extern const hci_cmd_t btstack_get_version;
+extern const hci_cmd_t btstack_get_system_bluetooth_enabled;
+extern const hci_cmd_t btstack_set_system_bluetooth_enabled;
+extern const hci_cmd_t btstack_set_discoverable;
+extern const hci_cmd_t btstack_set_bluetooth_enabled;    // only used by btstack config
+    
+extern const hci_cmd_t hci_accept_connection_request;
+extern const hci_cmd_t hci_authentication_requested;
+extern const hci_cmd_t hci_change_connection_link_key;
+extern const hci_cmd_t hci_create_connection;
+extern const hci_cmd_t hci_create_connection_cancel;
+extern const hci_cmd_t hci_write_stored_link_key;
+extern const hci_cmd_t hci_delete_stored_link_key;
+extern const hci_cmd_t hci_disconnect;
+extern const hci_cmd_t hci_host_buffer_size;
+extern const hci_cmd_t hci_inquiry;
+extern const hci_cmd_t hci_inquiry_cancel;
+extern const hci_cmd_t hci_link_key_request_negative_reply;
+extern const hci_cmd_t hci_link_key_request_reply;
+extern const hci_cmd_t hci_pin_code_request_reply;
+extern const hci_cmd_t hci_pin_code_request_negative_reply;
+extern const hci_cmd_t hci_qos_setup;
+extern const hci_cmd_t hci_read_bd_addr;
+extern const hci_cmd_t hci_read_buffer_size;
+extern const hci_cmd_t hci_read_le_host_supported;
+extern const hci_cmd_t hci_read_link_policy_settings;
+extern const hci_cmd_t hci_read_link_supervision_timeout;
+extern const hci_cmd_t hci_read_local_supported_features;
+extern const hci_cmd_t hci_read_num_broadcast_retransmissions;
+extern const hci_cmd_t hci_reject_connection_request;
+extern const hci_cmd_t hci_remote_name_request;
+extern const hci_cmd_t hci_remote_name_request_cancel;
+extern const hci_cmd_t hci_reset;
+extern const hci_cmd_t hci_role_discovery;
+extern const hci_cmd_t hci_set_event_mask;
+extern const hci_cmd_t hci_set_connection_encryption;
+extern const hci_cmd_t hci_sniff_mode;
+extern const hci_cmd_t hci_switch_role_command;
+extern const hci_cmd_t hci_write_authentication_enable;
+extern const hci_cmd_t hci_write_class_of_device;
+extern const hci_cmd_t hci_write_extended_inquiry_response;
+extern const hci_cmd_t hci_write_inquiry_mode;
+extern const hci_cmd_t hci_write_le_host_supported;
+extern const hci_cmd_t hci_write_link_policy_settings;
+extern const hci_cmd_t hci_write_link_supervision_timeout;
+extern const hci_cmd_t hci_write_local_name;
+extern const hci_cmd_t hci_write_num_broadcast_retransmissions;
+extern const hci_cmd_t hci_write_page_timeout;
+extern const hci_cmd_t hci_write_scan_enable;
+extern const hci_cmd_t hci_write_simple_pairing_mode;
+
+extern const hci_cmd_t hci_le_add_device_to_whitelist;
+extern const hci_cmd_t hci_le_clear_white_list;
+extern const hci_cmd_t hci_le_connection_update;
+extern const hci_cmd_t hci_le_create_connection;
+extern const hci_cmd_t hci_le_create_connection_cancel;
+extern const hci_cmd_t hci_le_encrypt;
+extern const hci_cmd_t hci_le_long_term_key_negative_reply;
+extern const hci_cmd_t hci_le_long_term_key_request_reply;
+extern const hci_cmd_t hci_le_rand;
+extern const hci_cmd_t hci_le_read_advertising_channel_tx_power;
+extern const hci_cmd_t hci_le_read_buffer_size ;
+extern const hci_cmd_t hci_le_read_channel_map;
+extern const hci_cmd_t hci_le_read_remote_used_features;
+extern const hci_cmd_t hci_le_read_supported_features;
+extern const hci_cmd_t hci_le_read_supported_states;
+extern const hci_cmd_t hci_le_read_white_list_size;
+extern const hci_cmd_t hci_le_receiver_test;
+extern const hci_cmd_t hci_le_remove_device_from_whitelist;
+extern const hci_cmd_t hci_le_set_advertise_enable;
+extern const hci_cmd_t hci_le_set_advertising_data;
+extern const hci_cmd_t hci_le_set_advertising_parameters;
+extern const hci_cmd_t hci_le_set_event_mask;
+extern const hci_cmd_t hci_le_set_host_channel_classification;
+extern const hci_cmd_t hci_le_set_random_address;
+extern const hci_cmd_t hci_le_set_scan_enable;
+extern const hci_cmd_t hci_le_set_scan_parameters;
+extern const hci_cmd_t hci_le_set_scan_response_data;
+extern const hci_cmd_t hci_le_start_encryption;
+extern const hci_cmd_t hci_le_test_end;
+extern const hci_cmd_t hci_le_transmitter_test;
+    
+extern const hci_cmd_t l2cap_accept_connection;
+extern const hci_cmd_t l2cap_create_channel;
+extern const hci_cmd_t l2cap_create_channel_mtu;
+extern const hci_cmd_t l2cap_decline_connection;
+extern const hci_cmd_t l2cap_disconnect;
+extern const hci_cmd_t l2cap_register_service;
+extern const hci_cmd_t l2cap_unregister_service;
+
+extern const hci_cmd_t sdp_register_service_record;
+extern const hci_cmd_t sdp_unregister_service_record;
+
+// accept connection @param bd_addr(48), rfcomm_cid (16)
+extern const hci_cmd_t rfcomm_accept_connection;
+// create rfcomm channel: @param bd_addr(48), channel (8)
+extern const hci_cmd_t rfcomm_create_channel;
+// create rfcomm channel: @param bd_addr(48), channel (8), mtu (16), credits (8)
+extern const hci_cmd_t rfcomm_create_channel_with_initial_credits;
+// decline rfcomm disconnect,@param bd_addr(48), rfcomm cid (16), reason(8)
+extern const hci_cmd_t rfcomm_decline_connection;
+// disconnect rfcomm disconnect, @param rfcomm_cid(8), reason(8)
+extern const hci_cmd_t rfcomm_disconnect;
+// register rfcomm service: @param channel(8), mtu (16)
+extern const hci_cmd_t rfcomm_register_service;
+// register rfcomm service: @param channel(8), mtu (16), initial credits (8)
+extern const hci_cmd_t rfcomm_register_service_with_initial_credits;
+// unregister rfcomm service, @param service_channel(16)
+extern const hci_cmd_t rfcomm_unregister_service;
+// request persisten rfcomm channel for service name: serive name (char*) 
+extern const hci_cmd_t rfcomm_persistent_channel_for_service;
+
+//sibu
+extern const hci_cmd_t hci_read_local_supprted_commands;
+extern const hci_cmd_t hci_read_rssi;
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/hci_dump.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  hci_dump.c
+ *
+ *  Dump HCI trace in various formats:
+ *
+ *  - BlueZ's hcidump format
+ *  - Apple's PacketLogger
+ *  - stdout hexdump
+ *
+ *  Created by Matthias Ringwald on 5/26/09.
+ */
+
+#include "config.h"
+
+#include "hci_dump.h"
+#include "hci.h"
+#include "hci_transport.h"
+#include <btstack/hci_cmds.h>
+
+#ifndef EMBEDDED
+#include <fcntl.h>        // open
+#include <arpa/inet.h>    // hton..
+#include <unistd.h>       // write 
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>     // for timestamps
+#include <sys/stat.h>     // for mode flags
+#include <stdarg.h>       // for va_list
+#endif
+
+// BLUEZ hcidump
+typedef struct {
+    uint16_t    len;
+    uint8_t        in;
+    uint8_t        pad;
+    uint32_t    ts_sec;
+    uint32_t    ts_usec;
+    uint8_t     packet_type;
+}
+#ifdef __GNUC__
+__attribute__ ((packed))
+#endif 
+hcidump_hdr;
+
+// APPLE PacketLogger
+typedef struct {
+    uint32_t    len;
+    uint32_t    ts_sec;
+    uint32_t    ts_usec;
+    uint8_t        type;   // 0xfc for note
+}
+#ifdef __GNUC__
+__attribute__ ((packed))
+#endif
+pktlog_hdr;
+
+#ifndef EMBEDDED
+static int dump_file = -1;
+static int dump_format;
+static hcidump_hdr header_bluez;
+static pktlog_hdr  header_packetlogger;
+static char time_string[40];
+static int  max_nr_packets = -1;
+static int  nr_packets = 0;
+static char log_message_buffer[256];
+#endif
+
+void hci_dump_open(char *filename, hci_dump_format_t format){
+#ifndef EMBEDDED
+    dump_format = format;
+    if (dump_format == HCI_DUMP_STDOUT) {
+        dump_file = fileno(stdout);
+    } else {
+        dump_file = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+    }
+#endif
+}
+
+#ifndef EMBEDDED
+void hci_dump_set_max_packets(int packets){
+    max_nr_packets = packets;
+}
+#endif
+
+void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) {
+#ifndef EMBEDDED
+
+    if (dump_file < 0) return; // not activated yet
+
+    // don't grow bigger than max_nr_packets
+    if (dump_format != HCI_DUMP_STDOUT && max_nr_packets > 0){
+        if (nr_packets >= max_nr_packets){
+            lseek(dump_file, 0, SEEK_SET);
+            ftruncate(dump_file, 0);
+            nr_packets = 0;
+        }
+        nr_packets++;
+    }
+    
+    // get time
+    struct timeval curr_time;
+    struct tm* ptm;
+    gettimeofday(&curr_time, NULL);
+    
+    switch (dump_format){
+        case HCI_DUMP_STDOUT: {
+            /* Obtain the time of day, and convert it to a tm struct. */
+            ptm = localtime (&curr_time.tv_sec);
+            /* Format the date and time, down to a single second. */
+            strftime (time_string, sizeof (time_string), "[%Y-%m-%d %H:%M:%S", ptm);
+            /* Compute milliseconds from microseconds. */
+            uint16_t milliseconds = curr_time.tv_usec / 1000;
+            /* Print the formatted time, in seconds, followed by a decimal point
+             and the milliseconds. */
+            printf ("%s.%03u] ", time_string, milliseconds);
+            switch (packet_type){
+                case HCI_COMMAND_DATA_PACKET:
+                    printf("CMD => ");
+                    break;
+                case HCI_EVENT_PACKET:
+                    printf("EVT <= ");
+                    break;
+                case HCI_ACL_DATA_PACKET:
+                    if (in) {
+                        printf("ACL <= ");
+                    } else {
+                        printf("ACL => ");
+                    }
+                    break;
+                case LOG_MESSAGE_PACKET:
+                    // assume buffer is big enough
+                    packet[len] = 0;
+                    printf("LOG -- %s\n", (char*) packet);
+                    return;
+                default:
+                    return;
+            }
+            hexdump(packet, len);
+            break;
+        }
+            
+        case HCI_DUMP_BLUEZ:
+            bt_store_16( (uint8_t *) &header_bluez.len, 0, 1 + len);
+            header_bluez.in  = in;
+            header_bluez.pad = 0;
+            bt_store_32( (uint8_t *) &header_bluez.ts_sec,  0, curr_time.tv_sec);
+            bt_store_32( (uint8_t *) &header_bluez.ts_usec, 0, curr_time.tv_usec);
+            header_bluez.packet_type = packet_type;
+            write (dump_file, &header_bluez, sizeof(hcidump_hdr) );
+            write (dump_file, packet, len );
+            break;
+            
+        case HCI_DUMP_PACKETLOGGER:
+            header_packetlogger.len = htonl( sizeof(pktlog_hdr) - 4 + len);
+            header_packetlogger.ts_sec =  htonl(curr_time.tv_sec);
+            header_packetlogger.ts_usec = htonl(curr_time.tv_usec);
+            switch (packet_type){
+                case HCI_COMMAND_DATA_PACKET:
+                    header_packetlogger.type = 0x00;
+                    break;
+                case HCI_ACL_DATA_PACKET:
+                    if (in) {
+                        header_packetlogger.type = 0x03;
+                    } else {
+                        header_packetlogger.type = 0x02;
+                    }
+                    break;
+                case HCI_EVENT_PACKET:
+                    header_packetlogger.type = 0x01;
+                    break;
+                case LOG_MESSAGE_PACKET:
+                    header_packetlogger.type = 0xfc;
+                    break;
+                default:
+                    return;
+            }
+            write (dump_file, &header_packetlogger, sizeof(pktlog_hdr) );
+            write (dump_file, packet, len );
+            break;
+            
+        default:
+            break;
+    }
+#endif
+}
+
+void hci_dump_log(const char * format, ...){
+#ifndef EMBEDDED
+    va_list argptr;
+    va_start(argptr, format);
+    int len = vsnprintf(log_message_buffer, sizeof(log_message_buffer), format, argptr);
+    hci_dump_packet(LOG_MESSAGE_PACKET, 0, (uint8_t*) log_message_buffer, len);
+    va_end(argptr);
+#endif    
+}
+
+void hci_dump_close(){
+#ifndef EMBEDDED
+    close(dump_file);
+    dump_file = -1;
+#endif
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/hci_dump.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  hci_dump.h
+ *
+ *  Dump HCI trace as BlueZ's hcidump format, Apple's PacketLogger, or stdout
+ * 
+ *  Created by Matthias Ringwald on 5/26/09.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+typedef enum {
+    HCI_DUMP_BLUEZ = 0,
+    HCI_DUMP_PACKETLOGGER,
+    HCI_DUMP_STDOUT
+} hci_dump_format_t;
+
+void hci_dump_open(char *filename, hci_dump_format_t format);
+void hci_dump_set_max_packets(int packets); // -1 for unlimited
+void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len);
+void hci_dump_log(const char * format, ...);
+void hci_dump_close(void);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/hci_transport.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  hci_transport.h
+ *
+ *  HCI Transport API -- allows BT Daemon to use different transport protcols 
+ *
+ *  Created by Matthias Ringwald on 4/29/09.
+ *
+ */
+#pragma once
+
+#include <stdint.h>
+#include <btstack/run_loop.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+    
+/* HCI packet types */
+typedef struct {
+    int    (*open)(void *transport_config);
+    int    (*close)(void *transport_config);
+    int    (*send_packet)(uint8_t packet_type, uint8_t *packet, int size);
+    void   (*register_packet_handler)(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
+    const char * (*get_transport_name)(void);
+    // custom extension for UART transport implementations
+    int    (*set_baudrate)(uint32_t baudrate);
+    // support async transport layers, e.g. IRQ driven without buffers
+    int    (*can_send_packet_now)(uint8_t packet_type);
+} hci_transport_t;
+
+typedef struct {
+    const char *device_name;
+    uint32_t   baudrate_init; // initial baud rate
+    uint32_t   baudrate_main; // = 0: same as initial baudrate
+    int   flowcontrol; // 
+} hci_uart_config_t;
+
+
+// inline various hci_transport_X.h files
+extern hci_transport_t * hci_transport_h4_instance(void);
+extern hci_transport_t * hci_transport_h4_dma_instance(void);
+extern hci_transport_t * hci_transport_h4_iphone_instance(void);
+extern hci_transport_t * hci_transport_h5_instance(void);
+extern hci_transport_t * hci_transport_usb_instance(void);
+
+// support for "enforece wake device" in h4 - used by iOS power management
+extern void hci_transport_h4_iphone_set_enforce_wake_device(char *path);
+    
+#if defined __cplusplus
+}
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/hci_transport_usb.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  hci_transport_usb.cpp
+ *
+ *  HCI Transport API implementation for USB
+ *
+ *  Created by Matthias Ringwald on 7/5/09.
+ */
+
+// delock bt class 2 - csr
+// 0a12:0001 (bus 27, device 2)
+
+// Interface Number - Alternate Setting - suggested Endpoint Address - Endpoint Type - Suggested Max Packet Size 
+// HCI Commands 0 0 0x00 Control 8/16/32/64 
+// HCI Events   0 0 0x81 Interrupt (IN) 16 
+// ACL Data     0 0 0x82 Bulk (IN) 32/64 
+// ACL Data     0 0 0x02 Bulk (OUT) 32/64 
+
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+#include "debug.h"
+#include "hci.h"
+#include "hci_transport.h"
+#include "hci_dump.h"
+#include "usbbt.h"
+// prototypes
+static int usb_close(void *transport_config);
+    
+enum {
+    LIB_USB_CLOSED = 0,
+    LIB_USB_OPENED,
+    LIB_USB_DEVICE_OPENDED,
+    LIB_USB_KERNEL_DETACHED,
+    LIB_USB_INTERFACE_CLAIMED,
+    LIB_USB_TRANSFERS_ALLOCATED
+} libusb_state = LIB_USB_CLOSED;
+
+// single instance
+static hci_transport_t * hci_transport_usb = NULL;
+static usbbt* bt = NULL;
+static int usb_process_ds(struct data_source *ds) {
+    if (bt) {
+        bt->poll();
+    }
+    return 0;
+}
+
+static int usb_open(void *transport_config){
+    log_info("usb_open\n");
+    data_source_t *ds = (data_source_t*)malloc(sizeof(data_source_t));
+    ds->process = usb_process_ds;
+    run_loop_add_data_source(ds);
+    return 0;
+}
+static int usb_close(void *transport_config){
+
+    return 0;
+}
+
+static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){
+    //log_info("usb_send_packet\n");
+    if (bt) {
+        bt->send_packet(packet_type, packet, size);
+    }
+    return 0;
+}
+
+static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
+    log_info("registering packet handler\n");
+    if (bt) {
+        bt->setOnPacket(handler);
+    }
+}
+
+static const char * usb_get_transport_name(void){
+    return "USB";
+}
+
+// get usb singleton
+hci_transport_t * hci_transport_usb_instance() {
+    if (!bt) {
+        bt = new usbbt;
+        bt->setup();
+    }
+    if (!hci_transport_usb) {
+        hci_transport_usb = (hci_transport_t*)malloc( sizeof(hci_transport_t));
+        hci_transport_usb->open                          = usb_open;
+        hci_transport_usb->close                         = usb_close;
+        hci_transport_usb->send_packet                   = usb_send_packet;
+        hci_transport_usb->register_packet_handler       = usb_register_packet_handler;
+        hci_transport_usb->get_transport_name            = usb_get_transport_name;
+        hci_transport_usb->set_baudrate                  = NULL;
+        hci_transport_usb->can_send_packet_now           = NULL;
+    }
+    return hci_transport_usb;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/l2cap.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2011-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  l2cap_le.c
+ *
+ *  Logical Link Control and Adaption Protocol (L2CAP) for Bluetooth Low Energy
+ *
+ *  Created by Matthias Ringwald on 5/16/09.
+ */
+
+#include "l2cap.h"
+#include "hci.h"
+#include "hci_dump.h"
+#include "debug.h"
+#include "btstack_memory.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+#include <stdio.h>
+
+static void l2cap_packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
+
+static void (*packet_handler) (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
+static btstack_packet_handler_t attribute_protocol_packet_handler;
+static btstack_packet_handler_t security_protocol_packet_handler;
+
+void l2cap_init(){
+    
+    packet_handler = NULL;
+    attribute_protocol_packet_handler = NULL;
+    security_protocol_packet_handler = NULL;
+    
+    // 
+    // register callback with HCI
+    //
+    hci_register_packet_handler(&l2cap_packet_handler);
+    hci_connectable_control(0); // no services yet
+}
+
+
+/** Register L2CAP packet handlers */
+void l2cap_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size)){
+    packet_handler = handler;
+}
+
+uint8_t *l2cap_get_outgoing_buffer(void){
+    return hci_get_outgoing_acl_packet_buffer() + COMPLETE_L2CAP_HEADER; // 8 bytes
+}
+
+int l2cap_send_prepared_connectionless(uint16_t handle, uint16_t cid, uint16_t len){
+    
+    if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)){
+        log_info("l2cap_send_prepared_to_handle cid %u, cannot send\n", cid);
+        return BTSTACK_ACL_BUFFERS_FULL;
+    }
+    
+    log_debug("l2cap_send_prepared_to_handle cid %u, handle %u\n", cid, handle);
+    
+    uint8_t *acl_buffer = hci_get_outgoing_acl_packet_buffer();
+
+    // 0 - Connection handle : PB=10 : BC=00 
+    bt_store_16(acl_buffer, 0, handle | (2 << 12) | (0 << 14));
+    // 2 - ACL length
+    bt_store_16(acl_buffer, 2,  len + 4);
+    // 4 - L2CAP packet length
+    bt_store_16(acl_buffer, 4,  len + 0);
+    // 6 - L2CAP channel DEST
+    bt_store_16(acl_buffer, 6, cid);    
+    // send
+    int err = hci_send_acl_packet(acl_buffer, len+8);
+        
+    return err;
+}
+
+int l2cap_send_connectionless(uint16_t handle, uint16_t cid, uint8_t *data, uint16_t len){
+
+    if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)){
+        log_info("l2cap_send_internal cid %u, cannot send\n", cid);
+        return BTSTACK_ACL_BUFFERS_FULL;
+    }
+
+    uint8_t *acl_buffer = hci_get_outgoing_acl_packet_buffer();
+
+    memcpy(&acl_buffer[8], data, len);
+
+    return l2cap_send_prepared_connectionless(handle, cid, len);
+}
+
+void l2cap_event_handler( uint8_t *packet, uint16_t size ){
+    
+    switch(packet[0]){
+            
+        case DAEMON_EVENT_HCI_PACKET_SENT:
+            if (attribute_protocol_packet_handler) {
+                (*attribute_protocol_packet_handler)(HCI_EVENT_PACKET, 0, packet, size);
+            }
+            if (security_protocol_packet_handler) {
+                (*security_protocol_packet_handler)(HCI_EVENT_PACKET, 0, packet, size);
+            }
+            break;
+            
+        default:
+            break;
+    }
+    
+    // pass on
+    if (packet_handler) {
+        (*packet_handler)(NULL, HCI_EVENT_PACKET, 0, packet, size);
+    }
+}
+
+void l2cap_acl_handler( uint8_t *packet, uint16_t size ){
+        
+    // Get Channel ID
+    uint16_t channel_id = READ_L2CAP_CHANNEL_ID(packet); 
+    hci_con_handle_t handle = READ_ACL_CONNECTION_HANDLE(packet);
+    
+    switch (channel_id) {
+            
+        case L2CAP_CID_ATTRIBUTE_PROTOCOL:
+            if (attribute_protocol_packet_handler) {
+                (*attribute_protocol_packet_handler)(ATT_DATA_PACKET, handle, &packet[COMPLETE_L2CAP_HEADER], size-COMPLETE_L2CAP_HEADER);
+            }
+            break;
+
+        case L2CAP_CID_SECURITY_MANAGER_PROTOCOL:
+            if (security_protocol_packet_handler) {
+                (*security_protocol_packet_handler)(SM_DATA_PACKET, handle, &packet[COMPLETE_L2CAP_HEADER], size-COMPLETE_L2CAP_HEADER);
+            }
+            break;
+            
+        default: {
+            break;
+        }
+    }
+}
+
+static void l2cap_packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
+    switch (packet_type) {
+        case HCI_EVENT_PACKET:
+            l2cap_event_handler(packet, size);
+            break;
+        case HCI_ACL_DATA_PACKET:
+            l2cap_acl_handler(packet, size);
+            break;
+        default:
+            break;
+    }
+}
+
+
+// Bluetooth 4.0 - allow to register handler for Attribute Protocol and Security Manager Protocol
+void l2cap_register_fixed_channel(btstack_packet_handler_t packet_handler, uint16_t channel_id) {
+    switch(channel_id){
+        case L2CAP_CID_ATTRIBUTE_PROTOCOL:
+            attribute_protocol_packet_handler = packet_handler;
+            break;
+        case L2CAP_CID_SECURITY_MANAGER_PROTOCOL:
+            security_protocol_packet_handler = packet_handler;
+            break;
+    }
+    
+    if (attribute_protocol_packet_handler || security_protocol_packet_handler){
+        hci_connectable_control(1); // new service
+    } else {
+        hci_connectable_control(0); // no services anymore
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/l2cap.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  l2cap.h
+ *
+ *  Logical Link Control and Adaption Protocl (L2CAP)
+ *
+ *  Created by Matthias Ringwald on 5/16/09.
+ */
+
+#pragma once
+
+#include "hci.h"
+#include "l2cap_signaling.h"
+#include <btstack/utils.h>
+#include <btstack/btstack.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+    
+#define L2CAP_SIG_ID_INVALID 0
+
+#define L2CAP_HEADER_SIZE 4
+
+// size of HCI ACL + L2CAP Header for regular data packets (8)
+#define COMPLETE_L2CAP_HEADER (HCI_ACL_HEADER_SIZE + L2CAP_HEADER_SIZE)
+    
+// minimum signaling MTU
+#define L2CAP_MINIMAL_MTU 48
+#define L2CAP_DEFAULT_MTU 672
+    
+// check L2CAP MTU
+#if (L2CAP_MINIMAL_MTU + L2CAP_HEADER_SIZE) > HCI_ACL_PAYLOAD_SIZE
+#error "HCI_ACL_PAYLOAD_SIZE too small for minimal L2CAP MTU of 48 bytes"
+#endif    
+    
+// L2CAP Fixed Channel IDs    
+#define L2CAP_CID_SIGNALING                 0x0001
+#define L2CAP_CID_CONNECTIONLESS_CHANNEL    0x0002
+#define L2CAP_CID_ATTRIBUTE_PROTOCOL        0x0004
+#define L2CAP_CID_SIGNALING_LE              0x0005
+#define L2CAP_CID_SECURITY_MANAGER_PROTOCOL 0x0006
+
+// L2CAP Configuration Result Codes
+#define L2CAP_CONF_RESULT_UNKNOWN_OPTIONS   0x0003
+
+// L2CAP Reject Result Codes
+#define L2CAP_REJ_CMD_UNKNOWN               0x0000
+    
+void l2cap_init(void);
+void l2cap_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size));
+void l2cap_create_channel_internal(void * connection, btstack_packet_handler_t packet_handler, bd_addr_t address, uint16_t psm, uint16_t mtu);
+void l2cap_disconnect_internal(uint16_t local_cid, uint8_t reason);
+uint16_t l2cap_get_remote_mtu_for_local_cid(uint16_t local_cid);
+uint16_t l2cap_max_mtu(void);
+
+void l2cap_block_new_credits(uint8_t blocked);
+int  l2cap_can_send_packet_now(uint16_t local_cid);    // non-blocking UART write
+
+// get outgoing buffer and prepare data
+uint8_t *l2cap_get_outgoing_buffer(void);
+
+int  l2cap_send_prepared(uint16_t local_cid, uint16_t len);
+int l2cap_send_internal(uint16_t local_cid, uint8_t *data, uint16_t len);
+
+int  l2cap_send_prepared_connectionless(uint16_t handle, uint16_t cid, uint16_t len);
+int  l2cap_send_connectionless(uint16_t handle, uint16_t cid, uint8_t *data, uint16_t len);
+    
+void l2cap_close_connection(void *connection);
+
+void l2cap_register_service_internal(void *connection, btstack_packet_handler_t packet_handler, uint16_t psm, uint16_t mtu);
+void l2cap_unregister_service_internal(void *connection, uint16_t psm);
+
+void l2cap_accept_connection_internal(uint16_t local_cid);
+void l2cap_decline_connection_internal(uint16_t local_cid, uint8_t reason);
+
+// Bluetooth 4.0 - allows to register handler for Attribute Protocol and Security Manager Protocol
+void l2cap_register_fixed_channel(btstack_packet_handler_t packet_handler, uint16_t channel_id);
+
+
+// private structs
+typedef enum {
+    L2CAP_STATE_CLOSED = 1,           // no baseband
+    L2CAP_STATE_WILL_SEND_CREATE_CONNECTION,
+    L2CAP_STATE_WAIT_CONNECTION_COMPLETE,
+    L2CAP_STATE_WAIT_CLIENT_ACCEPT_OR_REJECT,
+    L2CAP_STATE_WAIT_CONNECT_RSP, // from peer
+    L2CAP_STATE_CONFIG,
+    L2CAP_STATE_OPEN,
+    L2CAP_STATE_WAIT_DISCONNECT,  // from application
+    L2CAP_STATE_WILL_SEND_CONNECTION_REQUEST,
+    L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_DECLINE,
+    L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_ACCEPT,   
+    L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST,
+    L2CAP_STATE_WILL_SEND_DISCONNECT_RESPONSE,
+} L2CAP_STATE;
+
+typedef enum {
+    L2CAP_CHANNEL_STATE_VAR_NONE                  = 0,
+    L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_REQ         = 1 << 0,
+    L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP         = 1 << 1,
+    L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ         = 1 << 2,
+    L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP         = 1 << 3,
+    L2CAP_CHANNEL_STATE_VAR_SENT_CONF_REQ         = 1 << 4,
+    L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP         = 1 << 5,
+    L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_MTU     = 1 << 6,  // in CONF RSP, add MTU field
+    L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_CONT    = 1 << 7,  // in CONF RSP, set CONTINUE flag
+    L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_INVALID = 1 << 8,  // in CONF RSP, send UNKNOWN OPTIONS
+    L2CAP_CHANNEL_STATE_VAR_SEND_CMD_REJ_UNKNOWN  = 1 << 9,  // send CMD_REJ with reason unknown
+} L2CAP_CHANNEL_STATE_VAR;
+
+// info regarding an actual coneection
+typedef struct {
+    // linked list - assert: first field
+    linked_item_t    item;
+    
+    L2CAP_STATE state;
+    L2CAP_CHANNEL_STATE_VAR state_var;
+    
+    bd_addr_t address;
+    hci_con_handle_t handle;
+    
+    uint8_t   remote_sig_id;    // used by other side, needed for delayed response
+    uint8_t   local_sig_id;     // own signaling identifier
+    
+    uint16_t  local_cid;
+    uint16_t  remote_cid;
+    
+    uint16_t  local_mtu;
+    uint16_t  remote_mtu;
+    
+    uint16_t  psm;
+    
+    uint8_t   packets_granted;    // number of L2CAP/ACL packets client is allowed to send
+    
+    uint8_t   reason; // used in decline internal
+    
+    // client connection
+    void * connection;
+    
+    // internal connection
+    btstack_packet_handler_t packet_handler;
+    
+} l2cap_channel_t;
+
+// info regarding potential connections
+typedef struct {
+    // linked list - assert: first field
+    linked_item_t    item;
+    
+    // service id
+    uint16_t  psm;
+    
+    // incoming MTU
+    uint16_t mtu;
+    
+    // client connection
+    void *connection;    
+    
+    // internal connection
+    btstack_packet_handler_t packet_handler;
+    
+} l2cap_service_t;
+
+
+typedef struct l2cap_signaling_response {
+    hci_con_handle_t handle;
+    uint8_t  sig_id;
+    uint8_t  code;
+    uint16_t data; // infoType for INFORMATION REQUEST, result for CONNECTION request and command unknown
+} l2cap_signaling_response_t;
+    
+    
+#if defined __cplusplus
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/l2cap_signaling.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  l2cap_signaling.h
+ *
+ *  Created by Matthias Ringwald on 7/23/09.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdarg.h>
+#include <btstack/utils.h>
+#include <btstack/hci_cmds.h>
+
+typedef enum {
+    COMMAND_REJECT = 1,
+    CONNECTION_REQUEST,
+    CONNECTION_RESPONSE,
+    CONFIGURE_REQUEST,
+    CONFIGURE_RESPONSE,
+    DISCONNECTION_REQUEST,
+    DISCONNECTION_RESPONSE,
+    ECHO_REQUEST,
+    ECHO_RESPONSE,
+    INFORMATION_REQUEST,
+    INFORMATION_RESPONSE
+} L2CAP_SIGNALING_COMMANDS;
+
+uint16_t l2cap_create_signaling_internal(uint8_t * acl_buffer,hci_con_handle_t handle, L2CAP_SIGNALING_COMMANDS cmd, uint8_t identifier, va_list argptr);
+uint8_t  l2cap_next_sig_id(void);
+uint16_t l2cap_next_local_cid(void);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/linked_list.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  linked_list.c
+ *
+ *  Created by Matthias Ringwald on 7/13/09.
+ */
+
+#include <btstack/linked_list.h>
+#include <stdlib.h>
+/**
+ * tests if list is empty
+ */
+int  linked_list_empty(linked_list_t * list){
+    return *list == (void *) 0;
+}
+
+/**
+ * linked_list_get_last_item
+ */
+linked_item_t * linked_list_get_last_item(linked_list_t * list){        // <-- find the last item in the list
+    linked_item_t *lastItem = NULL;
+    linked_item_t *it;
+    for (it = *list; it ; it = it->next){
+        if (it) {
+            lastItem = it;
+        }
+    }
+    return lastItem;
+}
+
+
+/**
+ * linked_list_add
+ */
+void linked_list_add(linked_list_t * list, linked_item_t *item){        // <-- add item to list
+    // check if already in list
+    linked_item_t *it;
+    for (it = *list; it ; it = it->next){
+        if (it == item) {
+            return;
+        }
+    }
+    // add first
+    item->next = *list;
+    *list = item;
+}
+
+void linked_list_add_tail(linked_list_t * list, linked_item_t *item){   // <-- add item to list as last element
+    // check if already in list
+    linked_item_t *it;
+    for (it = (linked_item_t *) list; it->next ; it = it->next){
+        if (it->next == item) {
+            return;
+        }
+    }
+    item->next = (linked_item_t*) 0;
+    it->next = item;
+}
+
+/**
+ * Remove data_source from run loop
+ *
+ * @note: assumes that data_source_t.next is first element in data_source
+ */
+int  linked_list_remove(linked_list_t * list, linked_item_t *item){    // <-- remove item from list
+    linked_item_t *it;
+    for (it = (linked_item_t *) list; it ; it = it->next){
+        if (it->next == item){
+            it->next =  item->next;
+            return 0;
+        }
+    }
+    return -1;
+}
+
+void linked_item_set_user(linked_item_t *item, void *user_data){
+    item->next = (linked_item_t *) 0;
+    item->user_data = user_data;
+}
+
+void * linked_item_get_user(linked_item_t *item) {
+    return item->user_data;
+}
+
+#if 0
+#include <stdio.h>
+void test_linked_list(){
+    linked_list_t testList = 0;
+    linked_item_t itemA;
+    linked_item_t itemB;
+    linked_item_t itemC;
+    linked_item_set_user(&itemA, (void *) 0);
+    linked_item_set_user(&itemB, (void *) 0);
+    linked_list_add(&testList, &itemA);
+    linked_list_add(&testList, &itemB);
+    linked_list_add_tail(&testList, &itemC);
+    // linked_list_remove(&testList, &itemB);
+    linked_item_t *it;
+    for (it = (linked_item_t *) &testList; it ; it = it->next){
+        if (it->next == &itemA) printf("Item A\n");
+        if (it->next == &itemB) printf("Item B\n");
+        if (it->next == &itemC) printf("Item C\n");
+        /* if (it->next == &itemB){
+            it->next =  it->next;
+            printf(" remove\n");
+        } else {
+            printf(" keep\n");
+        
+         */
+    }
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/linked_list.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  linked_list.h
+ *
+ *  Created by Matthias Ringwald on 7/13/09.
+ */
+
+#pragma once
+
+#if defined __cplusplus
+extern "C" {
+#endif
+    
+typedef struct linked_item {
+    struct linked_item *next; // <-- next element in list, or NULL
+    void *user_data;          // <-- pointer to struct base
+} linked_item_t;
+
+typedef linked_item_t * linked_list_t;
+
+void linked_item_set_user(linked_item_t *item, void *user_data);        // <-- set user data
+void * linked_item_get_user(linked_item_t *item);                       // <-- get user data
+int  linked_list_empty(linked_list_t * list);
+void linked_list_add(linked_list_t * list, linked_item_t *item);        // <-- add item to list as first element
+void linked_list_add_tail(linked_list_t * list, linked_item_t *item);   // <-- add item to list as last element
+int  linked_list_remove(linked_list_t * list, linked_item_t *item);     // <-- remove item from list
+linked_item_t * linked_list_get_last_item(linked_list_t * list);        // <-- find the last item in the list
+
+void test_linked_list(void);
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/memory_pool.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  memory_pool.c
+ *
+ *  Fixed-size block allocation
+ *
+ *  Free blocks are kept in singly linked list
+ *
+ */
+
+#include <btstack/memory_pool.h>
+#include <stddef.h>
+
+typedef struct node {
+    struct node * next;
+} node_t;
+
+void memory_pool_create(memory_pool_t *pool, void * storage, int count, int block_size){
+    node_t *free_blocks = (node_t*) pool;
+    char   *mem_ptr = (char *) storage;
+    int i;
+    
+    // create singly linked list of all available blocks
+    free_blocks->next = NULL;
+    for (i = 0 ; i < count ; i++){
+        memory_pool_free(pool, mem_ptr);
+        mem_ptr += block_size;
+    }
+}
+
+void * memory_pool_get(memory_pool_t *pool){
+    node_t *free_blocks = (node_t*) pool;
+    
+    if (!free_blocks->next) return NULL;
+    
+    // remove first
+    node_t *node      = free_blocks->next;
+    free_blocks->next = node->next;
+    
+    return (void*) node;
+}
+
+void memory_pool_free(memory_pool_t *pool, void * block){
+    node_t *free_blocks = (node_t*) pool;
+    node_t *node        = (node_t*) block;
+    // add block as node to list
+    node->next          = free_blocks->next;
+    free_blocks->next   = node;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/memory_pool.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  memory_pool.h
+ *
+ *  @Brief Fixed-size block allocation
+ *
+ *  @Assumption block_size >= sizeof(void *)
+ *  @Assumption size of storage >= count * block_size
+ *
+ *  @Note minimal implementation, no error checking/handling
+ */
+
+#pragma once
+
+typedef void * memory_pool_t;
+
+// initialize memory pool with with given storage, block size and count
+void   memory_pool_create(memory_pool_t *pool, void * storage, int count, int block_size);
+
+// get free block from pool, @returns NULL or pointer to block
+void * memory_pool_get(memory_pool_t *pool);
+
+// return previously reserved block to memory pool
+void   memory_pool_free(memory_pool_t *pool, void * block);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/remote_device_db.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/**
+ * interface to provide link key and remote name storage
+ */
+
+#pragma once
+
+#include <btstack/utils.h>
+
+typedef struct {
+
+    // management
+    void (*open)(void);
+    void (*close)(void);
+    
+    // link key
+    int  (*get_link_key)(bd_addr_t *bd_addr, link_key_t *link_key);
+    void (*put_link_key)(bd_addr_t *bd_addr, link_key_t *key);
+    void (*delete_link_key)(bd_addr_t *bd_addr);
+    
+    // remote name
+    int  (*get_name)(bd_addr_t *bd_addr, device_name_t *device_name);
+    void (*put_name)(bd_addr_t *bd_addr, device_name_t *device_name);
+    void (*delete_name)(bd_addr_t *bd_addr);
+
+    // persistent rfcomm channel
+    uint8_t (*persistent_rfcomm_channel)(char *servicename);
+
+} remote_device_db_t;
+
+extern remote_device_db_t remote_device_db_iphone;
+extern const remote_device_db_t remote_device_db_memory;
+
+// MARK: non-persisten implementation
+#include <btstack/linked_list.h>
+#define MAX_NAME_LEN 32
+typedef struct {
+    // linked list - assert: first field
+    linked_item_t    item;
+    
+    bd_addr_t bd_addr;
+} db_mem_device_t;
+
+typedef struct {
+    db_mem_device_t device;
+    link_key_t link_key;
+} db_mem_device_link_key_t;
+
+typedef struct {
+    db_mem_device_t device;
+    char device_name[MAX_NAME_LEN];
+} db_mem_device_name_t;
+
+typedef struct {
+    // linked list - assert: first field
+    linked_item_t    item;
+    
+    char service_name[MAX_NAME_LEN];
+    uint8_t channel;
+} db_mem_service_t;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/remote_device_db_memory.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2010 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "remote_device_db.h"
+#include "btstack_memory.h"
+#include "debug.h"
+
+#include <btstack/utils.h>
+#include <btstack/linked_list.h>
+
+// This lists should be only accessed by tests.
+linked_list_t db_mem_link_keys = NULL;
+linked_list_t db_mem_names = NULL;
+static linked_list_t db_mem_services = NULL;
+
+// Device info
+static void db_open(void){
+}
+
+static void db_close(void){ 
+}
+
+static db_mem_device_t * get_item(linked_list_t list, bd_addr_t *bd_addr) {
+    linked_item_t *it;
+    for (it = (linked_item_t *) list; it ; it = it->next){
+        db_mem_device_t * item = (db_mem_device_t *) it;
+        if (BD_ADDR_CMP(item->bd_addr, *bd_addr) == 0) {
+            return item;
+        }
+    }
+    return NULL;
+}
+
+static int get_name(bd_addr_t *bd_addr, device_name_t *device_name) {
+    db_mem_device_name_t * item = (db_mem_device_name_t *) get_item(db_mem_names, bd_addr);
+    
+    if (!item) return 0;
+    
+    strncpy((char*)device_name, item->device_name, MAX_NAME_LEN);
+    
+    linked_list_remove(&db_mem_names, (linked_item_t *) item);
+    linked_list_add(&db_mem_names, (linked_item_t *) item);
+    
+    return 1;
+}
+
+static int get_link_key(bd_addr_t *bd_addr, link_key_t *link_key) {
+    db_mem_device_link_key_t * item = (db_mem_device_link_key_t *) get_item(db_mem_link_keys, bd_addr);
+    
+    if (!item) return 0;
+    
+    memcpy(link_key, item->link_key, LINK_KEY_LEN);
+    
+    linked_list_remove(&db_mem_link_keys, (linked_item_t *) item);
+    linked_list_add(&db_mem_link_keys, (linked_item_t *) item);
+
+    return 1;
+}
+
+static void delete_link_key(bd_addr_t *bd_addr){
+    db_mem_device_t * item = get_item(db_mem_link_keys, bd_addr);
+    
+    if (!item) return;
+    
+    linked_list_remove(&db_mem_link_keys, (linked_item_t *) item);
+    btstack_memory_db_mem_device_link_key_free(item);
+}
+
+
+static void put_link_key(bd_addr_t *bd_addr, link_key_t *link_key){
+    db_mem_device_link_key_t * existingRecord = (db_mem_device_link_key_t *) get_item(db_mem_link_keys, bd_addr);
+    
+    if (existingRecord){
+        memcpy(existingRecord->link_key, link_key, LINK_KEY_LEN);
+        return;
+    }
+    
+    // Record not found, create new one for this device
+    db_mem_device_link_key_t * newItem = (db_mem_device_link_key_t*) btstack_memory_db_mem_device_link_key_get();
+    if (!newItem){
+        newItem = (db_mem_device_link_key_t*)linked_list_get_last_item(&db_mem_link_keys);
+    }
+    
+    if (!newItem) return;
+    
+    memcpy(newItem->device.bd_addr, bd_addr, sizeof(bd_addr_t));
+    memcpy(newItem->link_key, link_key, LINK_KEY_LEN);
+    linked_list_add(&db_mem_link_keys, (linked_item_t *) newItem);
+}
+
+static void delete_name(bd_addr_t *bd_addr){
+    db_mem_device_t * item = get_item(db_mem_names, bd_addr);
+    
+    if (!item) return;
+    
+    linked_list_remove(&db_mem_names, (linked_item_t *) item);
+    btstack_memory_db_mem_device_name_free(item);    
+}
+
+static void put_name(bd_addr_t *bd_addr, device_name_t *device_name){
+    db_mem_device_name_t * existingRecord = (db_mem_device_name_t *) get_item(db_mem_names, bd_addr);
+    
+    if (existingRecord){
+        strncpy(existingRecord->device_name, (const char*) device_name, MAX_NAME_LEN);
+        return;
+    }
+    
+    // Record not found, create a new one for this device
+    db_mem_device_name_t * newItem = (db_mem_device_name_t *) btstack_memory_db_mem_device_name_get();
+    if (!newItem) {
+        newItem = (db_mem_device_name_t*)linked_list_get_last_item(&db_mem_names);
+    };
+
+    if (!newItem) return;
+    
+    memcpy(newItem->device.bd_addr, bd_addr, sizeof(bd_addr_t));
+    strncpy(newItem->device_name, (const char*) device_name, MAX_NAME_LEN);
+    linked_list_add(&db_mem_names, (linked_item_t *) newItem);
+}
+
+
+// MARK: PERSISTENT RFCOMM CHANNEL ALLOCATION
+
+static uint8_t persistent_rfcomm_channel(char *serviceName){
+    linked_item_t *it;
+    db_mem_service_t * item;
+    uint8_t max_channel = 1;
+
+    for (it = (linked_item_t *) db_mem_services; it ; it = it->next){
+        item = (db_mem_service_t *) it;
+        if (strncmp(item->service_name, serviceName, MAX_NAME_LEN) == 0) {
+            // Match found
+            return item->channel;
+        }
+
+        // TODO prevent overflow
+        if (item->channel >= max_channel) max_channel = item->channel + 1;
+    }
+
+    // Allocate new persistant channel
+    db_mem_service_t * newItem = (db_mem_service_t *) btstack_memory_db_mem_service_get();
+
+    if (!newItem) return 0;
+    
+    strncpy(newItem->service_name, serviceName, MAX_NAME_LEN);
+    newItem->channel = max_channel;
+    linked_list_add(&db_mem_services, (linked_item_t *) newItem);
+    return max_channel;
+}
+
+
+const remote_device_db_t remote_device_db_memory = {
+    db_open,
+    db_close,
+    get_link_key,
+    put_link_key,
+    delete_link_key,
+    get_name,
+    put_name,
+    delete_name,
+    persistent_rfcomm_channel
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/rfcomm.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  RFCOMM.h
+ */
+
+#include <btstack/btstack.h>
+#include <btstack/utils.h>
+
+#include <stdint.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+    
+void rfcomm_init(void);
+
+// register packet handler
+void rfcomm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
+void rfcomm_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type,
+                                                    uint16_t channel, uint8_t *packet, uint16_t size));
+
+// BTstack Internal RFCOMM API
+void rfcomm_create_channel_internal(void * connection, bd_addr_t *addr, uint8_t channel);
+void rfcomm_create_channel_with_initial_credits_internal(void * connection, bd_addr_t *addr, uint8_t server_channel, uint8_t initial_credits);
+void rfcomm_disconnect_internal(uint16_t rfcomm_cid);
+void rfcomm_register_service_internal(void * connection, uint8_t channel, uint16_t max_frame_size);
+void rfcomm_register_service_with_initial_credits_internal(void * connection, uint8_t channel, uint16_t max_frame_size, uint8_t initial_credits);
+void rfcomm_unregister_service_internal(uint8_t service_channel);
+void rfcomm_accept_connection_internal(uint16_t rfcomm_cid);
+void rfcomm_decline_connection_internal(uint16_t rfcomm_cid);
+void rfcomm_grant_credits(uint16_t rfcomm_cid, uint8_t credits);
+int  rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len);
+void rfcomm_close_connection(void *connection);
+
+#define UNLIMITED_INCOMING_CREDITS 0xff
+
+// private structs
+typedef enum {
+    RFCOMM_MULTIPLEXER_CLOSED = 1,
+    RFCOMM_MULTIPLEXER_W4_CONNECT,  // outgoing
+    RFCOMM_MULTIPLEXER_SEND_SABM_0,     //    "
+    RFCOMM_MULTIPLEXER_W4_UA_0,     //    "
+    RFCOMM_MULTIPLEXER_W4_SABM_0,   // incoming
+    RFCOMM_MULTIPLEXER_SEND_UA_0,
+    RFCOMM_MULTIPLEXER_OPEN,
+    RFCOMM_MULTIPLEXER_SEND_UA_0_AND_DISC
+} RFCOMM_MULTIPLEXER_STATE;
+
+typedef enum {
+    MULT_EV_READY_TO_SEND = 1,
+    
+} RFCOMM_MULTIPLEXER_EVENT;
+
+typedef enum {
+    RFCOMM_CHANNEL_CLOSED = 1,
+    RFCOMM_CHANNEL_W4_MULTIPLEXER,
+    RFCOMM_CHANNEL_SEND_UIH_PN,
+    RFCOMM_CHANNEL_W4_PN_RSP,
+    RFCOMM_CHANNEL_SEND_SABM_W4_UA,
+    RFCOMM_CHANNEL_W4_UA,
+    RFCOMM_CHANNEL_INCOMING_SETUP,
+    RFCOMM_CHANNEL_DLC_SETUP,
+    RFCOMM_CHANNEL_OPEN,
+    RFCOMM_CHANNEL_SEND_UA_AFTER_DISC,
+    RFCOMM_CHANNEL_SEND_DISC,
+    RFCOMM_CHANNEL_SEND_DM,
+    
+} RFCOMM_CHANNEL_STATE;
+
+/*
+typedef enum {
+    RFCOMM_CHANNEL_STATE_VAR_NONE            = 0,
+    RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED = 1 << 0,
+    RFCOMM_CHANNEL_STATE_VAR_RCVD_PN         = 1 << 1,
+    RFCOMM_CHANNEL_STATE_VAR_RCVD_RPN        = 1 << 2,
+    RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM       = 1 << 3,
+    
+    RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_CMD    = 1 << 4,
+    RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP    = 1 << 5,
+    RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP     = 1 << 6,
+    RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_INFO   = 1 << 7,
+    
+    RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP    = 1 << 8,
+    RFCOMM_CHANNEL_STATE_VAR_SEND_UA         = 1 << 9,
+    RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD    = 1 << 10,
+    RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP    = 1 << 11,
+    
+    RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS    = 1 << 12,
+    RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_CMD    = 1 << 13,
+    RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP    = 1 << 14,
+    RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS    = 1 << 15,
+} RFCOMM_CHANNEL_STATE_VAR;
+*/
+
+enum {
+    RFCOMM_CHANNEL_STATE_VAR_NONE            = 0,
+    RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED = 1 << 0,
+    RFCOMM_CHANNEL_STATE_VAR_RCVD_PN         = 1 << 1,
+    RFCOMM_CHANNEL_STATE_VAR_RCVD_RPN        = 1 << 2,
+    RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM       = 1 << 3,
+    
+    RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_CMD    = 1 << 4,
+    RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP    = 1 << 5,
+    RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP     = 1 << 6,
+    RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_INFO   = 1 << 7,
+    
+    RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP    = 1 << 8,
+    RFCOMM_CHANNEL_STATE_VAR_SEND_UA         = 1 << 9,
+    RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD    = 1 << 10,
+    RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP    = 1 << 11,
+    
+    RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS    = 1 << 12,
+    RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_CMD    = 1 << 13,
+    RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP    = 1 << 14,
+    RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS    = 1 << 15,
+};
+typedef uint16_t RFCOMM_CHANNEL_STATE_VAR;
+
+typedef enum {
+    CH_EVT_RCVD_SABM = 1,
+    CH_EVT_RCVD_UA,
+    CH_EVT_RCVD_PN,
+    CH_EVT_RCVD_PN_RSP,
+    CH_EVT_RCVD_DISC,
+    CH_EVT_RCVD_DM,
+    CH_EVT_RCVD_MSC_CMD,
+    CH_EVT_RCVD_MSC_RSP,
+    CH_EVT_RCVD_RPN_CMD,
+    CH_EVT_RCVD_RPN_REQ,
+    CH_EVT_RCVD_CREDITS,
+    CH_EVT_MULTIPLEXER_READY,
+    CH_EVT_READY_TO_SEND,
+} RFCOMM_CHANNEL_EVENT;
+
+typedef struct rfcomm_channel_event {
+    RFCOMM_CHANNEL_EVENT type;
+} rfcomm_channel_event_t;
+
+typedef struct rfcomm_channel_event_pn {
+    rfcomm_channel_event_t super;
+    uint16_t max_frame_size;
+    uint8_t  priority;
+    uint8_t  credits_outgoing;
+} rfcomm_channel_event_pn_t;
+
+typedef struct rfcomm_rpn_data {
+    uint8_t baud_rate;
+    uint8_t flags;
+    uint8_t flow_control;
+    uint8_t xon;
+    uint8_t xoff;
+    uint8_t parameter_mask_0;   // first byte
+    uint8_t parameter_mask_1;   // second byte
+} rfcomm_rpn_data_t;
+
+typedef struct rfcomm_channel_event_rpn {
+    rfcomm_channel_event_t super;
+    rfcomm_rpn_data_t data;
+} rfcomm_channel_event_rpn_t;
+
+// info regarding potential connections
+typedef struct {
+    // linked list - assert: first field
+    linked_item_t    item;
+    
+    // server channel
+    uint8_t server_channel;
+    
+    // incoming max frame size
+    uint16_t max_frame_size;
+
+    // use incoming flow control
+    uint8_t incoming_flow_control;
+    
+    // initial incoming credits
+    uint8_t incoming_initial_credits;
+    
+    // client connection
+    void *connection;    
+    
+    // internal connection
+    btstack_packet_handler_t packet_handler;
+    
+} rfcomm_service_t;
+
+// info regarding multiplexer
+// note: spec mandates single multplexer per device combination
+typedef struct {
+    // linked list - assert: first field
+    linked_item_t    item;
+    
+    timer_source_t   timer;
+    int              timer_active;
+    
+    RFCOMM_MULTIPLEXER_STATE state;    
+    
+    uint16_t  l2cap_cid;
+    uint8_t   l2cap_credits;
+    
+    bd_addr_t remote_addr;
+    hci_con_handle_t con_handle;
+    
+    uint8_t   outgoing;
+    
+    // hack to deal with authentication failure only observed by remote side
+    uint8_t   at_least_one_connection;
+    
+    uint16_t max_frame_size;
+    
+    // send DM for DLCI != 0
+    uint8_t send_dm_for_dlci;
+    
+} rfcomm_multiplexer_t;
+
+// info regarding an actual coneection
+typedef struct {
+    // linked list - assert: first field
+    linked_item_t    item;
+    
+    rfcomm_multiplexer_t *multiplexer;
+    uint16_t rfcomm_cid;
+    uint8_t  outgoing;
+    uint8_t  dlci; 
+    
+    // number of packets granted to client
+    uint8_t packets_granted;
+
+    // credits for outgoing traffic
+    uint8_t credits_outgoing;
+    
+    // number of packets remote will be granted
+    uint8_t new_credits_incoming;
+
+    // credits for incoming traffic
+    uint8_t credits_incoming;
+    
+    // use incoming flow control
+    uint8_t incoming_flow_control;
+    
+    // channel state
+    RFCOMM_CHANNEL_STATE state;
+    
+    // state variables used in RFCOMM_CHANNEL_INCOMING
+    RFCOMM_CHANNEL_STATE_VAR state_var;
+    
+    // priority set by incoming side in PN
+    uint8_t pn_priority;
+    
+    // negotiated frame size
+    uint16_t max_frame_size;
+    
+    // rpn data
+    rfcomm_rpn_data_t rpn_data;
+    
+    // server channel (see rfcomm_service_t) - NULL => outgoing channel
+    rfcomm_service_t * service;
+    
+    // internal connection
+    btstack_packet_handler_t packet_handler;
+    
+    // client connection
+    void * connection;
+    
+} rfcomm_channel_t;
+
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/run_loop.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2009 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  run_loop.c
+ *
+ *  Created by Matthias Ringwald on 6/6/09.
+ */
+
+#include <btstack/run_loop.h>
+
+#include <stdio.h>
+#include <stdlib.h>  // exit()
+
+#include "run_loop_private.h"
+
+#include "debug.h"
+#include "config.h"
+
+static run_loop_t * the_run_loop = NULL;
+
+extern const run_loop_t run_loop_embedded;
+
+#ifdef USE_POSIX_RUN_LOOP
+extern run_loop_t run_loop_posix;
+#endif
+
+#ifdef USE_COCOA_RUN_LOOP
+extern run_loop_t run_loop_cocoa;
+#endif
+
+// assert run loop initialized
+void run_loop_assert(void){
+#ifndef EMBEDDED
+    if (!the_run_loop){
+        log_error("ERROR: run_loop function called before run_loop_init!\n");
+        exit(10);
+    }
+#endif
+}
+
+/**
+ * Add data_source to run_loop
+ */
+void run_loop_add_data_source(data_source_t *ds){
+    run_loop_assert();
+    the_run_loop->add_data_source(ds);
+}
+
+/**
+ * Remove data_source from run loop
+ */
+int run_loop_remove_data_source(data_source_t *ds){
+    run_loop_assert();
+    return the_run_loop->remove_data_source(ds);
+}
+
+/**
+ * Add timer to run_loop (keep list sorted)
+ */
+void run_loop_add_timer(timer_source_t *ts){
+    run_loop_assert();
+    the_run_loop->add_timer(ts);
+}
+
+/**
+ * Remove timer from run loop
+ */
+int run_loop_remove_timer(timer_source_t *ts){
+    run_loop_assert();
+    return the_run_loop->remove_timer(ts);
+}
+
+void run_loop_timer_dump(){
+    run_loop_assert();
+    the_run_loop->dump_timer();
+}
+
+/**
+ * Execute run_loop
+ */
+void run_loop_execute() {
+    run_loop_assert();
+    the_run_loop->execute();
+}
+
+// init must be called before any other run_loop call
+void run_loop_init(RUN_LOOP_TYPE type){
+#ifndef EMBEDDED
+    if (the_run_loop){
+        log_error("ERROR: run loop initialized twice!\n");
+        exit(10);
+    }
+#endif
+    switch (type) {
+#ifdef EMBEDDED
+        case RUN_LOOP_EMBEDDED:
+            the_run_loop = (run_loop_t*) &run_loop_embedded;
+            break;
+#endif
+#ifdef USE_POSIX_RUN_LOOP
+        case RUN_LOOP_POSIX:
+            the_run_loop = &run_loop_posix;
+            break;
+#endif
+#ifdef USE_COCOA_RUN_LOOP
+        case RUN_LOOP_COCOA:
+            the_run_loop = &run_loop_cocoa;
+            break;
+#endif
+        default:
+#ifndef EMBEDDED
+            log_error("ERROR: invalid run loop type %u selected!\n", type);
+            exit(10);
+#endif
+            break;
+    }
+    the_run_loop->init();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/run_loop.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2009 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  run_loop.h
+ *
+ *  Created by Matthias Ringwald on 6/6/09.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include <btstack/linked_list.h>
+
+#include <stdint.h>
+
+#ifdef HAVE_TIME
+#include <sys/time.h>
+#endif
+
+#if defined __cplusplus
+extern "C" {
+#endif
+    
+typedef enum {
+    RUN_LOOP_POSIX = 1,
+    RUN_LOOP_COCOA,
+    RUN_LOOP_EMBEDDED
+} RUN_LOOP_TYPE;
+
+typedef struct data_source {
+    linked_item_t item;
+    int  fd;                                 // <-- file descriptor to watch or 0
+    int  (*process)(struct data_source *ds); // <-- do processing
+} data_source_t;
+
+typedef struct timer {
+    linked_item_t item; 
+#ifdef HAVE_TIME
+    struct timeval timeout;                  // <-- next timeout
+#endif
+#ifdef HAVE_TICK
+    uint32_t timeout;                       // timeout in system ticks
+#endif
+    void  (*process)(struct timer *ts);      // <-- do processing
+} timer_source_t;
+
+
+// set timer based on current time
+void run_loop_set_timer(timer_source_t *a, uint32_t timeout_in_ms);
+
+// add/remove timer_source
+void run_loop_add_timer(timer_source_t *timer); 
+int  run_loop_remove_timer(timer_source_t *timer);
+
+// init must be called before any other run_loop call
+void run_loop_init(RUN_LOOP_TYPE type);
+
+// add/remove data_source
+void run_loop_add_data_source(data_source_t *dataSource);
+int  run_loop_remove_data_source(data_source_t *dataSource);
+
+
+// execute configured run_loop
+void run_loop_execute(void);
+
+// hack to fix HCI timer handling
+#ifdef HAVE_TICK
+uint32_t embedded_get_ticks(void);
+uint32_t embedded_ticks_for_ms(uint32_t time_in_ms);
+#endif
+#ifdef EMBEDDED
+void     embedded_trigger(void);    
+#endif
+#if defined __cplusplus
+}
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/run_loop_embedded.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  run_loop_embedded.c
+ *
+ *  For this run loop, we assume that there's no global way to wait for a list
+ *  of data sources to get ready. Instead, each data source has to queried
+ *  individually. Calling ds->isReady() before calling ds->process() doesn't 
+ *  make sense, so we just poll each data source round robin.
+ *
+ *  To support an idle state, where an MCU could go to sleep, the process function
+ *  has to return if it has to called again as soon as possible
+ *
+ *  After calling process() on every data source and evaluating the pending timers,
+ *  the idle hook gets called if no data source did indicate that it needs to be
+ *  called right away.
+ *
+ */
+
+
+#include <btstack/run_loop.h>
+#include <btstack/linked_list.h>
+#include <btstack/hal_tick.h>
+#include <btstack/hal_cpu.h>
+
+#include "run_loop_private.h"
+#include "debug.h"
+
+#include <stddef.h> // NULL
+
+// the run loop
+static linked_list_t data_sources;
+
+static linked_list_t timers;
+
+#ifdef HAVE_TICK
+static uint32_t system_ticks;
+#endif
+
+static int trigger_event_received = 0;
+
+/**
+ * trigger run loop iteration
+ */
+void embedded_trigger(void){
+    trigger_event_received = 1;
+}
+
+/**
+ * Add data_source to run_loop
+ */
+void embedded_add_data_source(data_source_t *ds){
+    linked_list_add(&data_sources, (linked_item_t *) ds);
+}
+
+/**
+ * Remove data_source from run loop
+ */
+int embedded_remove_data_source(data_source_t *ds){
+    return linked_list_remove(&data_sources, (linked_item_t *) ds);
+}
+
+/**
+ * Add timer to run_loop (keep list sorted)
+ */
+void embedded_add_timer(timer_source_t *ts){
+#ifdef HAVE_TICK
+    linked_item_t *it;
+    for (it = (linked_item_t *) &timers; it->next ; it = it->next){
+        if (ts->timeout < ((timer_source_t *) it->next)->timeout) {
+            break;
+        }
+    }
+    ts->item.next = it->next;
+    it->next = (linked_item_t *) ts;
+    // log_info("Added timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
+    // embedded_dump_timer();
+#endif
+}
+
+/**
+ * Remove timer from run loop
+ */
+int embedded_remove_timer(timer_source_t *ts){
+#ifdef HAVE_TICK    
+    // log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
+    return linked_list_remove(&timers, (linked_item_t *) ts);
+#else
+    return 0;
+#endif
+}
+
+void embedded_dump_timer(void){
+#ifdef HAVE_TICK
+#ifdef ENABLE_LOG_INFO 
+    linked_item_t *it;
+    int i = 0;
+    for (it = (linked_item_t *) timers; it ; it = it->next){
+        timer_source_t *ts = (timer_source_t*) it;
+        log_info("timer %u, timeout %u\n", i, (unsigned int) ts->timeout);
+    }
+#endif
+#endif
+}
+
+/**
+ * Execute run_loop
+ */
+void embedded_execute(void) {
+    data_source_t *ds;
+
+    while (1) {
+
+        // process data sources
+        data_source_t *next;
+        for (ds = (data_source_t *) data_sources; ds != NULL ; ds = next){
+            next = (data_source_t *) ds->item.next; // cache pointer to next data_source to allow data source to remove itself
+            ds->process(ds);
+        }
+        
+#ifdef HAVE_TICK
+        // process timers
+        while (timers) {
+            timer_source_t *ts = (timer_source_t *) timers;
+            if (ts->timeout > system_ticks) break;
+            run_loop_remove_timer(ts);
+            ts->process(ts);
+        }
+#endif
+        
+        // disable IRQs and check if run loop iteration has been requested. if not, go to sleep
+        hal_cpu_disable_irqs();
+        if (trigger_event_received){
+            hal_cpu_enable_irqs_and_sleep();
+            continue;
+        }
+        hal_cpu_enable_irqs();
+    }
+}
+
+#ifdef HAVE_TICK
+static void embedded_tick_handler(void){
+    system_ticks++;
+    trigger_event_received = 1;
+}
+
+uint32_t embedded_get_ticks(void){
+    return system_ticks;
+}
+
+uint32_t embedded_ticks_for_ms(uint32_t time_in_ms){
+    return time_in_ms / hal_tick_get_tick_period_in_ms();
+}
+
+// set timer
+void run_loop_set_timer(timer_source_t *ts, uint32_t timeout_in_ms){
+    uint32_t ticks = embedded_ticks_for_ms(timeout_in_ms);
+    if (ticks == 0) ticks++;
+    ts->timeout = system_ticks + ticks; 
+}
+#endif
+
+void embedded_init(void){
+
+    data_sources = NULL;
+
+#ifdef HAVE_TICK
+    timers = NULL;
+    system_ticks = 0;
+    hal_tick_init();
+    hal_tick_set_handler(&embedded_tick_handler);
+#endif
+}
+
+extern const run_loop_t run_loop_embedded;
+const run_loop_t run_loop_embedded = {
+    &embedded_init,
+    &embedded_add_data_source,
+    &embedded_remove_data_source,
+    &embedded_add_timer,
+    &embedded_remove_timer,
+    &embedded_execute,
+    &embedded_dump_timer
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/run_loop_private.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  run_loop_private.h
+ *
+ *  Created by Matthias Ringwald on 6/6/09.
+ */
+
+#pragma once
+
+#include <btstack/run_loop.h>
+
+#ifdef HAVE_TIME
+#include <sys/time.h>
+
+// compare timeval or timers - NULL is assumed to be before the Big Bang
+int run_loop_timeval_compare(struct timeval *a, struct timeval *b);
+int run_loop_timer_compare(timer_source_t *a, timer_source_t *b);
+
+#endif
+
+// 
+void run_loop_timer_dump(void);
+
+// internal use only
+typedef struct {
+    void (*init)(void);
+    void (*add_data_source)(data_source_t *dataSource);
+    int  (*remove_data_source)(data_source_t *dataSource);
+    void (*add_timer)(timer_source_t *timer);
+    int  (*remove_timer)(timer_source_t *timer); 
+    void (*execute)(void);
+    void (*dump_timer)(void);
+} run_loop_t;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/sdp.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+#pragma once
+
+#include <stdint.h>
+#include <btstack/linked_list.h>
+
+#include "config.h"
+
+typedef enum {
+    SDP_ErrorResponse = 1,
+    SDP_ServiceSearchRequest,
+    SDP_ServiceSearchResponse,
+    SDP_ServiceAttributeRequest,
+    SDP_ServiceAttributeResponse,
+    SDP_ServiceSearchAttributeRequest,
+    SDP_ServiceSearchAttributeResponse
+} SDP_PDU_ID_t;
+
+// service record
+// -- uses user_data field for actual
+typedef struct {
+    // linked list - assert: first field
+    linked_item_t   item;
+    
+    // client connection
+    void *  connection;
+    
+    // data is contained in same memory
+    uint32_t        service_record_handle;
+    uint8_t         service_record[0];
+} service_record_item_t;
+
+
+void sdp_init(void);
+
+void sdp_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type,
+                                                 uint16_t channel, uint8_t *packet, uint16_t size));
+
+#ifdef EMBEDDED
+// register service record internally - the normal version creates a copy of the record
+// pre: AttributeIDs are in ascending order => ServiceRecordHandle is first attribute if present
+// @returns ServiceRecordHandle or 0 if registration failed
+uint32_t sdp_register_service_internal(void *connection, service_record_item_t * record_item);
+#else
+// register service record internally - this special version doesn't copy the record, it cannot be freeed
+// pre: AttributeIDs are in ascending order
+// pre: ServiceRecordHandle is first attribute and valid
+// pre: record
+// @returns ServiceRecordHandle or 0 if registration failed
+uint32_t sdp_register_service_internal(void *connection, uint8_t * service_record);
+#endif
+
+// unregister service record internally
+void sdp_unregister_service_internal(void *connection, uint32_t service_record_handle);
+
+//
+void sdp_unregister_services_for_connection(void *connection);
+
+//
+int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu);
+int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu);
+int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/sdp_util.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,698 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  sdp_util.c
+ */
+
+#include <btstack/sdp_util.h>
+#include <btstack/utils.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>   // PRIx32
+
+// workaround for missing PRIx32 on mspgcc (16-bit MCU)
+#ifndef PRIx32
+#warning Using own: #define PRIx32 "lx"
+#define PRIx32 "lx"
+#endif
+
+// date element type names
+const char * const type_names[] = { "NIL", "UINT", "INT", "UUID", "STRING", "BOOL", "DES", "DEA", "URL"};
+
+// Bluetooth Base UUID: 00000000-0000-1000-8000- 00805F9B34FB
+const uint8_t sdp_bluetooth_base_uuid[] = { 0x00, 0x00, 0x00, 0x00, /* - */ 0x00, 0x00, /* - */ 0x10, 0x00, /* - */
+    0x80, 0x00, /* - */ 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
+
+void sdp_normalize_uuid(uint8_t *uuid, uint32_t shortUUID){
+    memcpy(uuid, sdp_bluetooth_base_uuid, 16);
+    net_store_32(uuid, 0, shortUUID);
+}
+
+// MARK: DataElement getter
+de_size_t de_get_size_type(uint8_t *header){
+    return (de_size_t) (header[0] & 7);
+}
+
+de_type_t de_get_element_type(uint8_t *header){
+    return (de_type_t) (header[0] >> 3);
+}
+
+int de_get_header_size(uint8_t * header){
+    de_size_t de_size = de_get_size_type(header);
+    if (de_size <= DE_SIZE_128) {
+        return 1;
+    }
+    return 1 + (1 << (de_size-DE_SIZE_VAR_8));
+}
+
+int de_get_data_size(uint8_t * header){
+    uint32_t result = 0;
+    de_type_t de_type = de_get_element_type(header);
+    de_size_t de_size = de_get_size_type(header);
+    switch (de_size){
+        case DE_SIZE_VAR_8:
+            result = header[1];
+            break;
+        case DE_SIZE_VAR_16:
+            result = READ_NET_16(header,1);
+            break;
+        case DE_SIZE_VAR_32:
+            result = READ_NET_32(header,1);
+            break;
+        default:
+        // case DE_SIZE_8:
+        // case DE_SIZE_16:
+        // case DE_SIZE_32:
+        // case DE_SIZE_64:
+        // case DE_SIZE_128:
+            if (de_type == DE_NIL) return 0;
+            return 1 << de_size;
+    }
+    return result;    
+}
+
+int de_get_len(uint8_t *header){
+    return de_get_header_size(header) + de_get_data_size(header); 
+}
+
+// @returns: element is valid UUID
+int de_get_normalized_uuid(uint8_t *uuid128, uint8_t *element){
+    de_type_t uuidType = de_get_element_type(element);
+    de_size_t uuidSize = de_get_size_type(element);
+    if (uuidType != DE_UUID) return 0;
+    uint32_t shortUUID;
+    switch (uuidSize){
+        case DE_SIZE_16:
+            shortUUID = READ_NET_16(element, 1);
+            break;
+        case DE_SIZE_32:
+            shortUUID = READ_NET_32(element, 1);
+            break;
+        case DE_SIZE_128:
+            memcpy(uuid128, element+1, 16);
+            return 1;
+        default:
+            return 0;
+    }
+    sdp_normalize_uuid(uuid128, shortUUID);
+    return 1;
+}
+
+// functions to create record
+static void de_store_descriptor(uint8_t * header, de_type_t type, de_size_t size){
+    header[0] = (type << 3) | size; 
+}
+
+void de_store_descriptor_with_len(uint8_t * header, de_type_t type, de_size_t size, uint32_t len){
+    header[0] = (type << 3) | size; 
+    switch (size){
+        case DE_SIZE_VAR_8:
+            header[1] = len;
+            break;
+        case DE_SIZE_VAR_16:
+            net_store_16(header, 1, len);
+            break;
+        case DE_SIZE_VAR_32:
+            net_store_32(header, 1, len);
+            break;
+        default:
+            break;
+    }
+}
+
+// MARK: DataElement creation
+
+/* starts a new sequence in empty buffer - first call */
+void de_create_sequence(uint8_t *header){
+    de_store_descriptor_with_len( header, DE_DES, DE_SIZE_VAR_16, 0); // DES, 2 Byte Length
+};
+
+/* starts a sub-sequence, @returns handle for sub-sequence */
+uint8_t * de_push_sequence(uint8_t *header){
+    int element_len = de_get_len(header);
+    de_store_descriptor_with_len(header+element_len, DE_DES, DE_SIZE_VAR_16, 0); // DES, 2 Byte Length
+    return header + element_len;
+}
+
+/* closes the current sequence and updates the parent sequence */
+void de_pop_sequence(uint8_t * parent, uint8_t * child){
+    int child_len = de_get_len(child);
+    int data_size_parent = READ_NET_16(parent,1);
+    net_store_16(parent, 1, data_size_parent + child_len);
+}
+
+/* adds a single number value and 16+32 bit UUID to the sequence */
+void de_add_number(uint8_t *seq, de_type_t type, de_size_t size, uint32_t value){
+    int data_size   = READ_NET_16(seq,1);
+    int element_size = 1;   // e.g. for DE_TYPE_NIL
+    de_store_descriptor(seq+3+data_size, type, size); 
+    switch (size){
+        case DE_SIZE_8:
+            if (type != DE_NIL){
+                seq[4+data_size] = value;
+                element_size = 2;
+            }
+            break;
+        case DE_SIZE_16:
+            net_store_16(seq, 4+data_size, value);
+            element_size = 3;
+            break;
+        case DE_SIZE_32:
+            net_store_32(seq, 4+data_size, value);
+            element_size = 5;
+            break;
+        default:
+            break;
+    }
+    net_store_16(seq, 1, data_size+element_size);
+}
+
+/* add a single block of data, e.g. as DE_STRING, DE_URL */
+void de_add_data( uint8_t *seq, de_type_t type, uint16_t size, uint8_t *data){
+    int data_size   = READ_NET_16(seq,1);
+    if (size > 0xff) {
+        // use 16-bit lengh information (3 byte header)
+        de_store_descriptor_with_len(seq+3+data_size, type, DE_SIZE_VAR_16, size); 
+        data_size += 3;
+    } else {
+        // use 8-bit lengh information (2 byte header)
+        de_store_descriptor_with_len(seq+3+data_size, type, DE_SIZE_VAR_8, size); 
+        data_size += 2;
+    }
+    memcpy( seq + 3 + data_size, data, size);
+    data_size += size;
+    net_store_16(seq, 1, data_size);
+}
+
+void de_add_uuid128(uint8_t * seq, uint8_t * uuid){
+    int data_size   = READ_NET_16(seq,1);
+    de_store_descriptor(seq+3+data_size, DE_UUID, DE_SIZE_128); 
+    memcpy( seq + 4 + data_size, uuid, 16);
+    net_store_16(seq, 1, data_size+1+16);
+}
+
+void sdp_add_attribute(uint8_t *seq, uint16_t attributeID, uint8_t attributeValue){
+}
+
+// MARK: DataElementSequence traversal
+typedef int (*de_traversal_callback_t)(uint8_t * element, de_type_t type, de_size_t size, void *context);
+static void de_traverse_sequence(uint8_t * element, de_traversal_callback_t handler, void *context){
+    de_type_t type = de_get_element_type(element);
+    if (type != DE_DES) return;
+    int pos = de_get_header_size(element);
+    int end_pos = de_get_len(element);
+    while (pos < end_pos){
+        de_type_t elemType = de_get_element_type(element + pos);
+        de_size_t elemSize = de_get_size_type(element + pos);
+        uint8_t done = (*handler)(element + pos, elemType, elemSize, context); 
+        if (done) break;
+        pos += de_get_len(element + pos);
+    }
+}
+
+// MARK: AttributeList traversal
+typedef int (*sdp_attribute_list_traversal_callback_t)(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *context);
+static void sdp_attribute_list_traverse_sequence(uint8_t * element, sdp_attribute_list_traversal_callback_t handler, void *context){
+    de_type_t type = de_get_element_type(element);
+    if (type != DE_DES) return;
+    int pos = de_get_header_size(element);
+    int end_pos = de_get_len(element);
+    while (pos < end_pos){
+        de_type_t idType = de_get_element_type(element + pos);
+        de_size_t idSize = de_get_size_type(element + pos);
+        if (idType != DE_UINT || idSize != DE_SIZE_16) break; // wrong type
+        uint16_t attribute_id = READ_NET_16(element, pos + 1);
+        pos += 3;
+        if (pos >= end_pos) break; // array out of bounds
+        de_type_t valueType = de_get_element_type(element + pos);
+        de_size_t valueSize = de_get_size_type(element + pos);
+        uint8_t done = (*handler)(attribute_id, element + pos, valueType, valueSize, context); 
+        if (done) break;
+        pos += de_get_len(element + pos);
+    }
+}
+
+// MARK: AttributeID in AttributeIDList 
+// attribute ID in AttributeIDList
+// context { result, attributeID }
+struct sdp_context_attributeID_search {
+    int result;
+    uint16_t attributeID;
+};
+static int sdp_traversal_attributeID_search(uint8_t * element, de_type_t type, de_size_t size, void *my_context){
+    struct sdp_context_attributeID_search * context = (struct sdp_context_attributeID_search *) my_context;
+    if (type != DE_UINT) return 0;
+    switch (size) {
+        case DE_SIZE_16:
+            if (READ_NET_16(element, 1) == context->attributeID) {
+                context->result = 1;
+                return 1;
+            }
+            break;
+        case DE_SIZE_32:
+            if (READ_NET_16(element, 1) <= context->attributeID
+            &&  context->attributeID <= READ_NET_16(element, 3)) {
+                context->result = 1;
+                return 1;
+            }
+            break;
+        default:
+            break;
+    }
+    return 0;
+}
+int sdp_attribute_list_constains_id(uint8_t *attributeIDList, uint16_t attributeID){
+    struct sdp_context_attributeID_search attributeID_search;
+    attributeID_search.result = 0;
+    attributeID_search.attributeID = attributeID;
+    de_traverse_sequence(attributeIDList, sdp_traversal_attributeID_search, &attributeID_search);
+    return attributeID_search.result;
+}
+
+// MARK: Append Attributes for AttributeIDList
+// pre: buffer contains DES with 2 byte length field
+struct sdp_context_append_attributes {
+    uint8_t * buffer;
+    uint16_t startOffset;     // offset of when to start copying
+    uint16_t maxBytes;
+    uint16_t usedBytes;
+    uint8_t *attributeIDList;
+};
+
+static int sdp_traversal_append_attributes(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){
+    struct sdp_context_append_attributes * context = (struct sdp_context_append_attributes *) my_context;
+    if (sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) {
+        // DES_HEADER(3) + DES_DATA + (UINT16(3) + attribute)
+        uint16_t data_size = READ_NET_16(context->buffer, 1);
+        int attribute_len = de_get_len(attributeValue);
+        if (3 + data_size + (3 + attribute_len) <= context->maxBytes) {
+            // copy Attribute
+            de_add_number(context->buffer, DE_UINT, DE_SIZE_16, attributeID);   
+            data_size += 3; // 3 bytes
+            memcpy(context->buffer + 3 + data_size, attributeValue, attribute_len);
+            net_store_16(context->buffer,1,data_size+attribute_len);
+        } else {
+            // not enought space left -> continue with previous element
+            return 1;
+        }
+    }
+    return 0;
+}
+
+// maxBytes: maximal size of data element sequence
+uint16_t sdp_append_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint8_t *buffer){
+    struct sdp_context_append_attributes context;
+    context.buffer = buffer;
+    context.maxBytes = maxBytes;
+    context.usedBytes = 0;
+    context.startOffset = startOffset;
+    context.attributeIDList = attributeIDList;
+    sdp_attribute_list_traverse_sequence(record, sdp_traversal_append_attributes, &context);
+    return context.usedBytes;
+}
+
+// MARK: Filter attributes that match attribute list from startOffset and a max nr bytes
+struct sdp_context_filter_attributes {
+    uint8_t * buffer;
+    uint16_t startOffset;     // offset of when to start copying
+    uint16_t maxBytes;
+    uint16_t usedBytes;
+    uint8_t *attributeIDList;
+    int      complete;
+};
+
+// copy data with given start offset and max bytes, returns OK if all data has been copied
+static int spd_append_range(struct sdp_context_filter_attributes* context, uint16_t len, uint8_t *data){
+    int ok = 1;
+    uint16_t remainder_len = len - context->startOffset;
+    if (context->maxBytes < remainder_len){
+        remainder_len = context->maxBytes;
+        ok = 0;
+    }
+    memcpy(context->buffer, &data[context->startOffset], remainder_len);
+    context->usedBytes += remainder_len;
+    context->buffer    += remainder_len;
+    context->maxBytes  -= remainder_len;
+    context->startOffset = 0;
+    return ok;
+}
+
+static int sdp_traversal_filter_attributes(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){
+    struct sdp_context_filter_attributes * context = (struct sdp_context_filter_attributes *) my_context;
+
+    if (!sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) return 0;
+
+    // { Attribute ID (Descriptor, big endian 16-bit ID), AttributeValue (data)}
+
+    // handle Attribute ID
+    if (context->startOffset >= 3){
+        context->startOffset -= 3;
+    } else {
+        uint8_t idBuffer[3];
+        de_store_descriptor(idBuffer, DE_UINT,  DE_SIZE_16);
+        net_store_16(idBuffer,1,attributeID);
+        
+        int ok = spd_append_range(context, 3, idBuffer);
+        if (!ok) {
+            context->complete = 0;
+            return 1;
+        }
+    }
+    
+    // handle Attribute Value
+    int attribute_len = de_get_len(attributeValue);
+    if (context->startOffset >= attribute_len) {
+        context->startOffset -= attribute_len;
+        return 0;
+    }
+    
+    int ok = spd_append_range(context, attribute_len, attributeValue);
+    if (!ok) {
+        context->complete = 0;
+        return 1;
+    }
+    return 0;
+}
+
+int sdp_filter_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint16_t *usedBytes, uint8_t *buffer){
+
+    struct sdp_context_filter_attributes context;
+    context.buffer = buffer;
+    context.maxBytes = maxBytes;
+    context.usedBytes = 0;
+    context.startOffset = startOffset;
+    context.attributeIDList = attributeIDList;
+    context.complete = 1;
+
+    sdp_attribute_list_traverse_sequence(record, sdp_traversal_filter_attributes, &context);
+
+    *usedBytes = context.usedBytes;
+    return context.complete;
+}
+
+// MARK: Get sum of attributes matching attribute list
+struct sdp_context_get_filtered_size {
+    uint8_t *attributeIDList;
+    uint16_t size;
+};
+
+static int sdp_traversal_get_filtered_size(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){
+    struct sdp_context_get_filtered_size * context = (struct sdp_context_get_filtered_size *) my_context;
+    if (sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) {
+        context->size += 3 + de_get_len(attributeValue);
+    }
+    return 0;
+}
+
+int spd_get_filtered_size(uint8_t *record, uint8_t *attributeIDList){
+    struct sdp_context_get_filtered_size context;
+    context.size = 0;
+    context.attributeIDList = attributeIDList;
+    sdp_attribute_list_traverse_sequence(record, sdp_traversal_get_filtered_size, &context);
+    return context.size;
+}
+
+// MARK: Get AttributeValue for AttributeID
+// find attribute (ELEMENT) by ID
+struct sdp_context_attribute_by_id {
+    uint16_t  attributeID;
+    uint8_t * attributeValue;
+};
+static int sdp_traversal_attribute_by_id(uint16_t attributeID, uint8_t * attributeValue, de_type_t attributeType, de_size_t size, void *my_context){
+    struct sdp_context_attribute_by_id * context = (struct sdp_context_attribute_by_id *) my_context;
+    if (attributeID == context->attributeID) {
+        context->attributeValue = attributeValue;
+        return 1;
+    }
+    return 0;
+}
+
+uint8_t * sdp_get_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID){
+    struct sdp_context_attribute_by_id context;
+    context.attributeValue = NULL;
+    context.attributeID = attributeID;
+    sdp_attribute_list_traverse_sequence(record, sdp_traversal_attribute_by_id, &context);
+    return context.attributeValue;
+}
+
+// MARK: Set AttributeValue for AttributeID
+struct sdp_context_set_attribute_for_id {
+    uint16_t  attributeID;
+    uint32_t  attributeValue;
+    uint8_t   attributeFound;
+};
+static int sdp_traversal_set_attribute_for_id(uint16_t attributeID, uint8_t * attributeValue, de_type_t attributeType, de_size_t size, void *my_context){
+    struct sdp_context_set_attribute_for_id * context = (struct sdp_context_set_attribute_for_id *) my_context;
+    if (attributeID == context->attributeID) {
+        context->attributeFound = 1;
+        switch (size){
+            case DE_SIZE_8:
+                if (attributeType != DE_NIL){
+                    attributeValue[1] = context->attributeValue;
+                }
+                break;
+            case DE_SIZE_16:
+                net_store_16(attributeValue, 1, context->attributeValue);
+                break;
+            case DE_SIZE_32:
+                net_store_32(attributeValue, 1, context->attributeValue);
+                break;
+                // Might want to support STRINGS to, copy upto original length
+            default:
+                break;
+        }        
+        return 1;
+    }
+    return 0;
+}
+uint8_t sdp_set_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID, uint32_t value){
+    struct sdp_context_set_attribute_for_id context;
+    context.attributeID = attributeID;
+    context.attributeValue = value;
+    context.attributeFound = 0;
+    sdp_attribute_list_traverse_sequence(record, sdp_traversal_set_attribute_for_id, &context);
+    return context.attributeFound;
+}
+
+// MARK: ServiceRecord contains UUID
+// service record contains UUID
+// context { normalizedUUID }
+struct sdp_context_contains_uuid128 {
+    uint8_t * uuid128;
+    int result;
+};
+int sdp_record_contains_UUID128(uint8_t *record, uint8_t *uuid128);
+static int sdp_traversal_contains_UUID128(uint8_t * element, de_type_t type, de_size_t size, void *my_context){
+    struct sdp_context_contains_uuid128 * context = (struct sdp_context_contains_uuid128 *) my_context;
+    uint8_t normalizedUUID[16];
+    if (type == DE_UUID){
+        uint8_t uuidOK = de_get_normalized_uuid(normalizedUUID, element);
+        context->result = uuidOK && memcmp(context->uuid128, normalizedUUID, 16) == 0;
+    }
+    if (type == DE_DES){
+        context->result = sdp_record_contains_UUID128(element, context->uuid128);
+    }
+    return context->result;
+}
+int sdp_record_contains_UUID128(uint8_t *record, uint8_t *uuid128){
+    struct sdp_context_contains_uuid128 context;
+    context.uuid128 = uuid128;
+    context.result = 0;
+    de_traverse_sequence(record, sdp_traversal_contains_UUID128, &context);
+    return context.result;
+}
+    
+// MARK: ServiceRecord matches SearchServicePattern
+// if UUID in searchServicePattern is not found in record => false
+// context { result, record }
+struct sdp_context_match_pattern {
+    uint8_t * record;
+    int result;
+};
+int sdp_traversal_match_pattern(uint8_t * element, de_type_t attributeType, de_size_t size, void *my_context){
+    struct sdp_context_match_pattern * context = (struct sdp_context_match_pattern *) my_context;
+    uint8_t normalizedUUID[16];
+    uint8_t uuidOK = de_get_normalized_uuid(normalizedUUID, element);
+    if (!uuidOK || !sdp_record_contains_UUID128(context->record, normalizedUUID)){
+        context->result = 0;
+        return 1;
+    }
+    return 0;
+}
+int sdp_record_matches_service_search_pattern(uint8_t *record, uint8_t *serviceSearchPattern){
+    struct sdp_context_match_pattern context;
+    context.record = record;
+    context.result = 1;
+    de_traverse_sequence(serviceSearchPattern, sdp_traversal_match_pattern, &context);
+    return context.result;
+}
+
+// MARK: Dump DataElement
+// context { indent }
+static int de_traversal_dump_data(uint8_t * element, de_type_t de_type, de_size_t de_size, void *my_context){
+    int indent = *(int*) my_context;
+    int i;
+    for (i=0; i<indent;i++) printf("    ");
+    int pos     = de_get_header_size(element);
+    int end_pos = de_get_len(element);
+    printf("type %5s (%u), element len %2u ", type_names[de_type], de_type, end_pos);
+    if (de_type == DE_DES) {
+        printf("\n");
+        indent++;
+        de_traverse_sequence(element, de_traversal_dump_data, (void *)&indent);
+    } else if (de_type == DE_UUID && de_size == DE_SIZE_128) {
+        printf(", value: ");
+        printUUID(element+1);
+        printf("\n");
+    } else if (de_type == DE_STRING) {
+        int len = 0;
+        switch (de_size){
+            case DE_SIZE_VAR_8:
+                len = element[1];
+                break;
+            case DE_SIZE_VAR_16:
+                len = READ_NET_16(element, 1);
+                break;
+            default:
+                break;
+        }
+        printf("len %u (0x%02x)\n", len, len);
+        hexdump(&element[pos], len);
+    } else {
+        uint32_t value = 0;
+        switch (de_size) {
+            case DE_SIZE_8:
+                if (de_type != DE_NIL){
+                    value = element[pos];
+                }
+                break;
+            case DE_SIZE_16:
+                value = READ_NET_16(element,pos);
+                break;
+            case DE_SIZE_32:
+                value = READ_NET_32(element,pos);
+                break;
+            default:
+                break;
+        }
+        printf(", value: 0x%08" PRIx32 "\n", value);
+    }
+    return 0;
+}
+
+void de_dump_data_element(uint8_t * record){
+    int indent = 0;
+    // hack to get root DES, too.
+    de_type_t type = de_get_element_type(record);
+    de_size_t size = de_get_size_type(record);
+    de_traversal_dump_data(record, type, size, (void*) &indent);
+}
+
+void sdp_create_spp_service(uint8_t *service, int service_id, const char *name){
+    
+    uint8_t* attribute;
+    de_create_sequence(service);
+    
+    // 0x0000 "Service Record Handle"
+    de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceRecordHandle);
+    de_add_number(service, DE_UINT, DE_SIZE_32, 0x10001);
+    
+    // 0x0001 "Service Class ID List"
+    de_add_number(service,  DE_UINT, DE_SIZE_16, SDP_ServiceClassIDList);
+    attribute = de_push_sequence(service);
+    {
+        de_add_number(attribute,  DE_UUID, DE_SIZE_16, 0x1101 );
+    }
+    de_pop_sequence(service, attribute);
+    
+    // 0x0004 "Protocol Descriptor List"
+    de_add_number(service,  DE_UINT, DE_SIZE_16, SDP_ProtocolDescriptorList);
+    attribute = de_push_sequence(service);
+    {
+        uint8_t* l2cpProtocol = de_push_sequence(attribute);
+        {
+            de_add_number(l2cpProtocol,  DE_UUID, DE_SIZE_16, 0x0100);
+        }
+        de_pop_sequence(attribute, l2cpProtocol);
+        
+        uint8_t* rfcomm = de_push_sequence(attribute);
+        {
+            de_add_number(rfcomm,  DE_UUID, DE_SIZE_16, 0x0003);  // rfcomm_service
+            de_add_number(rfcomm,  DE_UINT, DE_SIZE_8,  service_id);  // rfcomm channel
+        }
+        de_pop_sequence(attribute, rfcomm);
+    }
+    de_pop_sequence(service, attribute);
+    
+    // 0x0005 "Public Browse Group"
+    de_add_number(service,  DE_UINT, DE_SIZE_16, SDP_BrowseGroupList); // public browse group
+    attribute = de_push_sequence(service);
+    {
+        de_add_number(attribute,  DE_UUID, DE_SIZE_16, 0x1002 );
+    }
+    de_pop_sequence(service, attribute);
+    
+    // 0x0006
+    de_add_number(service,  DE_UINT, DE_SIZE_16, SDP_LanguageBaseAttributeIDList);
+    attribute = de_push_sequence(service);
+    {
+        de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x656e);
+        de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x006a);
+        de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x0100);
+    }
+    de_pop_sequence(service, attribute);
+    
+    // 0x0009 "Bluetooth Profile Descriptor List"
+    de_add_number(service,  DE_UINT, DE_SIZE_16, SDP_BluetoothProfileDescriptorList);
+    attribute = de_push_sequence(service);
+    {
+        uint8_t *sppProfile = de_push_sequence(attribute);
+        {
+            de_add_number(sppProfile,  DE_UUID, DE_SIZE_16, 0x1101);
+            de_add_number(sppProfile,  DE_UINT, DE_SIZE_16, 0x0100);
+        }
+        de_pop_sequence(attribute, sppProfile);
+    }
+    de_pop_sequence(service, attribute);
+    
+    // 0x0100 "ServiceName"
+    de_add_number(service,  DE_UINT, DE_SIZE_16, 0x0100);
+    de_add_data(service,  DE_STRING, strlen(name), (uint8_t *) name);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/sdp_util.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  sdp_util.h
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+    
+typedef enum {
+    DE_NIL = 0,
+    DE_UINT,
+    DE_INT,
+    DE_UUID,
+    DE_STRING,
+    DE_BOOL,
+    DE_DES,
+    DE_DEA,
+    DE_URL
+} de_type_t;
+
+typedef enum {
+    DE_SIZE_8 = 0,
+    DE_SIZE_16,
+    DE_SIZE_32,
+    DE_SIZE_64,
+    DE_SIZE_128,
+    DE_SIZE_VAR_8,
+    DE_SIZE_VAR_16,
+    DE_SIZE_VAR_32
+} de_size_t;
+
+// UNIVERSAL ATTRIBUTE DEFINITIONS
+#define SDP_ServiceRecordHandle     0x0000
+#define SDP_ServiceClassIDList      0x0001
+#define SDP_ServiceRecordState      0x0002
+#define SDP_ServiceID               0x0003
+#define SDP_ProtocolDescriptorList  0x0004
+#define SDP_BrowseGroupList            0x0005
+#define SDP_LanguageBaseAttributeIDList 0x0006
+#define SDP_ServiceInfoTimeToLive    0x0007
+#define SDP_ServiceAvailability        0x0008
+#define SDP_BluetoothProfileDescriptorList 0x0009
+#define SDP_DocumentationURL        0x000a
+#define SDP_ClientExecutableURL     0x000b
+#define SDP_IconURL                 0x000c
+#define SDP_AdditionalProtocolDescriptorList 0x000d
+#define SDP_SupportedFormatsList    0x0303
+
+// SERVICE CLASSES
+#define SDP_OBEXObjectPush    0x1105
+#define SDP_OBEXFileTransfer  0x1106
+#define SDP_PublicBrowseGroup 0x1002
+
+// PROTOCOLS
+#define SDP_SDPProtocol       0x0001
+#define SDP_UDPProtocol       0x0002
+#define SDP_RFCOMMProtocol    0x0003
+#define SDP_OBEXProtocol      0x0008
+#define SDP_L2CAPProtocol     0x0100
+
+// OFFSETS FOR LOCALIZED ATTRIBUTES - SDP_LanguageBaseAttributeIDList
+#define SDP_Offest_ServiceName      0x0000
+#define SDP_Offest_ServiceDescription 0x0001
+#define SDP_Offest_ProviderName     0x0002
+
+// OBEX
+#define SDP_vCard_2_1       0x01
+#define SDP_vCard_3_0       0x02
+#define SDP_vCal_1_0        0x03
+#define SDP_iCal_2_0        0x04
+#define SDP_vNote           0x05
+#define SDP_vMessage        0x06
+#define SDP_OBEXFileTypeAny 0xFF
+
+// MARK: DateElement
+void de_dump_data_element(uint8_t * record);
+int de_get_len(uint8_t *header);
+de_size_t de_get_size_type(uint8_t *header);
+de_type_t de_get_element_type(uint8_t *header);
+int de_get_header_size(uint8_t * header);
+void de_create_sequence(uint8_t *header);
+void de_store_descriptor_with_len(uint8_t * header, de_type_t type, de_size_t size, uint32_t len);
+uint8_t * de_push_sequence(uint8_t *header);
+void de_pop_sequence(uint8_t * parent, uint8_t * child);
+void de_add_number(uint8_t *seq, de_type_t type, de_size_t size, uint32_t value);
+void de_add_data( uint8_t *seq, de_type_t type, uint16_t size, uint8_t *data);
+
+int de_get_data_size(uint8_t * header);
+void de_add_uuid128(uint8_t * seq, uint8_t * uuid);
+
+// MARK: SDP
+uint16_t  sdp_append_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint8_t *buffer);
+uint8_t * sdp_get_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID);
+uint8_t   sdp_set_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID, uint32_t value);
+int       sdp_record_matches_service_search_pattern(uint8_t *record, uint8_t *serviceSearchPattern);
+int       spd_get_filtered_size(uint8_t *record, uint8_t *attributeIDList);
+int       sdp_filter_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint16_t *usedBytes, uint8_t *buffer);  
+
+void      sdp_create_spp_service(uint8_t *service, int service_id, const char *name);
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/utils.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2009-2012 by Matthias Ringwald
+ *
+ * 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.
+ * 4. Any redistribution, use, or modification is done solely for
+ *    personal benefit and not for any commercial purpose or for
+ *    monetary gain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ * Please inquire about commercial licensing options at btstack@ringwald.ch
+ *
+ */
+
+/*
+ *  utils.c
+ *
+ *  General utility functions
+ *
+ *  Created by Matthias Ringwald on 7/23/09.
+ */
+
+#include "config.h"
+#include <btstack/utils.h>
+#include <stdio.h>
+#include "debug.h"
+
+void bt_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){
+    buffer[pos++] = value;
+    buffer[pos++] = value >> 8;
+}
+
+void bt_store_32(uint8_t *buffer, uint16_t pos, uint32_t value){
+    buffer[pos++] = value;
+    buffer[pos++] = value >> 8;
+    buffer[pos++] = value >> 16;
+    buffer[pos++] = value >> 24;
+}
+
+void net_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){
+    buffer[pos++] = value >> 8;
+    buffer[pos++] = value;
+}
+
+void net_store_32(uint8_t *buffer, uint16_t pos, uint32_t value){
+    buffer[pos++] = value >> 24;
+    buffer[pos++] = value >> 16;
+    buffer[pos++] = value >> 8;
+    buffer[pos++] = value;
+}
+
+void bt_flip_addr(bd_addr_t dest, bd_addr_t src){
+    dest[0] = src[5];
+    dest[1] = src[4];
+    dest[2] = src[3];
+    dest[3] = src[2];
+    dest[4] = src[1];
+    dest[5] = src[0];
+}
+
+void hexdump(void *data, int size){
+    int i;
+    for (i=0; i<size;i++){
+        log_info("%02X ", ((uint8_t *)data)[i]);
+    }
+    log_info("\n");
+}
+
+void printUUID(uint8_t *uuid) {
+    log_info("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+           uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
+           uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
+}
+
+static char bd_addr_to_str_buffer[6*3];  // 12:45:78:01:34:67\0
+char * bd_addr_to_str(bd_addr_t addr){
+    sprintf(bd_addr_to_str_buffer, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+    return (char *) bd_addr_to_str_buffer;
+}
+
+void print_bd_addr( bd_addr_t addr){
+    log_info("%s", bd_addr_to_str(addr));
+}
+
+#ifndef EMBEDDED
+int sscan_bd_addr(uint8_t * addr_string, bd_addr_t addr){
+    unsigned int bd_addr_buffer[BD_ADDR_LEN];  //for sscanf, integer needed
+    // reset result buffer
+    int i;
+    for (i = 0; i < BD_ADDR_LEN; i++) {
+        bd_addr_buffer[i] = 0;
+    }
+    
+    // parse
+    int result = sscanf( (char *) addr_string, "%2x:%2x:%2x:%2x:%2x:%2x", &bd_addr_buffer[0], &bd_addr_buffer[1], &bd_addr_buffer[2],
+                        &bd_addr_buffer[3], &bd_addr_buffer[4], &bd_addr_buffer[5]);
+    // store
+    if (result == 6){
+        for (i = 0; i < BD_ADDR_LEN; i++) {
+            addr[i] = (uint8_t) bd_addr_buffer[i];
+        }
+    }
+    return (result == 6);
+}
+#endif
+
+/*  
+ * CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0.
+ */
+static const uint8_t crc8table[256] = {    /* reversed, 8-bit, poly=0x07 */
+    0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
+    0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
+    0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
+    0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
+    0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
+    0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
+    0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
+    0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
+    0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
+    0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
+    0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
+    0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
+    0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
+    0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
+    0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
+    0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
+};
+
+#define CRC8_INIT  0xFF          // Initial FCS value 
+#define CRC8_OK    0xCF          // Good final FCS value 
+/*-----------------------------------------------------------------------------------*/
+uint8_t crc8(uint8_t *data, uint16_t len)
+{
+    uint16_t count;
+    uint8_t crc = CRC8_INIT;
+    for (count = 0; count < len; count++)
+        crc = crc8table[crc ^ data[count]];
+    return crc;
+}
+
+/*-----------------------------------------------------------------------------------*/
+uint8_t crc8_check(uint8_t *data, uint16_t len, uint8_t check_sum)
+{
+    uint8_t crc;
+    
+    crc = crc8(data, len);
+    
+    crc = crc8table[crc ^ check_sum];
+    if (crc == CRC8_OK) 
+        return 0;               /* Valid */
+    else 
+        return 1;               /* Failed */
+    
+}
+
+/*-----------------------------------------------------------------------------------*/
+uint8_t crc8_calc(uint8_t *data, uint16_t len)
+{
+    /* Ones complement */
+    return 0xFF - crc8(data, len);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/btstack/utils.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2009 by Matthias Ringwald
+ *
+ * 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 MATTHIAS RINGWALD 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 MATTHIAS
+ * RINGWALD 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.
+ *
+ */
+
+/*
+ *  utils.h
+ *
+ *  General utility functions
+ *
+ *  Created by Matthias Ringwald on 7/23/09.
+ */
+
+#pragma once
+
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/**
+ * @brief hci connection handle type
+ */
+typedef uint16_t hci_con_handle_t;
+
+/**
+ * @brief Length of a bluetooth device address.
+ */
+#define BD_ADDR_LEN 6
+typedef uint8_t bd_addr_t[BD_ADDR_LEN];
+
+/**
+ * @brief The link key type
+ */
+#define LINK_KEY_LEN 16
+typedef uint8_t link_key_t[LINK_KEY_LEN]; 
+
+/**
+ * @brief The device name type
+ */
+#define DEVICE_NAME_LEN 248
+typedef uint8_t device_name_t[DEVICE_NAME_LEN+1]; 
+    
+    
+// helper for BT little endian format
+#define READ_BT_16( buffer, pos) ( ((uint16_t) buffer[pos]) | (((uint16_t)buffer[pos+1]) << 8))
+#define READ_BT_24( buffer, pos) ( ((uint32_t) buffer[pos]) | (((uint32_t)buffer[pos+1]) << 8) | (((uint32_t)buffer[pos+2]) << 16))
+#define READ_BT_32( buffer, pos) ( ((uint32_t) buffer[pos]) | (((uint32_t)buffer[pos+1]) << 8) | (((uint32_t)buffer[pos+2]) << 16) | (((uint32_t) buffer[pos+3])) << 24)
+
+// helper for SDP big endian format
+#define READ_NET_16( buffer, pos) ( ((uint16_t) buffer[pos+1]) | (((uint16_t)buffer[pos  ]) << 8))
+#define READ_NET_32( buffer, pos) ( ((uint32_t) buffer[pos+3]) | (((uint32_t)buffer[pos+2]) << 8) | (((uint32_t)buffer[pos+1]) << 16) | (((uint32_t) buffer[pos])) << 24)
+
+// HCI CMD OGF/OCF
+#define READ_CMD_OGF(buffer) (buffer[1] >> 2)
+#define READ_CMD_OCF(buffer) ((buffer[1] & 0x03) << 8 | buffer[0])
+
+// check if command complete event for given command
+#define COMMAND_COMPLETE_EVENT(event,cmd) ( event[0] == HCI_EVENT_COMMAND_COMPLETE && READ_BT_16(event,3) == cmd.opcode)
+#define COMMAND_STATUS_EVENT(event,cmd) ( event[0] == HCI_EVENT_COMMAND_STATUS && READ_BT_16(event,4) == cmd.opcode)
+
+// Code+Len=2, Pkts+Opcode=3; total=5
+#define OFFSET_OF_DATA_IN_COMMAND_COMPLETE 5
+
+// ACL Packet
+#define READ_ACL_CONNECTION_HANDLE( buffer ) ( READ_BT_16(buffer,0) & 0x0fff)
+#define READ_ACL_FLAGS( buffer )      ( buffer[1] >> 4 )
+#define READ_ACL_LENGTH( buffer )     (READ_BT_16(buffer, 2))
+
+// L2CAP Packet
+#define READ_L2CAP_LENGTH(buffer)     ( READ_BT_16(buffer, 4))
+#define READ_L2CAP_CHANNEL_ID(buffer) ( READ_BT_16(buffer, 6))
+
+void bt_store_16(uint8_t *buffer, uint16_t pos, uint16_t value);
+void bt_store_32(uint8_t *buffer, uint16_t pos, uint32_t value);
+void bt_flip_addr(bd_addr_t dest, bd_addr_t src);
+
+void net_store_16(uint8_t *buffer, uint16_t pos, uint16_t value);
+void net_store_32(uint8_t *buffer, uint16_t pos, uint32_t value);
+
+void hexdump(void *data, int size);
+void printUUID(uint8_t *uuid);
+
+// @deprecated please use more convenient bd_addr_to_str
+void print_bd_addr( bd_addr_t addr);
+char * bd_addr_to_str(bd_addr_t addr);
+
+int sscan_bd_addr(uint8_t * addr_string, bd_addr_t addr);
+    
+uint8_t crc8_check(uint8_t *data, uint16_t len, uint8_t check_sum);
+uint8_t crc8_calc(uint8_t *data, uint16_t len);
+
+#define BD_ADDR_CMP(a,b) memcmp(a,b, BD_ADDR_LEN)
+#define BD_ADDR_COPY(dest,src) memcpy(dest,src,BD_ADDR_LEN)
+
+#if defined __cplusplus
+}
+#endif
+        
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,271 @@
+#include "mbed.h"
+#include <btstack/hci_cmds.h>
+#include <btstack/run_loop.h>
+#include <btstack/sdp_util.h>
+#include "hci.h"
+#include "l2cap.h"
+#include "btstack_memory.h"
+#include "remote_device_db.h"
+#include "rfcomm.h"
+#include "sdp.h"
+#include "config.h"
+#include "debug.h"
+#include "bd_addr.h"  // class bd_addr
+#include "att.h"
+
+#include "TB6612.h"
+#include "HighSpeedAnalogIn.h"
+
+Serial pc(USBTX, USBRX);
+DigitalOut led[] = { (LED1), (LED2), (LED3), (LED4) };
+DigitalIn sw[] = { (p29),(p30) };
+HighSpeedAnalogIn ain(p15, p16, p17, p18);
+TB6612 right(p21,p12,p11);
+TB6612 left(p22,p14,p13);
+
+static att_connection_t att_connection;
+static uint16_t         att_response_handle = 0;
+static uint16_t         att_response_size   = 0;
+static uint8_t          att_response_buffer[28];
+
+uint8_t    connection_status=0;
+
+static void att_try_respond(void){
+    if (!att_response_size) return;
+    if (!att_response_handle) return;
+    if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)) return;
+    
+    // update state before sending packet
+    uint16_t size = att_response_size;
+    att_response_size = 0;
+    l2cap_send_connectionless(att_response_handle, L2CAP_CID_ATTRIBUTE_PROTOCOL, att_response_buffer, size);
+}
+
+
+static void att_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size){
+    if (packet_type != ATT_DATA_PACKET) return;
+    
+    att_response_handle = handle;
+    att_response_size = att_handle_request(&att_connection, packet, size, att_response_buffer);
+    att_try_respond();
+}
+
+// enable LE, setup ADV data
+static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
+    static bd_addr_t addr;
+//    uint8_t adv_data[] = { 02, 01, 05,   03, 02, 0xf0, 0xff }; 
+    const uint8_t adv_data[31]="\x02\x01\x05" "\x06\x09mbed";
+    switch (packet_type) {
+            
+        case HCI_EVENT_PACKET:
+            switch (packet[0]) {
+                
+                case BTSTACK_EVENT_STATE:
+                    // bt stack activated, get started - set local name
+                    if (packet[2] == HCI_STATE_WORKING) {
+                       log_info("Working!\n");
+                        hci_send_cmd(&hci_read_local_supported_features);
+                    }
+                    break;
+                
+                case DAEMON_EVENT_HCI_PACKET_SENT:
+                    att_try_respond();
+                    break;
+                    
+                case HCI_EVENT_LE_META:
+                    switch (packet[2]) {
+                        case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
+                            // reset connection MTU
+                            att_connection.mtu = 23;
+                            break;
+                        default:
+                            break;
+                    }
+                    break;
+
+                case BTSTACK_EVENT_NR_CONNECTIONS_CHANGED:
+                    if (packet[2]) {
+                        connection_status=1;
+                        log_info("CONNECTED\n");
+                        led[0] = 1;
+                    } else {
+                        connection_status=0;
+                        log_info("NOT CONNECTED\n");
+                        led[0] = 0;
+                        right = 0;
+                        left = 0;
+                    }
+                    break;
+                    
+                case HCI_EVENT_DISCONNECTION_COMPLETE:
+                    att_response_handle =0;
+                    att_response_size = 0;
+                    
+                    // restart advertising
+                    hci_send_cmd(&hci_le_set_advertise_enable, 1);
+                    break;
+                    
+                case HCI_EVENT_COMMAND_COMPLETE:
+                    if (COMMAND_COMPLETE_EVENT(packet, hci_read_bd_addr)){
+                        bt_flip_addr(addr, &packet[6]);
+                        log_info("BD ADDR: %s\n", bd_addr_to_str(addr));
+                        break;
+                    }
+                    if (COMMAND_COMPLETE_EVENT(packet, hci_read_local_supported_features)){
+                        log_info("Local supported features: %04lX%04lX\n", READ_BT_32(packet, 10), READ_BT_32(packet, 6));
+                        hci_send_cmd(&hci_set_event_mask, 0xffffffff, 0x20001fff);
+                        break;
+                    }
+                    if (COMMAND_COMPLETE_EVENT(packet, hci_set_event_mask)){
+                        hci_send_cmd(&hci_write_le_host_supported, 1, 1);
+                        break;
+                    }
+                    if (COMMAND_COMPLETE_EVENT(packet, hci_write_le_host_supported)){
+                        hci_send_cmd(&hci_le_set_event_mask, 0xffffffff, 0xffffffff);
+                        break;
+                    }
+                    if (COMMAND_COMPLETE_EVENT(packet, hci_le_set_event_mask)){
+                        hci_send_cmd(&hci_le_read_buffer_size);
+                        break;
+                    }
+                    if (COMMAND_COMPLETE_EVENT(packet, hci_le_read_buffer_size)){
+                        log_info("LE buffer size: %u, count %u\n", READ_BT_16(packet,6), packet[8]);
+                       hci_send_cmd(&hci_le_read_supported_states);
+                       break;
+                    }
+                    if (COMMAND_COMPLETE_EVENT(packet, hci_le_read_supported_states)){
+                       hci_send_cmd(&hci_le_set_advertising_parameters,  0x0400, 0x0800, 0, 0, 0, &addr, 0x07, 0);
+                       break;
+                    }
+                    if (COMMAND_COMPLETE_EVENT(packet, hci_le_set_advertising_parameters)){
+                       hci_send_cmd(&hci_le_set_advertising_data, sizeof(adv_data), adv_data);
+                       break;
+                    }
+                    if (COMMAND_COMPLETE_EVENT(packet, hci_le_set_advertising_data)){
+                       hci_send_cmd(&hci_le_set_scan_response_data, 10, adv_data);
+                       break;
+                    }
+                    if (COMMAND_COMPLETE_EVENT(packet, hci_le_set_scan_response_data)){
+                       hci_send_cmd(&hci_le_set_advertise_enable, 1);
+                       break;
+                    }
+                    if (COMMAND_COMPLETE_EVENT(packet, hci_le_set_advertise_enable)){
+                        hci_discoverable_control(1);
+                        log_info("startup_state=1\n");
+                        //startup_state=1;
+                        led[1] = 0;
+                        break;
+                    }
+                    
+            }
+    }
+}
+
+// test profile
+#include "profile.h"
+
+static char    strbuf[80];
+static uint8_t    ledvalue;
+
+// read requests
+static uint16_t att_read_callback(uint16_t handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
+    uint16_t    ret=0,val;
+
+    if(buffer){
+     //   log_info("READ Callback, handle %04x\n", handle);
+    }
+    switch(handle){
+       case 0x000b:
+            ret=strlen((char*)strbuf);
+            if(buffer && ret<=buffer_size){
+                log_info("Read text: %s\n", strbuf);
+                memcpy(buffer,strbuf,ret);
+            }
+            break;
+        case 0x000d:
+            if(buffer && buffer_size>=8){
+                unsigned short dat[4];
+                dat[0] = ain.read_u16(p15);
+                dat[1] = ain.read_u16(p16);
+                dat[2] = ain.read_u16(p17);
+                dat[3] = ain.read_u16(p18);
+                memcpy( buffer , dat , 8 );
+            }
+            ret = 8;
+            break;
+        case 0x000f:
+            if(buffer && buffer_size>=1){
+             //   log_info("Read value: %u\n", val);
+                buffer[0]=(!sw[0])|((!sw[1])<<1);
+            }
+            ret=1;
+            break;
+    }
+    return ret;
+}
+
+// write requests
+static void att_write_callback(uint16_t handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size, signature_t * signature){
+ //   log_info("WRITE Callback, handle %04x\n", handle);
+    
+    switch(handle){
+        case 0x000b:
+            signed char r = buffer[0];
+            signed char l = buffer[1];
+            
+            right = r;
+            left = l;
+
+            break;
+        case 0x000d:
+          //  log_info("New value: %u\n", buffer[0]);
+            ledvalue=buffer[0];
+            if (buffer[0]){
+                led[1] = 1;
+            } else {
+                led[1] = 0;
+            }
+            break;
+    }
+}
+
+// main
+int main(void)
+{
+    sw[0].mode(PullUp);
+    sw[1].mode(PullUp);
+    
+    pc.baud(921600);
+    log_info("%s\n", __FILE__);
+    
+    led[0] = 0;
+    
+    /// GET STARTED with BTstack ///
+    btstack_memory_init();
+    run_loop_init(RUN_LOOP_EMBEDDED);
+    
+    // init HCI
+    hci_transport_t* transport = hci_transport_usb_instance();
+    remote_device_db_t * remote_db = (remote_device_db_t *) &remote_device_db_memory;
+    hci_init(transport, NULL, NULL, remote_db);
+    
+    // init L2CAP
+    l2cap_init();
+    l2cap_register_fixed_channel(att_packet_handler, L2CAP_CID_ATTRIBUTE_PROTOCOL);
+    l2cap_register_packet_handler(packet_handler);
+    
+    // set up ATT
+    att_set_db(profile_data);
+    att_set_write_callback(att_write_callback);
+    att_set_read_callback(att_read_callback);
+    att_dump_attributes();
+    att_connection.mtu = 27;
+
+     // turn on!
+    hci_power_control(HCI_POWER_ON);
+
+    // go!
+    run_loop_execute();    
+
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/737756e0b479
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/profile.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,43 @@
+
+// profile.h generated from profile.gatt for BTstack
+
+// binary representation
+// attribute size in bytes (16), flags(16), handle (16), uuid (16/128), value(...)
+
+#include <stdint.h>
+
+const uint8_t profile_data[] =
+{
+    // 0x0001 PRIMARY_SERVICE-GAP_SERVICE
+    0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28, 0x00, 0x18, 
+    // 0x0002 CHARACTERISTIC-GAP_DEVICE_NAME-READ
+    0x0d, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x28, 0x02, 0x03, 0x00, 0x00, 0x2a, 
+    // 0x0003 VALUE-GAP_DEVICE_NAME-READ-"WALLBOT BLE1.0"
+    0x16, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x2a, 0x57, 0x41, 0x4c, 0x4c, 0x42, 0x4f, 0x54, 0x20, 0x42, 0x4c, 0x45, 0x31, 0x2e, 0x30, 
+    // 0x0004 CHARACTERISTIC-GAP_APPEARANCE-READ
+    0x0d, 0x00, 0x02, 0x00, 0x04, 0x00, 0x03, 0x28, 0x02, 0x05, 0x00, 0x01, 0x2a, 
+    // 0x0005 VALUE-GAP_APPEARANCE-READ-00 00
+    0x0a, 0x00, 0x02, 0x00, 0x05, 0x00, 0x01, 0x2a, 0x00, 0x00, 
+    // 0x0006 PRIMARY_SERVICE-GATT_SERVICE
+    0x0a, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, 0x28, 0x01, 0x18, 
+    // 0x0007 CHARACTERISTIC-GATT_SERVICE_CHANGED-READ
+    0x0d, 0x00, 0x02, 0x00, 0x07, 0x00, 0x03, 0x28, 0x02, 0x08, 0x00, 0x05, 0x2a, 
+    // 0x0008 VALUE-GATT_SERVICE_CHANGED-READ-
+    0x08, 0x00, 0x02, 0x00, 0x08, 0x00, 0x05, 0x2a, 
+    // 0x0009 PRIMARY_SERVICE-FFF0
+    0x0a, 0x00, 0x02, 0x00, 0x09, 0x00, 0x00, 0x28, 0xf0, 0xff, 
+    // 0x000a CHARACTERISTIC-FFF1-READ | WRITE | DYNAMIC
+    0x0d, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x03, 0x28, 0x0a, 0x0b, 0x00, 0xf1, 0xff, 
+    // 0x000b VALUE-FFF1-READ | WRITE | DYNAMIC-
+    0x08, 0x00, 0x0a, 0x01, 0x0b, 0x00, 0xf1, 0xff, 
+    // 0x000c CHARACTERISTIC-FFF2-READ | WRITE | DYNAMIC
+    0x0d, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x03, 0x28, 0x0a, 0x0d, 0x00, 0xf2, 0xff, 
+    // 0x000d VALUE-FFF2-READ | WRITE | DYNAMIC-
+    0x08, 0x00, 0x0a, 0x01, 0x0d, 0x00, 0xf2, 0xff, 
+    // 0x000e CHARACTERISTIC-FFF3-READ | WRITE | DYNAMIC
+    0x0d, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x03, 0x28, 0x0a, 0x0f, 0x00, 0xf3, 0xff, 
+    // 0x000f VALUE-FFF3-READ | WRITE | DYNAMIC-
+    0x08, 0x00, 0x0a, 0x01, 0x0f, 0x00, 0xf3, 0xff, 
+    // END
+    0x00, 0x00, 
+}; // total size 110 bytes 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/UsbBaseClass.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,38 @@
+#include "UsbBaseClass.h"
+//#define __DEBUG
+#include "mydbg.h"
+
+UsbBaseClass::UsbBaseClass()
+{
+    if (m_pHost == NULL) {
+        m_pHost = new UsbHostMgr;
+        DBG_ASSERT(m_pHost);
+        m_pHost->init();
+    }
+    DBG("m_pHost=%p\n", m_pHost);
+}
+
+UsbErr UsbBaseClass::Usb_poll(int timeout, int timeout2)
+{
+    DBG("%p %d %d\n", this, timeout, timeout2);
+    Timer t;
+    t.reset();
+    t.start();
+    Timer t2;
+    t2.reset();
+    t2.start();
+    while(t.read_ms() < timeout) {
+        UsbErr rc = m_pHost->poll();
+        if (rc == USBERR_PROCESSING) {
+            t2.reset();
+        }
+        if (t2.read_ms() > timeout2) {
+            DBG("%p t=%d\n", this, t.read_ms());
+            return USBERR_OK;
+        }
+        wait_ms(50);
+    }
+    return USBERR_PROCESSING;
+}
+
+UsbHostMgr* UsbBaseClass::m_pHost = NULL;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/UsbBaseClass.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,13 @@
+#ifndef _USB_BASE_CLASS_H_
+#define _USB_BASE_CLASS_H_
+#include "UsbHostMgr.h"
+
+class UsbBaseClass {
+public:
+    UsbBaseClass();
+protected:
+    UsbErr Usb_poll(int timeout = 15000, int timeout2 = 2000);
+    static UsbHostMgr* m_pHost;
+};
+
+#endif //_USB_BASE_CLASS_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/UsbDevice.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,400 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ 
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+ 
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "UsbDevice.h"
+//#define __DEBUG
+#include "mydbg.h"
+
+UsbDevice::UsbDevice( UsbHostMgr* pMgr, int hub, int port, int addr ) : m_pControlEp(NULL), /*m_controlEp( this, 0x00, false, USB_CONTROL, 8 ),*/
+m_pMgr(pMgr), m_connected(false), m_enumerated(false), m_hub(hub), m_port(port), m_addr(addr), m_refs(0),
+m_vid(0), m_pid(0)
+{
+    m_DeviceClass    = 0x00;
+    m_InterfaceClass = 0x00;
+}
+
+UsbDevice::~UsbDevice()
+{
+    DBG_ASSERT(0);
+
+  if(m_pControlEp)
+    delete m_pControlEp;
+}
+
+UsbErr UsbDevice::enumerate()
+{
+  VERBOSE("Hub: %d Port: %d\n", m_hub, m_port);
+  UsbErr rc;
+  DBG("%p m_hub=%d m_port=%d\n", this, m_hub, m_port);
+  DBG_ASSERT(m_pMgr);
+  m_pMgr->resetPort(m_hub, m_port);
+
+  wait_ms(400);
+
+  uint8_t temp[8];
+  DBG_ASSERT(m_pControlEp == NULL);   
+  m_pControlEp = new UsbEndpoint( this, 0x00, false, USB_CONTROL, sizeof(temp), 0 );
+  DBG_ASSERT(m_pControlEp);
+  //EDCtrl->Control = 8 << 16;/* Put max pkt size = 8              */
+  /* Read first 8 bytes of device desc */
+  DBG_ASSERT(sizeof(temp) >= 8);
+  //rc = controlReceive(
+  //    USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR, 
+  //    (USB_DESCRIPTOR_TYPE_DEVICE << 8) |(0), 0, temp, sizeof(temp));
+  //DBG_ASSERT(rc == USBERR_OK);
+  rc = GetDescriptor(USB_DESCRIPTOR_TYPE_DEVICE, 0, temp, sizeof(temp));
+  if (rc != USBERR_OK) {
+      DBG("rc=%d\n", rc);
+      DBG_ASSERT(rc == USBERR_OK);
+      return rc;
+  }
+  DBG_ASSERT(rc == USBERR_OK);
+  DBG_BYTES("DeviceDescriptor first 8 bytes", temp, sizeof(temp));
+  DBG_ASSERT(temp[0] == 18);   // bLength
+  DBG_ASSERT(temp[1] == 0x01); // bDescriptType
+  if (rc)
+  {
+    DBG("RC=%d",rc);
+    return (rc);
+  }
+  uint8_t bMaxPacketSize = temp[7];
+  DBG_ASSERT(bMaxPacketSize >= 8);
+  DBG("Got descriptor, max ep size is %d\n", bMaxPacketSize);
+  
+  m_pControlEp->updateSize(bMaxPacketSize); /* Get max pkt size of endpoint 0    */
+  rc = controlSend(USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, SET_ADDRESS, m_addr, 0, NULL, 0); /* Set the device address to m_addr       */
+  DBG_ASSERT(rc == USBERR_OK);
+  if (rc)
+  {
+  //  PRINT_Err(rc);
+    return (rc);
+  }
+  wait_ms(2);
+  //EDCtrl->Control = (EDCtrl->Control) | 1; /* Modify control pipe with address 1 */
+  
+  //Update address
+  m_pControlEp->updateAddr(m_addr);
+  DBG("Ep addr is now %d", m_addr);
+  /**/
+  
+  //rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_DEVICE, 0, TDBuffer, 17); //Read full device descriptor
+  //rc = controlReceive(USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR, 
+  //    (USB_DESCRIPTOR_TYPE_DEVICE << 8)|(0), 0, 
+  //    m_controlDataBuf, 17);
+  uint8_t DeviceDesc[18];
+  rc = GetDescriptor(USB_DESCRIPTOR_TYPE_DEVICE, 0, DeviceDesc, sizeof(DeviceDesc));
+  DBG_ASSERT(rc == USBERR_OK);
+  DBG_BYTES("DeviceDescriptor", DeviceDesc, sizeof(DeviceDesc));
+  DBG_ASSERT(DeviceDesc[0] == 18);
+  DBG_ASSERT(DeviceDesc[1] == 0x01);
+  DBG_ASSERT(DeviceDesc[17] == 1); // bNumConfiguration
+  if (rc)
+  {
+    //PRINT_Err(rc);
+    return (rc);
+  }
+
+  /*
+  rc = SerialCheckVidPid();
+  if (rc != OK) {
+    PRINT_Err(rc);
+    return (rc);
+  }
+  */
+  /**/
+  m_DeviceClass = DeviceDesc[4];
+  VERBOSE("DeviceClass: %02X\n", m_DeviceClass);
+ 
+  m_vid = *((uint16_t*)&DeviceDesc[8]);
+  m_pid = *((uint16_t*)&DeviceDesc[10]);
+  VERBOSE("Vender: %04X\n", m_vid);
+  VERBOSE("Product: %04X\n", m_pid);
+  int iManufacture = DeviceDesc[14];
+  if (iManufacture) {
+    char str[64];
+    rc = GetString(iManufacture, str, sizeof(str)); 
+    DBG_ASSERT(rc == USBERR_OK);
+    VERBOSE("Manufacture: %s\n", str);
+  }
+  int iProduct = DeviceDesc[15];
+  if (iProduct) {
+    char str[64];
+    rc = GetString(iProduct, str, sizeof(str)); 
+    DBG_ASSERT(rc == USBERR_OK);
+    VERBOSE("Product: %s\n", str);
+  }
+  if (DeviceDesc[4] == 0x09) { // Hub
+      return hub_init();
+  }
+  
+  uint8_t ConfigDesc[9];
+  int index = 0;
+  rc = GetDescriptor(USB_DESCRIPTOR_TYPE_CONFIGURATION, index, ConfigDesc, sizeof(ConfigDesc));
+  DBG_ASSERT(rc == USBERR_OK);
+  DBG_BYTES("ConfigDescriptor 9bytes", ConfigDesc, sizeof(ConfigDesc));
+  DBG_ASSERT(ConfigDesc[0] == 9);
+  DBG_ASSERT(ConfigDesc[1] == 0x02);
+  int wTotalLength = *((uint16_t*)&ConfigDesc[2]);
+  DBG("TotalLength: %d\n", wTotalLength);
+  int bConfigValue = ConfigDesc[5];
+  DBG_ASSERT(bConfigValue == 1);
+  DBG("ConfigValue: %d\n", bConfigValue);
+  DBG("MaxPower: %d mA\n", ConfigDesc[8]*2);   
+
+  uint8_t* buf = new uint8_t[wTotalLength];
+  rc = GetDescriptor(USB_DESCRIPTOR_TYPE_CONFIGURATION, index, buf, wTotalLength);
+  DBG_ASSERT(rc == USBERR_OK);
+  DBG_ASSERT(ConfigDesc[1] == 0x02);
+  int pos = 0;
+  while(pos < wTotalLength) {
+      DBG_BYTES("", buf+pos, buf[pos]);
+      if (buf[pos+1] == 4) { // interface ?
+          m_InterfaceClass = buf[pos+5];
+          VERBOSE("InterfaceClass: %02X\n", m_InterfaceClass);
+          break;
+      }
+      pos += buf[pos];
+  }
+  delete[] buf;
+  
+  rc = setConfiguration(1);
+  DBG_ASSERT(rc == USBERR_OK);
+  if (rc)
+  {
+   // PRINT_Err(rc);
+   return rc;
+  }
+  wait_ms(100);/* Some devices may require this delay */
+  
+  m_enumerated = true;
+  return USBERR_OK;
+}
+
+bool UsbDevice::connected()
+{
+  return m_connected;
+}
+
+bool UsbDevice::enumerated()
+{
+  return m_enumerated;
+}
+
+int UsbDevice::getPid()
+{
+  return m_pid;
+}
+
+int UsbDevice::getVid()
+{
+  return m_vid;
+}
+#if 0
+UsbErr UsbDevice::getConfigurationDescriptor(int config, uint8_t** pBuf)
+{
+  DBG_ASSERT(m_controlDataBuf);
+  //For now olny one config
+  *pBuf = m_controlDataBuf;
+  return USBERR_OK;
+}
+
+UsbErr UsbDevice::getInterfaceDescriptor(int config, int item, uint8_t** pBuf)
+{
+  DBG_ASSERT(m_controlDataBuf);
+  byte* desc_ptr = m_controlDataBuf;
+
+/*  if (desc_ptr[1] != USB_DESCRIPTOR_TYPE_CONFIGURATION)
+  {    
+    return USBERR_BADCONFIG;
+  }*/
+  DBG_ASSERT(m_controlDataBuf);
+  if(item>=m_controlDataBuf[4])//Interfaces count
+    return USBERR_NOTFOUND;
+  
+  desc_ptr += desc_ptr[0];
+  
+  *pBuf = NULL;
+  
+  while (desc_ptr < m_controlDataBuf + *((uint16_t*)&m_controlDataBuf[2]))
+  {
+
+    switch (desc_ptr[1]) {
+      case USB_DESCRIPTOR_TYPE_INTERFACE: 
+        if(desc_ptr[2] == item)
+        {
+          *pBuf = desc_ptr;
+          return USBERR_OK;
+        }
+        desc_ptr += desc_ptr[0]; // Move to next descriptor start
+        break;
+    }
+      
+  }
+  
+  if(*pBuf == NULL)
+    return USBERR_NOTFOUND;
+    
+  return USBERR_OK;
+}
+#endif
+
+UsbErr UsbDevice::setConfiguration(int config)
+{
+  DBG("config=%d\n", config);
+  DBG_ASSERT(config == 1);    
+  UsbErr rc = controlSend(
+          USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, // 0x00 
+          SET_CONFIGURATION, config, 0, 0, 0);
+  return rc;
+}
+
+UsbErr UsbDevice::controlSend(byte requestType, byte request, word value, word index, const byte* buf, int len)
+{
+  UsbErr rc;
+  fillControlBuf(requestType, request, value, index, len);
+  DBG_ASSERT(m_pControlEp);
+  m_pControlEp->setNextToken(TD_SETUP);
+  rc = m_pControlEp->transfer(m_controlBuf, 8);
+  while(m_pControlEp->status() == USBERR_PROCESSING);
+  rc = (UsbErr) MIN(0, m_pControlEp->status());
+  if(rc)
+    return rc;
+  if(len)
+  {
+    m_pControlEp->setNextToken(TD_OUT);
+    rc = m_pControlEp->transfer((byte*)buf, len);
+    while(m_pControlEp->status() == USBERR_PROCESSING);
+    rc = (UsbErr) MIN(0, m_pControlEp->status());
+    if(rc)
+      return rc;
+  }
+  m_pControlEp->setNextToken(TD_IN);
+  rc = m_pControlEp->transfer(NULL, 0);
+  while(m_pControlEp->status() == USBERR_PROCESSING);
+  rc = (UsbErr) MIN(0, m_pControlEp->status());
+  if(rc)
+    return rc;
+  return USBERR_OK;
+}
+
+UsbErr UsbDevice::controlReceive(byte requestType, byte request, word value, word index, const byte* buf, int len)
+{
+  DBG("buf=%p len=%d\n", buf, len);
+  UsbErr rc;
+  fillControlBuf(requestType, request, value, index, len);
+  DBG_ASSERT(m_pControlEp);
+  m_pControlEp->setNextToken(TD_SETUP);
+  rc = m_pControlEp->transfer(m_controlBuf, 8);
+  while(m_pControlEp->status() == USBERR_PROCESSING);
+  rc = (UsbErr) MIN(0, m_pControlEp->status());
+  if(rc)
+    return rc;
+  if(len)
+  {
+    m_pControlEp->setNextToken(TD_IN);
+    rc = m_pControlEp->transfer( (byte*) buf, len);
+    while(m_pControlEp->status() == USBERR_PROCESSING);
+    rc = (UsbErr) MIN(0, m_pControlEp->status());
+    if(rc)
+      return rc;
+  }
+  m_pControlEp->setNextToken(TD_OUT);
+  rc = m_pControlEp->transfer(NULL, 0);
+  while(m_pControlEp->status() == USBERR_PROCESSING);
+  rc = (UsbErr) MIN(0, m_pControlEp->status());
+  if(rc)
+    return rc;
+  return USBERR_OK;
+}
+
+UsbErr UsbDevice::GetDescriptor(int type, int index, const byte* buf, int len)
+{
+  DBG("type=%02X\n", type);
+  return controlReceive(
+      USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR, 
+      (type << 8) |(index), 0, buf, len);
+
+}
+
+UsbErr UsbDevice::GetString(int index, char* buf, int len)
+{
+  DBG("index=%d buf=%p len=%d\n", index, buf, len);
+  DBG_ASSERT(index >= 1);
+  uint8_t temp[4];
+  UsbErr rc;
+  rc = GetDescriptor(USB_DESCRIPTOR_TYPE_STRING, 0, temp, sizeof(temp));
+  DBG_ASSERT(rc == USBERR_OK);
+  DBG_BYTES("LANG_ID", temp, sizeof(temp));
+  DBG_ASSERT(temp[0] == 4);
+  DBG_ASSERT(temp[1] == 0x03);
+  
+  rc = GetDescriptor(USB_DESCRIPTOR_TYPE_STRING, index, temp, 2);
+  DBG_ASSERT(rc == USBERR_OK);
+  DBG_BYTES("length check", temp, 2);
+  if (temp[0] == 0x00 && temp[1] == 0x00) { // for pl2303
+      if (len > 0) {
+          strcpy(buf, "");
+      }
+      return rc;
+  }
+  DBG_ASSERT(temp[1] == 0x03);
+  int temp_len = temp[0];
+    
+  uint8_t* temp_buf = new uint8_t[temp_len];
+  DBG_ASSERT(temp_buf);
+  rc = GetDescriptor(USB_DESCRIPTOR_TYPE_STRING, index, temp_buf, temp_len);
+  DBG_ASSERT(rc == USBERR_OK);
+  temp_len = temp_buf[0];
+  DBG_HEX(temp_buf, temp_len);
+  int i = 0;
+  for(int pos = 2; pos < temp_len; pos+= 2) {
+    buf[i++] = temp_buf[pos];
+    DBG_ASSERT(i < len-1);
+  }
+  buf[i] = '\0';
+  delete[] temp_buf;
+  return rc;
+}
+
+UsbErr UsbDevice::SetInterfaceAlternate(int interface, int alternate)
+{
+    UsbErr rc = controlSend(
+              USB_HOST_TO_DEVICE | USB_RECIPIENT_INTERFACE, 
+              SET_INTERFACE, alternate, interface, NULL, 0);
+    return rc;
+}
+
+void UsbDevice::fillControlBuf(byte requestType, byte request, word value, word index, int len)
+{
+#ifdef __BIG_ENDIAN
+  #error "Must implement BE to LE conv here"
+#endif
+  m_controlBuf[0] = requestType;
+  m_controlBuf[1] = request;
+  //We are in LE so it's fine
+  *((word*)&m_controlBuf[2]) = value;
+  *((word*)&m_controlBuf[4]) = index;
+  *((word*)&m_controlBuf[6]) = (word) len;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/UsbDevice.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,99 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ 
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+ 
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef USB_DEVICE_H
+#define USB_DEVICE_H
+
+#include "mbed.h"
+#include "UsbInc.h"
+#include "UsbEndpoint.h"
+#include "UsbHostMgr.h"
+
+class UsbHostMgr;
+class UsbEndpoint;
+
+class UsbDevice
+{
+protected:
+  UsbDevice( UsbHostMgr* pMgr, int hub, int port, int addr );
+  ~UsbDevice();
+
+  UsbErr enumerate();
+
+public: 
+  bool connected();
+  bool enumerated();
+  
+  int getPid();
+  int getVid();
+  
+  //UsbErr getConfigurationDescriptor(int config, uint8_t** pBuf);
+  //UsbErr getInterfaceDescriptor(int config, int item, uint8_t** pBuf);
+  
+  UsbErr setConfiguration(int config);
+
+  UsbErr controlSend(byte requestType, byte request, word value, word index, const byte* buf, int len);
+  UsbErr controlReceive(byte requestType, byte request, word value, word index, const byte* buf, int len);
+  UsbErr GetDescriptor(int type, int index, const byte* buf, int len);
+  UsbErr GetString(int index, char* buf, int len);
+  UsbErr SetInterfaceAlternate(int interface, int alternate);
+
+  uint8_t m_DeviceClass;
+  uint8_t m_InterfaceClass;
+
+protected:
+  void fillControlBuf(byte requestType, byte request, word value, word index, int len);
+private:
+  friend class UsbEndpoint;
+  friend class UsbHostMgr;
+  
+  UsbEndpoint* m_pControlEp;
+  
+  UsbHostMgr* m_pMgr;
+  
+  bool m_connected;
+  bool m_enumerated;
+  
+  int m_hub;
+  int m_port;
+  int m_addr;
+  
+  int m_refs;
+  
+  uint16_t m_vid;
+  uint16_t m_pid;
+  
+  byte m_controlBuf[8];//8
+  //byte m_controlDataBuf[/*128*/256];
+  
+  UsbErr hub_init();
+  UsbErr hub_poll();
+  UsbErr hub_PortReset(int port);
+  UsbErr SetPortFeature(int feature, int index);
+  UsbErr ClearPortFeature(int feature, int index);
+  UsbErr SetPortReset(int port);
+  UsbErr GetPortStatus(int port, uint8_t* buf, int size);
+  int m_hub_ports;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/UsbDevice2.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,133 @@
+#include "UsbDevice.h"
+//#define __DEBUG
+#include "mydbg.h"
+
+#define PORT_RESET         4
+#define PORT_POWER         8
+#define C_PORT_CONNECTION 16
+#define C_PORT_RESET      20
+
+UsbErr UsbDevice::hub_init()
+{
+    UsbErr rc;
+    uint8_t buf[9];
+    rc = controlReceive(
+          USB_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_DEVICE, // 0xa0 
+          GET_DESCRIPTOR, 
+          (USB_DESCRIPTOR_TYPE_HUB << 8), 0, buf, sizeof(buf));
+    DBG_ASSERT(rc == USBERR_OK);
+    DBG_ASSERT(buf[0] == 9);
+    DBG_ASSERT(buf[1] == 0x29);
+    DBG_BYTES("HUB DESCRIPTOR", buf, sizeof(buf));
+
+    m_hub_ports = buf[2];
+    VERBOSE("NbrPorts: %d\n", m_hub_ports);
+    int PwrOn2PwrGood = buf[5];
+    VERBOSE("PwrOn2PwrGood: %d %d ms\n", PwrOn2PwrGood, PwrOn2PwrGood*2);
+    VERBOSE("HubContrCurrent: %d\n", buf[6]);
+
+    rc = setConfiguration(1);    
+    DBG_ASSERT(rc == USBERR_OK);
+    
+    uint8_t status[4];
+    rc = controlReceive(
+          USB_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_DEVICE, // 0xa0 
+          GET_STATUS, 
+          0, 0, status, sizeof(status));
+    DBG_ASSERT(rc == USBERR_OK);
+    DBG_BYTES("HUB STATUS", status, sizeof(status));
+
+    for(int i = 1; i <= m_hub_ports; i++) {
+        rc = SetPortFeature(PORT_POWER, i);
+        DBG("PORT_POWER port=%d rc=%d\n", i, rc);
+        DBG_ASSERT(rc == USBERR_OK);
+        if (rc != USBERR_OK) {
+            return rc;
+        }
+    }
+    wait_ms(PwrOn2PwrGood*2);
+    
+    m_enumerated = true;
+    return USBERR_OK;
+}
+
+UsbErr UsbDevice::hub_poll()
+{
+    DBG("%p m_hub=%d m_port=%d m_addr=%d\n", this, m_hub, m_port, m_addr);
+    // check status
+    for(int port = 1; port <= m_hub_ports; port++) {
+        uint8_t status[4];
+        UsbErr rc = GetPortStatus(port, status, sizeof(status));
+        DBG_ASSERT(rc == USBERR_OK);
+        DBG("port=%d\n", port);
+        DBG_BYTES("STATUS", status, sizeof(status));
+        if (status[2] & 0x01) { // Connect Status Change, has changed
+            DBG_ASSERT(status[0] & 0x01);
+            ClearPortFeature(C_PORT_CONNECTION, port);
+            DBG_ASSERT(m_pMgr);
+            m_pMgr->onUsbDeviceConnected(m_addr, port);
+            return USBERR_PROCESSING;
+        }
+    }
+    return USBERR_OK;
+}
+
+UsbErr UsbDevice::hub_PortReset(int port)
+{
+    DBG("%p port=%d\n", this, port);
+    DBG_ASSERT(port >= 1);
+    SetPortReset(port);
+    // wait reset
+    for(int i = 0; i < 100; i++) {
+        uint8_t status[4];    
+        UsbErr rc = GetPortStatus(port, status, sizeof(status));
+        DBG_ASSERT(rc == USBERR_OK);
+        DBG_BYTES("RESET", status, sizeof(status));
+        if (status[2] & 0x10) { // Reset change , Reset complete
+            return USBERR_OK;
+        }
+        wait_ms(5);
+     }
+     return USBERR_ERROR;
+}
+
+UsbErr UsbDevice::SetPortFeature(int feature, int index)
+{
+    //DBG("feature=%d index=%d\n", feature, index);
+    UsbErr rc;
+    rc = controlSend(
+          USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_OTHER, 
+          SET_FEATURE, feature, index, 0, 0);
+    return rc;
+}
+
+UsbErr UsbDevice::ClearPortFeature(int feature, int index)
+{
+    UsbErr rc;
+    rc = controlSend(
+          USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_OTHER, 
+          CLEAR_FEATURE, feature, index, 0, 0);
+    return rc;
+}
+
+UsbErr UsbDevice::SetPortReset(int port)
+{
+    //DBG("port=%d\n", port);
+    UsbErr rc = SetPortFeature(PORT_RESET, port);
+    DBG_ASSERT(rc == USBERR_OK);
+    return rc;
+}
+
+UsbErr UsbDevice::GetPortStatus(int port, uint8_t* buf, int size)
+{
+    DBG_ASSERT(size == 4);
+    UsbErr rc;
+    //return USBControlTransfer(device,
+    //DEVICE_TO_HOST | REQUEST_TYPE_CLASS | RECIPIENT_OTHER,
+    //GET_STATUS,0,port,(u8*)status,4);
+
+    rc = controlReceive(
+          USB_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_OTHER, 
+          GET_STATUS, 0, port, buf, sizeof(buf));
+    return rc;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/UsbEndpoint.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,373 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ 
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+ 
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "UsbEndpoint.h"
+#include "UsbDevice.h"
+#include "usb_mem.h"
+#include "Usb_td.h"
+#include "netCfg.h"
+#if NET_USB
+
+//#define __DEBUG
+//#define __DEBUG3
+//#include "dbg/dbg.h"
+#include "mydbg.h"
+
+UsbEndpoint::UsbEndpoint( UsbDevice* pDevice, uint8_t ep, bool dir, UsbEndpointType type, uint16_t size, int addr /*= -1*/ ) 
+: m_pDevice(pDevice), m_result(true), m_status((int)USBERR_OK), m_len(0), m_pBufStartPtr(NULL),
+  m_pCbItem(NULL), m_pCbMeth(NULL)
+{
+  if (type == USB_ISO) {
+      UsbEndpoint_iso(pDevice, ep, dir, type, size, addr);
+      return;
+  }
+
+  m_pEd = (volatile HCED*)usb_get_ed();
+  DBG_ASSERT(m_pEd);
+  memset((void*)m_pEd, 0, sizeof(HCED));
+  
+  m_pTdHead = (volatile HCTD*)usb_get_td((uint32_t)this);
+  DBG_ASSERT(m_pTdHead);
+  m_pTdTail = (volatile HCTD*)usb_get_td((uint32_t)this);
+  DBG_ASSERT(m_pTdTail);
+  DBG("m_pEd    =%p\n", m_pEd);
+  DBG("m_pTdHead=%p\n", m_pTdHead);
+  DBG("m_pTdTail=%p\n", m_pTdTail);
+  
+  if(addr == -1)
+    addr = pDevice->m_addr;
+  
+  //Setup Ed
+  //printf("\r\n--Ep Setup--\r\n");
+  m_pEd->Control =  addr        |      /* USB address           */
+  ((ep & 0x7F) << 7)            |      /* Endpoint address      */
+  (type!=USB_CONTROL?((dir?2:1) << 11):0)             |      /* direction : Out = 1, 2 = In */
+  (size << 16);     /* MaxPkt Size           */
+  DBG3("m_pEd->Control=%08X\n", m_pEd->Control);
+  m_dir = dir;
+  m_setup = false;
+  m_type = type;
+  
+  m_pEd->TailTd = m_pEd->HeadTd = (uint32_t) m_pTdTail; //Empty TD list
+  
+  DBG("Before link\n");
+  
+  //printf("\r\n--Ep Reg--\r\n");
+  //Append Ed to Ed list
+  HCCA* hcca;
+  //volatile HCED* nextEd;
+  DBG("m_type=%d\n", m_type);
+  switch( m_type )
+  {
+  case USB_CONTROL:
+    m_pEd->Next = LPC_USB->HcControlHeadED;
+    LPC_USB->HcControlHeadED = (uint32_t)m_pEd;
+    return;
+
+  case USB_BULK:
+    m_pEd->Next = LPC_USB->HcBulkHeadED;
+    LPC_USB->HcBulkHeadED = (uint32_t)m_pEd;
+    return;
+
+  case USB_INT:
+    hcca = (HCCA*)usb_get_hcca();
+    m_pEd->Next = hcca->IntTable[0];
+    hcca->IntTable[0] = (uint32_t)m_pEd;
+    return;
+  
+  default:
+    DBG_ASSERT(0);
+  }
+}
+
+
+UsbEndpoint::~UsbEndpoint()
+{
+  DBG_ASSERT(0);
+
+  m_pEd->Control |= ED_SKIP; //Skip this Ep in queue
+
+  //Remove from queue
+  volatile HCED* prevEd;
+  HCCA* hcca;
+  switch( m_type )
+  {
+  case USB_CONTROL:
+    prevEd = (volatile HCED*) LPC_USB->HcControlHeadED;
+    break;
+  case USB_BULK:
+    prevEd = (volatile HCED*) LPC_USB->HcBulkHeadED;
+    break;
+  case USB_INT:
+    hcca = (HCCA*)usb_get_hcca();
+    prevEd = (volatile HCED*)hcca->IntTable[0];
+    break;
+  default:
+    DBG_ASSERT(0);
+  }
+  if( m_pEd == prevEd )
+  {
+    switch( m_type )
+    {
+    case USB_CONTROL:
+      LPC_USB->HcControlHeadED = m_pEd->Next;
+      break;
+    case USB_BULK:
+      LPC_USB->HcBulkHeadED = m_pEd->Next;
+      break;
+    case USB_INT:
+      hcca = (HCCA*)usb_get_hcca();
+      hcca->IntTable[0] = m_pEd->Next;
+      break;
+    default:
+      DBG_ASSERT(0);
+    }
+    LPC_USB->HcBulkHeadED = m_pEd->Next;
+  }
+  else
+  {
+    while( prevEd->Next != (uint32_t) m_pEd )
+    {
+      prevEd = (volatile HCED*) prevEd->Next;
+    }
+    prevEd->Next = m_pEd->Next;
+  }
+  
+  //
+  usb_free_ed((volatile byte*)m_pEd);
+  
+  usb_free_td((volatile byte*)m_pTdHead);
+  usb_free_td((volatile byte*)m_pTdTail);
+}
+
+void UsbEndpoint::setNextToken(uint32_t token) //Only for control Eps
+{
+  switch(token)
+  {
+    case TD_SETUP:
+      m_dir = false;
+      m_setup = true;
+      break;
+    case TD_IN:
+      m_dir = true;
+      m_setup = false;
+      break;
+    case TD_OUT:
+      m_dir = false;
+      m_setup = false;
+      break;
+  }
+}
+
+UsbErr UsbEndpoint::transfer(volatile uint8_t* buf, uint32_t len)
+{
+  DBG("buf=%p\n", buf);
+  if(!m_result)
+    return USBERR_BUSY; //The previous trasnfer is not completed 
+    //FIXME: We should be able to queue the next transfer, still needs to be implemented
+  
+  if( !m_pDevice->connected() )
+    return USBERR_DISCONNECTED;
+  
+  m_result = false;
+  
+  volatile uint32_t token = (m_setup?TD_SETUP:(m_dir?TD_IN:TD_OUT));
+
+  volatile uint32_t td_toggle;
+  if (m_type == USB_CONTROL)
+  {
+    if (m_setup)
+    {
+      td_toggle = TD_TOGGLE_0;
+    }
+    else
+    {
+      td_toggle = TD_TOGGLE_1;
+    }
+  }
+  else
+  {
+    td_toggle = 0;
+  }
+
+  //Swap Tds
+  volatile HCTD* pTdSwap;
+  pTdSwap = m_pTdTail;
+  m_pTdTail = m_pTdHead;
+  m_pTdHead = pTdSwap;
+
+  m_pTdHead->Control = (TD_ROUNDING    |
+                       token           |
+                       TD_DELAY_INT(0) |//7
+                       td_toggle       |
+                       TD_CC);
+
+  m_pTdTail->Control = 0;
+  m_pTdHead->CurrBufPtr   = (uint32_t) buf;
+  m_pBufStartPtr = buf;
+  m_pTdTail->CurrBufPtr   = 0;
+  m_pTdHead->Next         = (uint32_t) m_pTdTail;
+  m_pTdTail->Next         = 0;
+  m_pTdHead->BufEnd       = (uint32_t)(buf + (len - 1));
+  m_pTdTail->BufEnd       = 0;
+  
+  m_pEd->HeadTd  = (uint32_t)m_pTdHead | ((m_pEd->HeadTd) & 0x00000002); //Carry bit
+  m_pEd->TailTd  = (uint32_t)m_pTdTail;
+  
+  //DBG("m_pEd->HeadTd = %08x\n", m_pEd->HeadTd);
+
+  if(m_type == USB_CONTROL) {
+    LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | OR_CMD_STATUS_CLF;
+    LPC_USB->HcControl       = LPC_USB->HcControl       | OR_CONTROL_CLE; //Enable control list
+  } else if (m_type == USB_BULK) { //USB_BULK
+    LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | OR_CMD_STATUS_BLF;
+    LPC_USB->HcControl       = LPC_USB->HcControl       | OR_CONTROL_BLE; //Enable bulk list
+  } else if (m_type == USB_INT) { // USB_INT
+    LPC_USB->HcControl       = LPC_USB->HcControl       | OR_CONTROL_PLE; //Enable Periodic
+  } else { // USB_ISO
+    DBG_ASSERT(0);
+  }
+  
+  //m_done = false;
+  m_len = len;
+
+  return USBERR_PROCESSING;
+ 
+}
+
+int UsbEndpoint::status()
+{
+  if( !m_pDevice->connected() )
+  {
+    if(!m_result)
+      onCompletion();
+    m_result = true;
+    return (int)USBERR_DISCONNECTED;
+  }
+  else if( !m_result )
+  { 
+    return (int)USBERR_PROCESSING;
+  }
+  /*else if( m_done )
+  {
+    return (int)USBERR_OK;
+  }*/
+  else
+  {
+    return m_status;
+  }
+}
+
+void UsbEndpoint::updateAddr(int addr)
+{
+  DBG("m_pEd->Control = %08x\n", m_pEd->Control);
+  m_pEd->Control &= ~0x7F;
+  m_pEd->Control |= addr;
+  DBG("m_pEd->Control = %08x\n", m_pEd->Control);
+}
+
+void UsbEndpoint::updateSize(uint16_t size)
+{
+  DBG("m_pEd->Control = %08x\n", m_pEd->Control);
+  m_pEd->Control &= ~0x3FF0000;
+  m_pEd->Control |= (size << 16);
+  DBG("m_pEd->Control = %08x\n", m_pEd->Control);
+}
+
+#if 0 //For doc only
+template <class T>
+void UsbEndpoint::setOnCompletion( T* pCbItem, void (T::*pCbMeth)() )
+{
+  m_pCbItem = (CDummy*) pCbItem;
+  m_pCbMeth = (void (CDummy::*)()) pCbMeth;
+}
+#endif
+
+void UsbEndpoint::onCompletion()
+{
+  DBG_ASSERT(m_type != USB_ISO);
+  DBG_ASSERT(m_pTdHead);
+  //DBG("Transfer completed\n");
+  if( m_pTdHead->Control >> 28  )
+  {
+    DBG("TD Failed with condition code %01x\n", m_pTdHead->Control >> 28 );
+    m_status = (int)USBERR_TDFAIL;
+  }
+  else if( m_pEd->HeadTd & 0x1 )
+  {
+    m_pEd->HeadTd = m_pEd->HeadTd & ~0x1;
+    DBG("\r\nHALTED!!\r\n");
+    m_status = (int)USBERR_HALTED;
+  }
+  else if( (m_pEd->HeadTd & ~0xF) == (uint32_t) m_pTdTail )
+  {
+    //Done
+    int len;
+    DBG("m_pEp=%p\n", m_pEd);
+    DBG("m_pTdHead->CurrBufPtr=%08x\n", (uint32_t)m_pTdHead->CurrBufPtr);
+    DBG("m_pBufStartPtr=%08x\n", (uint32_t) m_pBufStartPtr);
+    if(m_pTdHead->CurrBufPtr)
+      len = m_pTdHead->CurrBufPtr - (uint32_t) m_pBufStartPtr;
+    else
+      len = m_len;
+    /*if(len == 0) //Packet transfered completely
+      len = m_len;*/
+    //m_done = true;
+    DBG("Transfered %d bytes\n", len);
+    m_status = len; 
+  }
+  else
+  {
+    DBG("Unknown error...\n");
+    m_status = (int)USBERR_ERROR;
+  }
+  m_result = true;
+  if(m_pCbItem && m_pCbMeth)
+    (m_pCbItem->*m_pCbMeth)();
+}
+
+
+ 
+void UsbEndpoint::sOnCompletion(uint32_t pTd)
+{
+    HCTD* td = td_reverse((HCTD*)pTd);
+    while(td) {
+        HCTD* next = (HCTD*)td->Next;
+        HCUTD* utd = (HCUTD*)td;
+        UsbEndpoint* pEp = (UsbEndpoint*)utd->UsbEndpoint;
+        DBG_ASSERT(pEp);
+        if (usb_is_itd((byte*)td)) {
+            HCITD* itd = (HCITD*)td;
+            DBG_ASSERT(pEp->m_type == USB_ISO);
+            pEp->queue_done_itd.push(itd);
+        } else {
+            DBG_ASSERT(pEp->m_pTdHead == td);
+            if(pEp->m_pTdHead == td) { // found?
+                pEp->onCompletion();
+            }
+        }
+        td = next;
+    }
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/UsbEndpoint.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,100 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ 
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+ 
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef USB_ENDPOINT_H
+#define USB_ENDPOINT_H
+
+#include "mbed.h"
+#include "UsbInc.h"
+#include "Usb_td.h"
+
+class UsbDevice;
+
+enum UsbEndpointType
+{
+  USB_CONTROL,
+  USB_BULK,
+  USB_INT,
+  USB_ISO
+};
+
+class UsbEndpoint
+{
+public:
+  UsbEndpoint( UsbDevice* pDevice, uint8_t ep, bool dir, UsbEndpointType type, uint16_t size, int addr = -1 );
+  ~UsbEndpoint();
+  void UsbEndpoint_iso(UsbDevice* pDevice, uint8_t ep, bool dir, UsbEndpointType type, uint16_t size, int addr);
+
+  void setNextToken(uint32_t token); //Only for control Eps
+  
+  UsbErr transfer(volatile uint8_t* buf, uint32_t len); 
+  UsbErr transfer(uint16_t frame, int count, volatile uint8_t* buf, int len); // for isochronous
+  int m_itdActive; 
+  tdqueue <HCITD*> queue_done_itd;
+  int status(); //return UsbErr or transfered len
+  
+  void updateAddr(int addr);
+  void updateSize(uint16_t size);
+  
+  //void setOnCompletion( void(*pCb)completed() );
+  class CDummy;
+  template <class T>
+  void setOnCompletion( T* pCbItem, void (T::*pCbMeth)() )
+  {
+    m_pCbItem = (CDummy*) pCbItem;
+    m_pCbMeth = (void (CDummy::*)()) pCbMeth;
+  }
+   
+//static void completed(){}
+
+protected:
+  void onCompletion();
+public:
+  static void sOnCompletion(uint32_t pTd);
+  
+private:
+  friend class UsbDevice;
+
+  UsbDevice* m_pDevice;
+  
+  bool m_dir;
+  bool m_setup;
+  UsbEndpointType m_type;
+  
+  //bool m_done;
+  volatile bool m_result;
+  volatile int m_status;
+  
+  volatile uint32_t m_len;
+  
+  volatile uint8_t* m_pBufStartPtr;
+
+  volatile HCED* m_pEd; //Ep descriptor
+
+  volatile HCTD* m_pTdHead; //Head trf descriptor
+  volatile HCTD* m_pTdTail; //Tail trf descriptor
+
+  CDummy* m_pCbItem;
+  void (CDummy::*m_pCbMeth)();
+};
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/UsbEndpoint2.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,132 @@
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ 
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+ 
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include "UsbEndpoint.h"
+#include "UsbDevice.h"
+#include "usb_mem.h"
+
+//#define __DEBUG
+//#define __DEBUG3
+//#include "dbg/dbg.h"
+#include "mydbg.h"
+
+void UsbEndpoint::UsbEndpoint_iso(UsbDevice* pDevice, uint8_t ep, bool dir, UsbEndpointType type, uint16_t size, int addr)
+{
+  m_itdActive = 0;
+  m_pEd = (volatile HCED*)usb_get_ed();
+  DBG_ASSERT(m_pEd);
+  memset((void*)m_pEd, 0, sizeof(HCED));
+  
+  m_pTdHead = NULL;
+  m_pTdTail = NULL;
+  
+  volatile HCTD* itd = (volatile HCTD*)usb_get_itd((uint32_t)this);
+  DBG_ASSERT(itd);
+  memset((void*)itd, 0, sizeof(HCITD));
+  DBG3("m_pEd =%p\n", m_pEd);
+  DBG3("itd   =%p\n", itd);
+  
+  if(addr == -1)
+    addr = pDevice->m_addr;
+  
+  //Setup Ed
+  //printf("\r\n--Ep Setup--\r\n");
+  m_pEd->Control =  addr | /* USB address */
+      ((ep & 0x7F) << 7) | /* Endpoint address */
+      ((dir?2:1) << 11)  | /* direction : Out = 1, 2 = In */
+      (1 << 15)          | /* F Format */
+      (size << 16);        /* MaxPkt Size */
+
+  DBG3("m_pEd->Control=%08X\n", m_pEd->Control);
+
+  m_dir = dir;
+  m_setup = false;
+  m_type = type;
+  
+  m_pEd->TailTd = m_pEd->HeadTd = (uint32_t)itd; //Empty TD list
+  
+  DBG("Before link\n");
+  
+  //printf("\r\n--Ep Reg--\r\n");
+  //Append Ed to Ed list
+  HCCA* hcca = (HCCA*)usb_get_hcca();
+  for(int i = 0; i < 32; i++) {
+    if (hcca->IntTable[i] == 0) {
+      hcca->IntTable[i] = (uint32_t)m_pEd;
+    } else {
+      volatile HCED* nextEd = (volatile HCED*)hcca->IntTable[i];
+      while(nextEd->Next && nextEd->Next != (uint32_t)m_pEd) {
+        nextEd = (volatile HCED*)nextEd->Next;
+      }
+      nextEd->Next = (uint32_t)m_pEd;
+    }
+  }
+}
+
+// for isochronous 
+UsbErr UsbEndpoint::transfer(uint16_t frame, int count, volatile uint8_t* buf, int len)
+{
+  DBG_ASSERT(count >= 1 && count <= 8);
+  DBG_ASSERT(buf);
+  DBG_ASSERT((len % count) == 0);
+  HCITD *itd = (HCITD*)m_pEd->TailTd;
+  DBG_ASSERT(itd); 
+  HCITD *new_itd = (HCITD*)usb_get_itd((uint32_t)this);
+  DBG_ASSERT(new_itd);
+  if (itd == NULL) {
+    return USBERR_ERROR;
+  }
+  DBG("itd=%p\n", itd);
+  DBG2("new_itd=%p\n", new_itd);
+  int di = 0; //DelayInterrupt
+  itd->Control = 0xe0000000        | // CC ConditionCode NOT ACCESSED
+                 ((count-1) << 24) | // FC FrameCount
+                 TD_DELAY_INT(di)  | // DI DelayInterrupt
+                 frame;              // SF StartingFrame
+  itd->BufferPage0 = (uint32_t)buf;
+  itd->BufferEnd = (uint32_t)buf+len-1;
+  itd->Next = (uint32_t)new_itd; 
+  uint16_t offset[8];
+  for(int i = 0; i < 8; i++) {
+      uint32_t addr = (uint32_t)buf + i*(len/count);
+      offset[i] = addr & 0x0fff;
+      if ((addr&0xfffff000) == (itd->BufferEnd&0xfffff000)) {
+          offset[i] |= 0x1000;
+      }
+      offset[i] |= 0xe000;
+  }
+  itd->OffsetPSW10 = (offset[1]<<16) | offset[0];
+  itd->OffsetPSW32 = (offset[3]<<16) | offset[2];
+  itd->OffsetPSW54 = (offset[5]<<16) | offset[4];
+  itd->OffsetPSW76 = (offset[7]<<16) | offset[6];
+  m_itdActive++;
+  DBG2("itd->Control    =%08X\n", itd->Control); 
+  DBG2("itd->BufferPage0=%08X\n", itd->BufferPage0); 
+  DBG2("itd->Next       =%08X\n", itd->Next); 
+  DBG2("itd->BufferEnd  =%08X\n", itd->BufferEnd); 
+  DBG2("itd->OffsetPSW10=%08X\n", itd->OffsetPSW10); 
+  DBG2("itd->OffsetPSW32=%08X\n", itd->OffsetPSW32); 
+  DBG2("itd->OffsetPSW54=%08X\n", itd->OffsetPSW54); 
+  DBG2("itd->OffsetPSW76=%08X\n", itd->OffsetPSW76); 
+  m_pEd->TailTd = (uint32_t)new_itd; // start!!!
+  LPC_USB->HcControl |= OR_CONTROL_PLE; //Enable Periodic
+  return USBERR_PROCESSING;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/UsbHostMgr.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,395 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ 
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+ 
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include "UsbHostMgr.h"
+#include "usb_mem.h"
+#include "Usb_td.h"
+#include "string.h" //For memcpy, memmove, memset
+#include "netCfg.h"
+#if NET_USB
+//#define __DEBUG
+//#define __DEBUG3
+//#include "dbg/dbg.h"
+#include "mydbg.h"
+
+// bits of the USB/OTG clock control register
+#define HOST_CLK_EN     (1<<0)
+#define DEV_CLK_EN      (1<<1)
+#define PORTSEL_CLK_EN  (1<<3)
+#define AHB_CLK_EN      (1<<4)
+
+// bits of the USB/OTG clock status register
+#define HOST_CLK_ON     (1<<0)
+#define DEV_CLK_ON      (1<<1)
+#define PORTSEL_CLK_ON  (1<<3)
+#define AHB_CLK_ON      (1<<4)
+
+// we need host clock, OTG/portsel clock and AHB clock
+#define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN)
+
+static UsbHostMgr* pMgr = NULL;
+
+extern "C" void sUsbIrqhandler(void) __irq
+{
+  DBG("\n+Int\n");
+  if(pMgr)
+    pMgr->UsbIrqhandler();
+  DBG("\n-Int\n");
+  return;
+}
+
+UsbHostMgr::UsbHostMgr() : m_lpDevices()
+{
+  /*if(!pMgr)*/ //Assume singleton
+  pMgr = this;
+  usb_mem_init();
+  for(int i = 0; i < USB_HOSTMGR_MAX_DEVS; i++) {
+      m_lpDevices[i] = NULL;
+  }
+  m_pHcca = (HCCA*) usb_get_hcca();
+  memset((void*)m_pHcca, 0, 0x100);
+  m_hardware_init = false;
+  DBG("Host manager at %p\n", this);
+  
+  test_td(); // TD test program
+}
+
+UsbHostMgr::~UsbHostMgr()
+{
+  if(pMgr == this)
+    pMgr = NULL;
+}
+
+UsbErr UsbHostMgr::init() //Initialize host
+{
+  DBG("m_hardware_init=%d\n", m_hardware_init);
+  if(m_hardware_init) {
+      return USBERR_OK;
+  }
+
+  NVIC_DisableIRQ(USB_IRQn);                           /* Disable the USB interrupt source           */
+  
+  LPC_SC->PCONP       &= ~(1UL<<31); //Cut power
+  wait(1);
+  
+  
+  // turn on power for USB
+  LPC_SC->PCONP       |= (1UL<<31);
+  // Enable USB host clock, port selection and AHB clock
+  LPC_USB->USBClkCtrl |= CLOCK_MASK;
+  // Wait for clocks to become available
+  while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK)
+      ;
+  
+  // it seems the bits[0:1] mean the following
+  // 0: U1=device, U2=host
+  // 1: U1=host, U2=host
+  // 2: reserved
+  // 3: U1=host, U2=device
+  // NB: this register is only available if OTG clock (aka "port select") is enabled!!
+  // since we don't care about port 2, set just bit 0 to 1 (U1=host)
+  LPC_USB->OTGStCtrl |= 1;
+  
+  // now that we've configured the ports, we can turn off the portsel clock
+  LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN;
+  
+  // power pins are not connected on mbed, so we can skip them
+  /* P1[18] = USB_UP_LED, 01 */
+  /* P1[19] = /USB_PPWR,     10 */
+  /* P1[22] = USB_PWRD, 10 */
+  /* P1[27] = /USB_OVRCR, 10 */
+  /*LPC_PINCON->PINSEL3 &= ~((3<<4) | (3<<6) | (3<<12) | (3<<22));  
+  LPC_PINCON->PINSEL3 |=  ((1<<4)|(2<<6) | (2<<12) | (2<<22));   // 0x00802080
+  */
+
+  // configure USB D+/D- pins
+  /* P0[29] = USB_D+, 01 */
+  /* P0[30] = USB_D-, 01 */
+  LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28));  
+  LPC_PINCON->PINSEL1 |=  ((1<<26)|(1<<28));     // 0x14000000
+      
+  DBG("Initializing Host Stack\n");
+  
+  wait_ms(100);                                   /* Wait 50 ms before apply reset              */
+  LPC_USB->HcControl       = 0;                       /* HARDWARE RESET                             */
+  LPC_USB->HcControlHeadED = 0;                       /* Initialize Control list head to Zero       */
+  LPC_USB->HcBulkHeadED    = 0;                       /* Initialize Bulk list head to Zero          */
+  
+                                                      /* SOFTWARE RESET                             */
+  LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR;
+  LPC_USB->HcFmInterval    = DEFAULT_FMINTERVAL;      /* Write Fm Interval and Largest Data Packet Counter */
+  LPC_USB->HcPeriodicStart = FI*90/100;
+
+                                                      /* Put HC in operational state                */
+  LPC_USB->HcControl  = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER;
+  LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC;            /* Set Global Power                           */
+  
+  LPC_USB->HcHCCA = (uint32_t)(m_pHcca);
+  LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus;                   /* Clear Interrrupt Status                    */
+
+
+  LPC_USB->HcInterruptEnable  = OR_INTR_ENABLE_MIE |
+                       OR_INTR_ENABLE_WDH |
+                       OR_INTR_ENABLE_RHSC;
+
+  NVIC_SetPriority(USB_IRQn, 0);       /* highest priority */
+  /* Enable the USB Interrupt */
+  NVIC_SetVector(USB_IRQn, (uint32_t)(sUsbIrqhandler));
+  LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
+  LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
+  
+  
+  /* Check for any connected devices */
+  //if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS)  //Root device connected
+  //{
+  //  //Device connected
+  //  wait(1);
+  //  DBG("Device connected (%08x)\n", LPC_USB->HcRhPortStatus1);
+  //  onUsbDeviceConnected(0, 1); //Hub 0 (root hub), Port 1 (count starts at 1)
+  //}
+    
+  DBG("Enabling IRQ\n");
+  NVIC_EnableIRQ(USB_IRQn);
+  DBG("End of host stack initialization\n");
+  m_hardware_init = true;
+  return USBERR_OK;
+}
+
+UsbErr UsbHostMgr::poll() //Enumerate connected devices, etc
+{
+  /* Check for any connected devices */
+  if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS)  //Root device connected
+  {
+    //Device connected
+    wait(1);
+    DBG("Device connected (%08x)\n", LPC_USB->HcRhPortStatus1);
+    onUsbDeviceConnected(0, 1); //Hub 0 (root hub), Port 1 (count starts at 1)
+  }
+  
+  for(int i = 0; i < USB_HOSTMGR_MAX_DEVS; i++)
+  {
+    UsbDevice* dev = m_lpDevices[i];
+    if (dev == NULL) {
+      continue;
+    }
+    DBG3("%d dev=%p %d %d addr=%d\n", i, dev, dev->m_connected, dev->m_enumerated, dev->m_addr);
+    if(dev->m_connected) {
+      if (!dev->m_enumerated) {
+          dev->enumerate();
+          return USBERR_PROCESSING;
+      }
+    }
+  }
+  for(int i = 0; i < USB_HOSTMGR_MAX_DEVS; i++) {
+    UsbDevice* dev = m_lpDevices[i];
+    if (dev == NULL) {
+      continue;
+    }   
+    if (dev->m_connected && dev->m_enumerated) {
+      if (dev->m_DeviceClass == 0x09) { // HUB
+        UsbErr rc = dev->hub_poll();
+        if (rc == USBERR_PROCESSING) {
+          return USBERR_PROCESSING;
+        } 
+      }
+    }
+  }
+  return USBERR_OK;
+}
+
+int UsbHostMgr::devicesCount()
+{
+  int i;
+  for(i = 0; i < USB_HOSTMGR_MAX_DEVS; i++)
+  {
+    if (m_lpDevices[i] == NULL) {
+      return i;
+    }
+  }
+  return i;
+}
+
+UsbDevice* UsbHostMgr::getDevice(int item)
+{
+  UsbDevice* pDev = m_lpDevices[item];
+  if(!pDev)
+    return NULL;
+    
+  pDev->m_refs++;
+  return pDev;
+}
+
+void UsbHostMgr::releaseDevice(UsbDevice* pDev)
+{
+  DBG_ASSERT(0);
+
+  pDev->m_refs--;
+  if(pDev->m_refs > 0)
+    return;
+  //If refs count = 0, delete
+  //Find & remove from list
+  int i;
+  for(i = 0; i < USB_HOSTMGR_MAX_DEVS; i++)
+  {
+    if (m_lpDevices[i] == pDev)
+      break;
+  }
+  if(i!=USB_HOSTMGR_MAX_DEVS)
+    memmove(&m_lpDevices[i], &m_lpDevices[i+1], sizeof(UsbDevice*) * (USB_HOSTMGR_MAX_DEVS - (i + 1))); //Safer than memcpy because of overlapping mem
+  m_lpDevices[USB_HOSTMGR_MAX_DEVS - 1] = NULL;
+  delete pDev;
+}
+
+void UsbHostMgr::UsbIrqhandler()
+{
+  uint32_t   int_status;
+  uint32_t   ie_status;
+  
+  int_status    = LPC_USB->HcInterruptStatus;                          /* Read Interrupt Status                */
+  ie_status     = LPC_USB->HcInterruptEnable;                          /* Read Interrupt enable status         */
+
+  if (!(int_status & ie_status))
+  {
+    return;
+  }
+  else
+  {
+    int_status = int_status & ie_status;
+    if (int_status & OR_INTR_STATUS_RHSC) /* Root hub status change interrupt     */
+    {
+      DBG("LPC_USB->HcRhPortStatus1 = %08x\n", LPC_USB->HcRhPortStatus1);
+      if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CSC)
+      {
+        if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE)
+        {
+            /*
+             * When DRWE is on, Connect Status Change
+             * means a remote wakeup event.
+            */
+            //HOST_RhscIntr = 1;// JUST SOMETHING FOR A BREAKPOINT
+        }
+        else
+        {
+          /*
+           * When DRWE is off, Connect Status Change
+           * is NOT a remote wakeup event
+          */
+          if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS)  //Root device connected
+          {
+            //Device connected
+            DBG("Device connected (%08x)\n", LPC_USB->HcRhPortStatus1);
+            onUsbDeviceConnected(0, 1); //Hub 0 (root hub), Port 1 (count starts at 1)
+          }
+          else //Root device disconnected
+          {
+            //Device disconnected
+            DBG("Device disconnected\n");
+            onUsbDeviceDisconnected(0, 1);
+          }
+          //TODO: HUBS
+        }
+        LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC;
+      }
+      if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRSC)
+      {
+        LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC;
+      }
+    }  
+    if (int_status & OR_INTR_STATUS_WDH) /* Writeback Done Head interrupt        */
+    {                  
+      //UsbEndpoint::sOnCompletion((LPC_USB->HccaDoneHead) & 0xFE);
+      if(m_pHcca->DoneHead)
+      {
+        UsbEndpoint::sOnCompletion(m_pHcca->DoneHead);
+        m_pHcca->DoneHead = 0;
+        LPC_USB->HcInterruptStatus = OR_INTR_STATUS_WDH;
+        if(m_pHcca->DoneHead)
+          DBG("??????????????????????????????\n\n\n");
+      }
+      else
+      {
+        //Probably an error
+        int_status = LPC_USB->HcInterruptStatus;
+        DBG("HcInterruptStatus = %08x\n", int_status);
+        if (int_status & OR_INTR_STATUS_UE) //Unrecoverable error, disconnect devices and resume
+        {
+          onUsbDeviceDisconnected(0, 1);
+          LPC_USB->HcInterruptStatus = OR_INTR_STATUS_UE;
+          LPC_USB->HcCommandStatus = 0x01; //Host Controller Reset
+        }
+      }
+    }
+    LPC_USB->HcInterruptStatus = int_status; /* Clear interrupt status register      */
+  }
+  return;
+}
+
+void UsbHostMgr::onUsbDeviceDisconnected(int hub, int port)
+{
+  for(int i = 0; i < devicesCount(); i++)
+  {
+     if( (m_lpDevices[i]->m_hub == hub)
+     &&  (m_lpDevices[i]->m_port == port) )
+     {
+       m_lpDevices[i]->m_connected = false;
+       if(!m_lpDevices[i]->m_enumerated)
+       {
+         delete m_lpDevices[i];
+         m_lpDevices[i] = NULL;
+       }
+       return;
+     }
+  }
+}
+
+void UsbHostMgr::resetPort(int hub, int port)
+{
+  DBG3("hub=%d port=%d\n", hub, port);
+  if(hub == 0) //Root hub
+  {
+    wait_ms(100); /* USB 2.0 spec says at least 50ms delay before port reset */
+    LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset
+    DBG("Before loop\n");
+    while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS)
+      ;
+    LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal
+    DBG("After loop\n");
+    wait_ms(200); /* Wait for 100 MS after port reset  */
+  }
+  else
+  {
+    for(int i = 0; i < USB_HOSTMGR_MAX_DEVS; i++) {
+      UsbDevice* dev = m_lpDevices[i];
+      if (dev == NULL) {
+        continue;
+      }
+      if (dev->m_addr == hub) {
+        DBG("%d dev=%p\n", i, dev);
+        dev->hub_PortReset(port);
+        return;
+      }
+    }
+    DBG_ASSERT(0);
+  }
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/UsbHostMgr.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,69 @@
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ 
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+ 
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+//Assigns addresses to connected devices...
+
+#ifndef USB_HOSTMGR_H
+#define USB_HOSTMGR_H
+
+#include "mbed.h"
+#include "UsbInc.h"
+#include "UsbDevice.h"
+
+#define USB_HOSTMGR_MAX_DEVS 8
+
+class UsbDevice;
+
+class UsbHostMgr //[0-2] inst
+{
+public:
+  UsbHostMgr();
+  ~UsbHostMgr();
+  
+  UsbErr init(); //Initialize host
+  
+  UsbErr poll(); //Enumerate connected devices, etc
+  
+  int devicesCount();
+  
+  UsbDevice* getDevice(int item);
+  UsbDevice* getDeviceByClass(uint8_t IfClass, int count = 0);
+  void releaseDevice(UsbDevice* pDev);
+  
+
+  void UsbIrqhandler();
+
+protected:  
+  void onUsbDeviceConnected(int hub, int port);
+  void onUsbDeviceDisconnected(int hub, int port);
+  
+  friend class UsbDevice;
+  void resetPort(int hub, int port);
+  
+private:
+/*  __align(8)*/ volatile HCCA* m_pHcca;
+  
+  UsbDevice* m_lpDevices[USB_HOSTMGR_MAX_DEVS];
+  bool m_hardware_init; 
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/UsbHostMgr2.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,51 @@
+#include "UsbHostMgr.h"
+//#define __DEBUG
+#include "mydbg.h"
+
+UsbDevice* UsbHostMgr::getDeviceByClass(uint8_t IfClass, int count)
+{
+    DBG("IfClass=%02X count=%d\n", IfClass, count);
+    for(int i = 0; i < USB_HOSTMGR_MAX_DEVS; i++) {
+       UsbDevice* dev = m_lpDevices[i];
+       if (dev) {
+           if(dev->m_connected && dev->m_enumerated) {
+               if (dev->m_InterfaceClass == IfClass) { // found
+                   if (count-- <= 0) {
+                       return dev;
+                   }
+               }
+           }
+       }
+    }
+    return NULL;
+}
+
+void UsbHostMgr::onUsbDeviceConnected(int hub, int port)
+{
+  DBG("%p hub=%d port=%d\n", this, hub, port);
+  for(int i = 0; i < USB_HOSTMGR_MAX_DEVS; i++) {
+      UsbDevice* dev = m_lpDevices[i];
+      if (dev) {
+          if (dev->m_hub == hub && dev->m_port == port) { // skip
+              return;
+          }
+      }
+  }
+  
+  int item = devicesCount();
+  DBG_ASSERT(item < USB_HOSTMGR_MAX_DEVS);
+  if( item == USB_HOSTMGR_MAX_DEVS )
+    return; //List full...
+  //Find a free address (not optimized, but not really important)
+  int addr = 1;
+  for(int i = 0; i < item; i++)
+  {
+    DBG_ASSERT(m_lpDevices[i]);
+    addr = MAX( addr, m_lpDevices[i]->m_addr + 1 );
+  }
+  DBG("item=%d addr=%d\n", item, addr);
+  UsbDevice* dev = new UsbDevice( this, hub, port, addr );
+  DBG_ASSERT(dev);
+  dev->m_connected = true;
+  m_lpDevices[item] = dev;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/UsbInc.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,227 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ 
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+ 
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef USB_INC_H
+#define USB_INC_H
+
+#include "mbed.h"
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+//typedef int32_t RC;
+
+typedef uint8_t byte;
+typedef uint16_t word;
+
+enum UsbErr
+{
+  __USBERR_MIN = -0xFFFF,
+  USBERR_DISCONNECTED,
+  USBERR_NOTFOUND,
+  USBERR_BADCONFIG,
+  USBERR_PROCESSING,
+  USBERR_HALTED, //Transfer on an ep is stalled
+  USBERR_BUSY,
+  USBERR_TDFAIL,
+  USBERR_ERROR,
+  USBERR_OK = 0
+};
+
+
+/* From NXP's USBHostLite stack's usbhost_lpc17xx.h */
+/* Only the types names have been changed to avoid unecessary typedefs */
+
+
+/*
+**************************************************************************************************************
+*                                                 NXP USB Host Stack
+*
+*                                     (c) Copyright 2008, NXP SemiConductors
+*                                     (c) Copyright 2008, OnChip  Technologies LLC
+*                                                 All Rights Reserved
+*
+*                                                  www.nxp.com
+*                                               www.onchiptech.com
+*
+* File           : usbhost_lpc17xx.h
+* Programmer(s)  : Ravikanth.P
+* Version        :
+*
+**************************************************************************************************************
+*/
+
+
+
+/*
+**************************************************************************************************************
+*                                  OHCI OPERATIONAL REGISTER FIELD DEFINITIONS
+**************************************************************************************************************
+*/
+
+                                            /* ------------------ HcControl Register ---------------------  */
+#define  OR_CONTROL_PLE                 0x00000004
+#define  OR_CONTROL_IE                  0x00000008
+#define  OR_CONTROL_CLE                 0x00000010
+#define  OR_CONTROL_BLE                 0x00000020
+#define  OR_CONTROL_HCFS                0x000000C0
+#define  OR_CONTROL_HC_OPER             0x00000080
+                                            /* ----------------- HcCommandStatus Register ----------------- */
+#define  OR_CMD_STATUS_HCR              0x00000001
+#define  OR_CMD_STATUS_CLF              0x00000002
+#define  OR_CMD_STATUS_BLF              0x00000004
+                                            /* --------------- HcInterruptStatus Register ----------------- */
+#define  OR_INTR_STATUS_WDH             0x00000002
+#define  OR_INTR_STATUS_RHSC            0x00000040
+#define  OR_INTR_STATUS_UE              0x00000010
+                                            /* --------------- HcInterruptEnable Register ----------------- */
+#define  OR_INTR_ENABLE_WDH             0x00000002
+#define  OR_INTR_ENABLE_RHSC            0x00000040
+#define  OR_INTR_ENABLE_MIE             0x80000000
+                                            /* ---------------- HcRhDescriptorA Register ------------------ */
+#define  OR_RH_STATUS_LPSC              0x00010000
+#define  OR_RH_STATUS_DRWE              0x00008000
+                                            /* -------------- HcRhPortStatus[1:NDP] Register -------------- */
+#define  OR_RH_PORT_CCS                 0x00000001
+#define  OR_RH_PORT_PRS                 0x00000010
+#define  OR_RH_PORT_CSC                 0x00010000
+#define  OR_RH_PORT_PRSC                0x00100000
+
+
+/*
+**************************************************************************************************************
+*                                               FRAME INTERVAL
+**************************************************************************************************************
+*/
+
+#define  FI                     0x2EDF           /* 12000 bits per frame (-1)                               */
+#define  DEFAULT_FMINTERVAL     ((((6 * (FI - 210)) / 7) << 16) | FI)
+
+/*
+**************************************************************************************************************
+*                                       ENDPOINT DESCRIPTOR CONTROL FIELDS
+**************************************************************************************************************
+*/
+
+#define  ED_SKIP            (uint32_t) (0x00001000)        /* Skip this ep in queue                       */
+
+/*
+**************************************************************************************************************
+*                                       TRANSFER DESCRIPTOR CONTROL FIELDS
+**************************************************************************************************************
+*/
+
+#define  TD_ROUNDING        (uint32_t) (0x00040000)        /* Buffer Rounding                             */
+#define  TD_SETUP           (uint32_t)(0)                  /* Direction of Setup Packet                   */
+#define  TD_IN              (uint32_t)(0x00100000)         /* Direction In                                */
+#define  TD_OUT             (uint32_t)(0x00080000)         /* Direction Out                               */
+#define  TD_DELAY_INT(x)    (uint32_t)((x) << 21)          /* Delay Interrupt                             */
+#define  TD_TOGGLE_0        (uint32_t)(0x02000000)         /* Toggle 0                                    */
+#define  TD_TOGGLE_1        (uint32_t)(0x03000000)         /* Toggle 1                                    */
+#define  TD_CC              (uint32_t)(0xF0000000)         /* Completion Code                             */
+
+#define  ITD_SF             (uint32_t)(0x0000FFFF)         /* Starting Frame */
+#define  ITD_FC             (uint32_t)(0x07000000)         /* FrameCount */
+
+/*
+**************************************************************************************************************
+*                                       USB STANDARD REQUEST DEFINITIONS
+**************************************************************************************************************
+*/
+
+#define  USB_DESCRIPTOR_TYPE_DEVICE                     1
+#define  USB_DESCRIPTOR_TYPE_CONFIGURATION              2
+#define  USB_DESCRIPTOR_TYPE_STRING                     3
+#define  USB_DESCRIPTOR_TYPE_INTERFACE                  4
+#define  USB_DESCRIPTOR_TYPE_ENDPOINT                   5
+#define  USB_DESCRIPTOR_TYPE_HUB                     0x29
+                                                    /*  ----------- Control RequestType Fields  ----------- */
+#define  USB_DEVICE_TO_HOST         0x80
+#define  USB_HOST_TO_DEVICE         0x00
+#define  USB_REQUEST_TYPE_CLASS     0x20
+#define  USB_RECIPIENT_DEVICE       0x00
+#define  USB_RECIPIENT_INTERFACE    0x01
+#define  USB_RECIPIENT_OTHER        0x03
+
+                                                /* -------------- USB Standard Requests  -------------- */
+#define  GET_STATUS                  0
+#define  CLEAR_FEATURE               1
+#define  SET_FEATURE                 3
+#define  SET_ADDRESS                 5
+#define  GET_DESCRIPTOR              6
+#define  SET_CONFIGURATION           9
+#define  SET_INTERFACE              11
+
+/*
+**************************************************************************************************************
+*                                       TYPE DEFINITIONS
+**************************************************************************************************************
+*/
+
+typedef struct hcEd {                       /* ----------- HostController EndPoint Descriptor ------------- */
+    volatile  uint32_t  Control;              /* Endpoint descriptor control                              */
+    volatile  uint32_t  TailTd;               /* Physical address of tail in Transfer descriptor list     */
+    volatile  uint32_t  HeadTd;               /* Physcial address of head in Transfer descriptor list     */
+    volatile  uint32_t  Next;                 /* Physical address of next Endpoint descriptor             */
+} HCED;
+
+typedef struct hcTd {                       /* ------------ HostController Transfer Descriptor ------------ */
+    volatile  uint32_t  Control;              /* Transfer descriptor control                              */
+    volatile  uint32_t  CurrBufPtr;           /* Physical address of current buffer pointer               */
+    volatile  uint32_t  Next;                 /* Physical pointer to next Transfer Descriptor             */
+    volatile  uint32_t  BufEnd;               /* Physical address of end of buffer                        */
+} HCTD;
+
+typedef struct hcItd {               // HostController Isochronous Transfer Descriptor
+    volatile  uint32_t  Control;     // Transfer descriptor control
+    volatile  uint32_t  BufferPage0; // Buffer Page 0
+    volatile  uint32_t  Next;        // Physical pointer to next Transfer Descriptor
+    volatile  uint32_t  BufferEnd;   // buffer End
+    volatile  uint32_t  OffsetPSW10; // Offset1/PSW1 Offset0/PSW0
+    volatile  uint32_t  OffsetPSW32; // Offset3/PSW3 Offset2/PSW2
+    volatile  uint32_t  OffsetPSW54; // Offset5/PSW5 Offset4/PSW4
+    volatile  uint32_t  OffsetPSW76; // Offset7/PSW7 Offset6/PSW6
+} HCITD;
+
+typedef struct hcUtd {
+    union {
+        HCTD  hctd;
+        HCITD hcitd;
+    };
+    volatile uint32_t type;        // 1:TD, 2:ITD 
+    volatile uint32_t UsbEndpoint;
+    volatile uint32_t reserve1;
+    volatile uint32_t reserve2;
+} HCUTD;
+
+typedef struct hcca {                       /* ----------- Host Controller Communication Area ------------  */
+    volatile  uint32_t  IntTable[32];         /* Interrupt Table                                          */
+    volatile  uint32_t  FrameNumber;          /* Frame Number                                             */
+    volatile  uint32_t  DoneHead;             /* Done Head                                                */
+    volatile  uint8_t  Reserved[116];        /* Reserved for future use                                  */
+    volatile  uint8_t  Unknown[4];           /* Unused                                                   */
+} HCCA;
+
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/Usb_td.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,240 @@
+#include "mbed.h"
+#include "Usb_td.h"
+//#define __DEBUG
+#include "mydbg.h"
+
+#define __TEST
+
+#ifdef __TEST
+#include <queue>
+#endif
+
+template class tdqueue<HCTD*>;
+template class tdqueue<HCITD*>;
+
+HCTD* td_reverse(HCTD* td)
+{
+    HCTD* result = NULL;
+    HCTD* next;
+    while(td) {
+        next = (HCTD*)td->Next;
+        td->Next = (uint32_t)result;
+        result = td;
+        td = next;
+    }
+    return result;
+}
+
+template <class T>
+tdqueue<T>::tdqueue():m_head(NULL),m_tail(NULL)
+{
+
+}
+
+template <class T>
+int tdqueue<T>::size()
+{
+    int n = 0;
+    T td = m_head;
+    while(td) {
+        td = (T)td->Next;
+        n++;
+    }
+    return n;
+}
+
+template <class T>
+bool tdqueue<T>::empty()
+{
+    return (m_head == NULL);
+}
+
+template <class T>
+T tdqueue<T>::front()
+{
+    return m_head;
+}
+
+template <class T>
+void tdqueue<T>::pop()
+{
+    T td = m_head;
+    if (td) {
+        m_head = (T)td->Next;
+    }
+    if (td == m_tail) {
+        m_tail = NULL;
+    }
+}
+
+template <class T>
+void tdqueue<T>::push(T td)
+{
+    td->Next = NULL;
+    if (m_tail) {
+        m_tail->Next = (uint32_t)td;
+    }
+    m_tail = td;
+    if (m_head == NULL) {
+        m_head = td;
+    }
+}
+
+#ifdef __TEST
+static void test_td_list()
+{
+    tdqueue<HCTD*> list;
+    HCTD* td1;
+    HCTD* td2;
+    HCTD* td3;
+    HCTD* td4;
+    // 0
+    DBG_ASSERT(list.size() == 0);
+    td1 = list.front();
+    DBG_ASSERT(td1  == NULL);
+    list.pop();
+    td1 = list.front();
+    DBG_ASSERT(td1 == NULL);
+    // 1   
+    td1 = (HCTD*)usb_get_td(1);
+    list.push(td1);
+    DBG_ASSERT(list.size() == 1);
+    td2 = list.front();
+    DBG_ASSERT(td2 == td1);
+    list.pop();
+    td2 = list.front();
+    DBG_ASSERT(td2 == NULL);
+    DBG_ASSERT(list.size() == 0);
+    usb_free_td((byte*)td1);
+    // 2
+    td1 = (HCTD*)usb_get_td(1);
+    td2 = (HCTD*)usb_get_td(2);
+    list.push(td1);
+    list.push(td2);
+    DBG_ASSERT(list.size() == 2);
+    td3 = list.front();
+    DBG_ASSERT(td3 == td1);
+    list.pop();
+    td3 = list.front();
+    DBG_ASSERT(td3 == td2);
+    list.pop();
+    td3 = list.front();
+    DBG_ASSERT(td3 == NULL); 
+    usb_free_td((byte*)td1);
+    usb_free_td((byte*)td2);
+    // 3 
+    td1 = (HCTD*)usb_get_td(1);
+    td2 = (HCTD*)usb_get_td(2);
+    td3 = (HCTD*)usb_get_td(3);
+    list.push(td1);
+    list.push(td2);
+    list.push(td3);
+    DBG_ASSERT(list.size() == 3);
+    td4 = list.front();
+    DBG_ASSERT(td4 == td1);
+    list.pop();
+    td4 = list.front();
+    DBG_ASSERT(td4 == td2);
+    list.pop();
+    td4 = list.front();
+    DBG_ASSERT(td4 == td3);
+    list.pop();
+    td4 = list.front();
+    DBG_ASSERT(td4 == NULL);    
+    usb_free_td((byte*)td1);
+    usb_free_td((byte*)td2);
+    usb_free_td((byte*)td3);
+    // n
+    queue<HCTD*> queue;
+    for(int n = 1; n <= 10; n++) {
+        DBG_ASSERT(list.size() == queue.size());
+        DBG_ASSERT(list.empty() == queue.empty());
+        if (list.empty() || (rand()%10) > 5) {
+            td1 = (HCTD*)usb_get_td(n);
+            if (td1) {
+                list.push(td1);
+                queue.push(td1);
+            }
+            //DBG("+ %d %p\n", n, td1);
+        } else {
+            td1 = list.front();
+            td2 = queue.front();
+            if (td1 != td2) {
+                //DBG("td1=%p td2=%p\n", td1, td2);
+            }
+            DBG_ASSERT(td1 == td2);
+            if (td1) {
+                list.pop();
+                queue.pop();
+                usb_free_td((byte*)td1);
+            }
+            //DBG("- %d %p\n", n, td1);
+        }
+    }
+    while(!list.empty()) {
+        td1 = list.front();
+        list.pop();
+        usb_free_td((byte*)td1);
+    }
+    //DBG_ASSERT(0);    
+}
+
+static void test_td_reverse()
+{
+
+    HCTD* td1;
+    HCTD* td2;
+    HCTD* td3;
+    HCTD* td4;
+    // 0
+    td1 = NULL;
+    td2 = td_reverse(td1);
+    DBG_ASSERT(td2 == NULL);
+    // 1
+    td1 = (HCTD*)usb_get_td(1);
+    DBG_ASSERT(td1);
+    DBG_ASSERT(td1->Next == NULL);
+    td2 = td_reverse(td1);
+    DBG_ASSERT(td2 == td1);
+    DBG_ASSERT(td2->Next == NULL);
+    usb_free_td((byte*)td1);
+    // 2
+    td1 = (HCTD*)usb_get_td(1);
+    DBG_ASSERT(td1);
+    td2 = (HCTD*)usb_get_td(2);
+    DBG_ASSERT(td2);
+    td1->Next = (uint32_t)td2;
+    td3 = td_reverse(td1);
+    DBG_ASSERT(td3 == td2);
+    DBG_ASSERT(td3->Next == (uint32_t)td1);
+    DBG_ASSERT(td1->Next == NULL);
+    usb_free_td((byte*)td1);
+    usb_free_td((byte*)td2);
+    // 3
+    td1 = (HCTD*)usb_get_td(1);
+    td2 = (HCTD*)usb_get_td(2);
+    td3 = (HCTD*)usb_get_td(3);
+    td1->Next = (uint32_t)td2;
+    td2->Next = (uint32_t)td3;
+    td4 = td_reverse(td1);
+    DBG_ASSERT(td4 == td3);
+    DBG_ASSERT(td3->Next == (uint32_t)td2);
+    DBG_ASSERT(td2->Next == (uint32_t)td1);
+    DBG_ASSERT(td1->Next == NULL);
+    usb_free_td((byte*)td1);
+    usb_free_td((byte*)td2);
+    usb_free_td((byte*)td3);
+}
+
+void test_td()
+{
+    test_td_reverse();
+    test_td_list();
+    DBG("Usb_td.cpp TD test done.\n");
+}
+#else  //__TEST
+void test_td()
+{
+
+}
+#endif //__TEST
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/Usb_td.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,23 @@
+#ifndef USB_TD_H
+#define USB_TD_H
+#include "UsbInc.h"
+#include "usb_mem.h"
+
+template <class T> 
+class tdqueue {
+public:
+    tdqueue();
+    int size();
+    bool empty();
+    T front();
+    void pop();
+    void push(T td);
+private:
+    T m_head;
+    T m_tail;
+};
+
+HCTD* td_reverse(HCTD* td);
+void test_td();
+
+#endif //USB_TD_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/Utils.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,48 @@
+
+
+#include "mbed.h"
+#include "Utils.h"
+
+void printfBytes(const char* s, const u8* data, int len)
+{
+    printf("%s %d:",s,len);
+    if (len > 256)
+        len = 256;
+    while (len-- > 0)
+        printf(" %02X",*data++);
+    printf("\n");
+}
+
+void printHexLine(const u8* d, int addr, int len)
+{
+    printf("%04X ",addr);
+    int i;
+    for (i = 0; i < len; i++)
+        printf("%02X ",d[i]);
+    for (;i < 16; i++)
+        printf("   ");
+    char s[16+1];
+    memset(s,0,sizeof(s));
+    for (i = 0; i < len; i++)
+    {
+        int c = d[i];
+        if (c < 0x20 || c > 0x7E)
+            c = '.';
+        s[i] = c;
+    }
+    printf("%s\n",s);
+}
+
+void printHex(const u8* d, int len)
+{
+    int addr = 0;
+    while (len)
+    {
+        int count = len;
+        if (count > 16)
+            count = 16;
+        printHexLine(d+addr,addr,count);
+        addr += 16;
+        len -= count;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/Utils.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,48 @@
+#ifndef UTILS_H
+#define UTILS_H
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned long u32;
+
+//void DelayMS(int ms);
+
+void printfBytes(const char* label,const u8* data, int len);
+void printHex(const u8* d, int len);
+
+#ifndef min
+#define min(_a,_b) ((_a) < (_b) ? (_a) : (_b))
+#endif
+
+inline int LE16(const u8* d)
+{
+    return d[0] | (d[1] << 8);
+}
+
+
+inline int LE24(const uint8_t* d) {
+    return d[0] | (d[1]<<8) | (d[2] << 16);
+}
+
+inline int LE32(const uint8_t* d) {
+    return d[0] |(d[1]<<8) | (d[2] << 16) |(d[3] << 24) ;
+}
+
+inline u32 BE32(const u8* d)
+{
+    return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+inline void BE32(u32 n, u8* d)
+{
+    d[0] = (u8)(n >> 24);
+    d[1] = (u8)(n >> 16);
+    d[2] = (u8)(n >> 8);
+    d[3] = (u8)n;
+}
+
+inline void BE16(u32 n, u8* d)
+{
+    d[0] = (u8)(n >> 8);
+    d[1] = (u8)n;
+}
+#endif //UTILS_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/mydbg.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,34 @@
+#ifndef _MYDBG_H_
+#define _MYDBG_H_
+#ifdef __DEBUG
+#include "Utils.h"
+#define DBG(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);} while(0);
+#define DBG2(...) do{fprintf(stderr,"[%d] ",__LINE__);fprintf(stderr,__VA_ARGS__);} while(0);
+#define DBG_BYTES(A,B,C) do{printf("[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);printfBytes(A,B,C);}while(0);
+#define DBG_HEX(A,B) do{printf("[%s@%d]\n",__PRETTY_FUNCTION__,__LINE__);printHex(A,B);}while(0);
+#define DBG_LED4(A) led4.write(A)
+#define DBG_PRINTF(...) do{fprintf(stderr,__VA_ARGS__);} while(0);
+#else //__DEBUG
+#define DBG(...)  while(0);
+#define DBG2(...)  while(0);
+#define DBG_BYTES(A,B,C) while(0);
+#define DBG_HEX(A,B)  while(0);
+#define DBG_LED4(A) while(0);
+#define DBG_PRINTF(...)  while(0);
+#endif //__DEBUG
+#ifdef __DEBUG3
+#define DBG3(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);} while(0);
+
+#else //__DEBUG3
+#define DBG3(...)  while(0);
+#endif //__DEBUG3
+
+#ifndef __NODEBUG
+#define DBG_ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);};
+#else
+#define DBG_ASSERT(A)  while(0);
+#endif
+
+#define VERBOSE(...) do{printf(__VA_ARGS__);} while(0);
+
+#endif //_MYDBG_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/netCfg.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,11 @@
+#ifndef NET_CFG_H
+#define NET_GPRS 1
+#define NET_PPP 1
+#define NET_GPRS_MODULE 1
+#define NET_ETH 1
+#define NET_USB_SERIAL 1
+#define NET_CFG_H 1
+#define NET_UMTS 1
+#define NET_USB 1
+#define NET_LWIP_STACK 1
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/usb_mem.c	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,260 @@
+
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ 
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+ 
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#include "mbed.h"
+//#define __DEBUG
+#include "mydbg.h"
+#include "usb_mem.h"
+#include "string.h" //For memcpy, memmove, memset
+//#include "netCfg.h"
+#include "UsbInc.h"
+
+//#if NET_USB
+
+#define EDS_COUNT 16
+#define TDS_COUNT  0
+#define ITDS_COUNT 0
+#define UTDS_COUNT 32
+#define BPS_COUNT 8
+
+#define HCCA_SIZE 0x100
+#define ED_SIZE 0x10
+#define TD_SIZE 0x10
+#define ITD_SIZE 0x20
+#define UTD_SIZE (32+16)
+#define BP_SIZE  (128*8)
+
+#define TOTAL_SIZE (HCCA_SIZE + (EDS_COUNT*ED_SIZE) + (TDS_COUNT*TD_SIZE) + (ITDS_COUNT*ITD_SIZE))
+
+static volatile __align(256) byte usb_buf[TOTAL_SIZE] __attribute((section("AHBSRAM0"),aligned));  //256 bytes aligned!
+static volatile __align(32) uint8_t usb_utdBuf[UTDS_COUNT*UTD_SIZE] __attribute((section("AHBSRAM0"),aligned));
+
+static volatile byte* usb_hcca;  //256 bytes aligned!
+
+static volatile byte* usb_edBuf;  //4 bytes aligned!
+
+static byte usb_edBufAlloc[EDS_COUNT] __attribute((section("AHBSRAM0"),aligned));
+static uint8_t usb_utdBufAlloc[UTDS_COUNT] __attribute((section("AHBSRAM0"),aligned));
+static uint8_t usb_bpBufAlloc[BPS_COUNT] __attribute((section("AHBSRAM0"),aligned));
+static uint8_t usb_bpBuf[BP_SIZE*BPS_COUNT] __attribute((section("AHBSRAM0"),aligned));
+
+static void utd_init()
+{
+    DBG_ASSERT(sizeof(HCTD) == 16);
+    DBG_ASSERT(sizeof(HCITD) == 32);
+    DBG_ASSERT(sizeof(HCUTD) == 48);
+
+    DBG_ASSERT(((uint32_t)usb_utdBuf % 16) == 0);
+    DBG_ASSERT(((uint32_t)usb_utdBuf % 32) == 0);
+
+    DBG_ASSERT((uint32_t)usb_utdBufAlloc >= 0x2007c000);
+    DBG_ASSERT((uint32_t)&usb_utdBufAlloc[UTDS_COUNT] <= 0x2007ffff);
+
+    DBG_ASSERT((uint32_t)usb_utdBuf >= 0x2007c000);
+    DBG_ASSERT((uint32_t)&usb_utdBuf[UTD_SIZE*UTDS_COUNT] <= 0x2007cfff);
+
+    memset(usb_utdBufAlloc, 0x00, UTDS_COUNT);
+}
+
+static void pb_init()
+{
+    memset(usb_bpBufAlloc, 0x00, BPS_COUNT);
+
+    DBG_ASSERT((uint32_t)usb_bpBufAlloc >= 0x2007c000);
+    DBG_ASSERT((uint32_t)&usb_bpBufAlloc[BPS_COUNT] <= 0x2007ffff);
+    DBG_ASSERT((uint32_t)usb_bpBuf >= 0x2007c000);
+    DBG_ASSERT((uint32_t)(&usb_bpBuf[BP_SIZE*BPS_COUNT]) <= 0x2007ffff);
+}
+
+void usb_mem_init()
+{
+  usb_hcca = usb_buf;
+  usb_edBuf = usb_buf + HCCA_SIZE;
+  memset(usb_edBufAlloc, 0, EDS_COUNT);
+
+  utd_init();
+  pb_init();
+
+  DBG("--- Memory Map --- \n");
+  DBG("usb_hcca       =%p\n", usb_hcca);
+  DBG("usb_edBuf      =%p\n", usb_edBuf);
+  DBG("usb_utdBuf     =%p\n", usb_utdBuf);
+  DBG("usb_edBufAlloc =%p\n", usb_edBufAlloc);
+  DBG("usb_utdBufAlloc=%p\n", usb_utdBufAlloc);
+  DBG("usb_bpBufAlloc =%p\n", usb_bpBufAlloc);
+  DBG("usb_bpBuf      =%p\n", usb_bpBuf);
+  DBG("               =%p\n", &usb_bpBuf[BP_SIZE*BPS_COUNT]);
+  DBG_ASSERT(((uint32_t)usb_hcca % 256) == 0);
+  DBG_ASSERT(((uint32_t)usb_edBuf % 16) == 0);
+  DBG_ASSERT(((uint32_t)usb_utdBuf % 32) == 0);
+}
+
+volatile byte* usb_get_hcca()
+{
+  return usb_hcca;
+}
+
+volatile byte* usb_get_ed()
+{
+  int i;
+  for(i = 0; i < EDS_COUNT; i++)
+  {
+    if( !usb_edBufAlloc[i] )
+    {
+      usb_edBufAlloc[i] = 1;
+      return usb_edBuf + i*ED_SIZE;
+    }
+  }
+  return NULL; //Could not alloc ED
+}
+
+static uint8_t* usb_get_utd(int al)
+{
+    DBG_ASSERT(al == 16 || al == 32); // GTD or ITD
+    if (al == 16) {
+        for(int i = 1; i < UTDS_COUNT; i += 2) {
+            if (usb_utdBufAlloc[i] == 0) {
+                uint32_t p = (uint32_t)usb_utdBuf + i * UTD_SIZE;
+                if ((p % al) == 0) {
+                    usb_utdBufAlloc[i] = 1;
+                    DBG_ASSERT((p % al) == 0);
+                    return (uint8_t*)p;
+                }
+            }
+        }
+    }
+    for(int i = 0; i < UTDS_COUNT; i++) {
+        if (usb_utdBufAlloc[i] == 0) {
+            uint32_t p = (uint32_t)usb_utdBuf + i * UTD_SIZE;
+            if ((p % al) == 0) {
+                usb_utdBufAlloc[i] = 1;
+                DBG_ASSERT((p % al) == 0);
+                return (uint8_t*)p;
+            }
+        }
+    }
+    return NULL;
+}
+
+volatile byte* usb_get_td(uint32_t endpoint)
+{
+    DBG_ASSERT(endpoint);
+    uint8_t* td = usb_get_utd(16);
+    if (td) {
+        HCUTD* utd = (HCUTD*)td;
+        memset(utd, 0x00, sizeof(HCTD));
+        utd->type = 1;
+        utd->UsbEndpoint = endpoint;
+    }
+    return td;
+}
+
+volatile byte* usb_get_itd(uint32_t endpoint)
+{
+    DBG_ASSERT(endpoint);
+    uint8_t* itd = usb_get_utd(32);
+    if (itd) {
+        HCUTD* utd = (HCUTD*)itd;
+        memset(utd, 0x00, sizeof(HCITD));
+        utd->type = 2;
+        utd->UsbEndpoint = endpoint;
+    }
+    return itd;
+}
+
+volatile byte* usb_get_bp(int size)
+{
+  DBG_ASSERT(size >= 128 && size <= BP_SIZE);
+  if (size > BP_SIZE) {
+      return NULL;
+  }    
+  for(int i = 0; i < BPS_COUNT; i++)
+  {
+    if( !usb_bpBufAlloc[i] )
+    {
+      usb_bpBufAlloc[i] = 1;
+      return usb_bpBuf + i*BP_SIZE;
+    }
+  }
+  return NULL; //Could not alloc Buffer Page
+}
+
+int usb_bp_size()
+{
+    return BP_SIZE; 
+}
+
+void usb_free_ed(volatile byte* ed)
+{
+  int i;
+  i = (ed - usb_edBuf) / ED_SIZE;
+  usb_edBufAlloc[i] = 0;
+}
+
+static void usb_free_utd(volatile uint8_t* utd)
+{
+  DBG_ASSERT(utd >= usb_utdBuf);
+  DBG_ASSERT(utd <= (usb_utdBuf+UTD_SIZE*(UTDS_COUNT-1)));
+  DBG_ASSERT(((uint32_t)utd % 16) == 0);
+  int i = (utd - usb_utdBuf) / UTD_SIZE;
+  DBG_ASSERT(usb_utdBufAlloc[i] == 1);
+  usb_utdBufAlloc[i] = 0;
+}
+
+void usb_free_td(volatile byte* td)
+{
+    usb_free_utd(td);
+    return;
+}
+
+void usb_free_itd(volatile byte* itd)
+{
+    usb_free_utd(itd);
+    return;
+}
+
+void usb_free_bp(volatile byte* bp)
+{
+  DBG_ASSERT(bp >= usb_bpBuf);
+  int i;
+  i = (bp - usb_bpBuf) / BP_SIZE;
+  DBG_ASSERT(usb_bpBufAlloc[i] == 1);
+  usb_bpBufAlloc[i] = 0;
+}
+
+bool usb_is_td(volatile byte* td)
+{
+    DBG_ASSERT(td);
+    HCUTD* utd = (HCUTD*)td;
+    DBG_ASSERT(utd->type != 0);
+    return utd->type == 1;
+}
+
+bool usb_is_itd(volatile byte* itd)
+{
+    DBG_ASSERT(itd);
+    HCUTD* utd = (HCUTD*)itd;
+    DBG_ASSERT(utd->type != 0);
+    return utd->type == 2;
+}
+
+//#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usb/usb_mem.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,59 @@
+/*
+Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
+ 
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+ 
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+ 
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef USB_MEM_H
+#define USB_MEM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned char byte;
+
+void usb_mem_init();
+
+volatile byte* usb_get_hcca();
+
+volatile byte* usb_get_ed();
+
+volatile byte* usb_get_td(uint32_t endpoint);
+volatile byte* usb_get_itd(uint32_t endpoint);
+
+volatile byte* usb_get_bp(int size);
+int usb_bp_size();
+
+void usb_free_ed(volatile byte* ed);
+
+void usb_free_td(volatile byte* td);
+
+void usb_free_itd(volatile byte* itd);
+
+void usb_free_bp(volatile byte* bp);
+
+bool usb_is_td(volatile byte* td);
+bool usb_is_itd(volatile byte* td);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbbt/bd_addr.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,33 @@
+#include "mbed.h"
+#include "bd_addr.h"
+bd_addr::bd_addr()
+{
+
+}
+
+bd_addr::bd_addr(char* s)
+{
+    for(int i = 0; i <= 5; i++) {
+        ad[i] = strtol(s+i*3, NULL, 16);
+    }
+}
+
+uint8_t* bd_addr::data(uint8_t* addr)
+{
+    if (addr != NULL) {
+        ad[5] = addr[0];
+        ad[4] = addr[1];
+        ad[3] = addr[2];
+        ad[2] = addr[3];
+        ad[1] = addr[4];
+        ad[0] = addr[5];
+    }
+    return ad;
+}
+
+char* bd_addr::to_str()
+{
+    snprintf(ad_str, sizeof(ad_str), "%02X:%02X:%02X:%02X:%02X:%02X", 
+        ad[0], ad[1], ad[2], ad[3], ad[4], ad[5]);
+    return ad_str;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbbt/bd_addr.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,13 @@
+#ifndef BD_ADDR_H
+#define BD_ADDR_H
+class bd_addr {
+public:
+    bd_addr();
+    bd_addr(char* s);
+    uint8_t* data(uint8_t* addr = NULL);
+    char* to_str();
+private:
+    char ad_str[18];
+    uint8_t ad[6];
+};
+#endif //BD_ADDR_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbbt/usbbt.cpp	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,220 @@
+#include "usbbt.h"
+#define __DEBUG
+#include "mydbg.h"
+#include "Utils.h"
+
+usbbt::usbbt(int dongle)
+    : m_dongle(dongle),m_pEpIntIn(NULL),m_pEpBulkIn(NULL),m_pEpBulkOut(NULL),
+    m_int_seq(0),m_bulk_seq(0)
+{
+
+}
+
+int usbbt::setup(int timeout)
+{
+    for(int i = 0; i < 2; i++) {
+        m_pDev = m_pHost->getDeviceByClass(0xe0, m_dongle); 
+        if (m_pDev || i > 0) {
+            break;
+        }
+        UsbErr rc = Usb_poll();
+        if (rc == USBERR_PROCESSING) {
+            VERBOSE("%p USBERR_PROCESSING\n", this);
+            return -1;
+        }
+    }
+    DBG("m_pDev=%p\n", m_pDev);
+    if (m_pDev == NULL) {
+        VERBOSE("%p Bluetooth dongle(%d) NOT FOUND\n", this, m_dongle);
+        return -1;
+    }
+    DBG_ASSERT(m_pDev);
+
+    ParseConfiguration();
+    return 0;
+}
+
+int usbbt::ParseConfiguration()
+{
+  UsbErr rc;
+  uint8_t ConfigDesc[9];
+  int index = 0;
+  DBG_ASSERT(m_pDev);
+  rc = m_pDev->GetDescriptor(USB_DESCRIPTOR_TYPE_CONFIGURATION, index, ConfigDesc, sizeof(ConfigDesc));
+  DBG_ASSERT(rc == USBERR_OK);
+  DBG_BYTES("ConfigDescriptor 9bytes", ConfigDesc, sizeof(ConfigDesc));
+  DBG_ASSERT(ConfigDesc[0] == 9);
+  DBG_ASSERT(ConfigDesc[1] == 0x02);
+  int wTotalLength = *((uint16_t*)&ConfigDesc[2]);
+  DBG("TotalLength: %d\n", wTotalLength);
+  int bConfigValue = ConfigDesc[5];
+  DBG_ASSERT(bConfigValue == 1);
+  DBG("ConfigValue: %d\n", bConfigValue);
+  VERBOSE("MaxPower: %d mA\n", ConfigDesc[8]*2);   
+
+  uint8_t* buf = new uint8_t[wTotalLength];
+  DBG_ASSERT(buf);
+  rc = m_pDev->GetDescriptor(USB_DESCRIPTOR_TYPE_CONFIGURATION, index, buf, wTotalLength);
+  DBG_ASSERT(rc == USBERR_OK);
+  DBG_ASSERT(ConfigDesc[1] == 0x02);
+  for (int pos = 0; pos < wTotalLength; pos += buf[pos]) {
+      DBG_BYTES("CFG", buf+pos, buf[pos]);
+      int type = buf[pos+1];
+      if (USB_DESCRIPTOR_TYPE_INTERFACE == type) { // 0x04
+        DBG("InterfaceNumber: %d\n", buf[pos+2]);
+        DBG("AlternateSetting: %d\n", buf[pos+3]);
+        DBG("NumEndpoint: %d\n", buf[pos+4]);
+        VERBOSE("InterfaceClass: %02X\n", buf[pos+5]);
+        VERBOSE("InterfaceSubClass: %02X\n", buf[pos+6]);
+        VERBOSE("InterfaceProtocol: %02X\n", buf[pos+7]);
+        DBG_ASSERT(buf[pos+6] == 0x01);
+        DBG_ASSERT(buf[pos+7] == 0x01);
+      } 
+      if (USB_DESCRIPTOR_TYPE_ENDPOINT == type) {
+          DBG_ASSERT(buf[pos] == 7);
+          uint8_t att = buf[pos+3];
+          uint8_t ep = buf[pos+2];
+          bool dir = ep & 0x80; // true=IN
+          uint16_t size = LE16(buf+pos+4);
+          DBG("EndpointAddress: %02X\n", ep);
+          DBG("Attribute: %02X\n", att);
+          DBG("MaxPacketSize: %d\n", size); 
+          UsbEndpoint* pEp = new UsbEndpoint(m_pDev, ep, dir, att == 3 ? USB_INT : USB_BULK, size);
+          DBG_ASSERT(pEp);
+          if (att == 3) { // interrupt
+              if (m_pEpIntIn == NULL) {
+                  m_pEpIntIn = pEp;
+              }
+          } else if (att == 2) { // bulk
+              if (dir) {
+                  if (m_pEpBulkIn == NULL) {
+                      m_pEpBulkIn = pEp;
+                  }
+              } else {
+                  if (m_pEpBulkOut == NULL) {
+                      m_pEpBulkOut = pEp;
+                  }
+              } 
+          }
+      }
+      if (m_pEpIntIn && m_pEpBulkIn && m_pEpBulkOut) { // cut off
+          break;
+      }
+  }
+  delete[] buf;
+  DBG_ASSERT(m_pEpIntIn);
+  DBG_ASSERT(m_pEpBulkIn);
+  DBG_ASSERT(m_pEpBulkOut);
+  return 0;   
+}
+
+int usbbt::send_packet(uint8_t packet_type, uint8_t* packet, int size)
+{
+    //DBG("\npacket_type=%d\n", packet_type);
+    //DBG_HEX(packet, size);
+
+    int rc;
+    switch(packet_type){
+        case HCI_COMMAND_DATA_PACKET:
+            DBG_ASSERT(m_pDev);
+            DBG_BYTES("\nCMD", packet, size);
+            rc = m_pDev->controlSend(
+                    USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_DEVICE, 
+                    0, 0, 0, packet, size);
+            DBG_ASSERT(rc == USBERR_OK);
+            return 0;
+        case HCI_ACL_DATA_PACKET:
+            DBG_ASSERT(m_pEpBulkOut);
+            DBG_BYTES("\nACL", packet, size);
+            rc = m_pEpBulkOut->transfer(packet, size);
+            DBG_ASSERT(rc == USBERR_PROCESSING);
+            while(m_pEpBulkOut->status() == USBERR_PROCESSING){
+                wait_us(1);
+            }
+            return 0;
+        default:
+            DBG_ASSERT(0);
+            return -1;
+    }
+}
+
+
+void usbbt::poll()
+{
+    //DBG("m_int_seq=%d\n", m_int_seq);
+    int rc, len;
+    switch(m_int_seq) {
+        case 0:
+            m_int_seq++;
+            break;
+        case 1:
+            rc = m_pEpIntIn->transfer(m_int_buf, sizeof(m_int_buf));
+            DBG_ASSERT(rc == USBERR_PROCESSING);
+            m_int_seq++;
+            break;
+        case 2:
+            len = m_pEpIntIn->status();
+            if (len == USBERR_PROCESSING) {
+                break;
+            }
+            if (len >= 0) {
+                //DBG("len=%d\n", len);
+                //DBG_HEX(m_int_buf, len);
+                onPacket(HCI_EVENT_PACKET, m_int_buf, len);
+                m_int_seq = 0;
+                break;
+            }
+            DBG_ASSERT(0);
+            break;
+    } 
+
+    switch(m_bulk_seq) {
+        case 0:
+            m_bulk_seq++;
+            break;
+        case 1:
+            rc = m_pEpBulkIn->transfer(m_bulk_buf, sizeof(m_bulk_buf));
+            DBG_ASSERT(rc == USBERR_PROCESSING);
+            m_bulk_seq++;
+            break;
+        case 2:
+            len = m_pEpBulkIn->status();
+            if (len == USBERR_PROCESSING) {
+                break;
+            }
+            if (len >= 0) {
+                //DBG("len=%d\n", len);
+                //DBG_HEX(m_bulk_buf, len);
+                onPacket(HCI_ACL_DATA_PACKET, m_bulk_buf, len);
+                m_bulk_seq = 0;
+                break;
+            }
+            DBG_ASSERT(0);
+            break;
+    } 
+}
+
+void usbbt::onPacket(uint8_t packet_type, uint8_t* packet, uint16_t size)
+{
+  DBG("\npacket_type=%d packet=%p size=%d\n", packet_type, packet, size);
+  DBG_HEX(packet, size);
+
+  if(m_pCbItem && m_pCbMeth)
+    (m_pCbItem->*m_pCbMeth)(packet_type, packet, size);
+  else if(m_pCb)
+    m_pCb(packet_type, packet, size);
+}
+
+void usbbt::setOnPacket( void (*pMethod)(uint8_t, uint8_t*, uint16_t) )
+{
+  m_pCb = pMethod;
+  m_pCbItem = NULL;
+  m_pCbMeth = NULL;
+}
+
+void usbbt::clearOnPacket()
+{
+  m_pCb = NULL;
+  m_pCbItem = NULL;
+  m_pCbMeth = NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbbt/usbbt.h	Fri May 10 11:48:07 2013 +0000
@@ -0,0 +1,55 @@
+#ifndef USBBT_H
+#define USBBT_H
+#include "UsbHostMgr.h"
+#include "UsbEndpoint.h"
+#include "UsbBaseClass.h"
+
+#define HCI_COMMAND_DATA_PACKET    0x01
+#define HCI_ACL_DATA_PACKET        0x02
+#define HCI_SCO_DATA_PACKET        0x03
+#define HCI_EVENT_PACKET        0x04
+
+class usbbt : public UsbBaseClass {
+public:
+    usbbt(int dongle = 0);
+    int setup(int timeout = 9000);
+    int send_packet(uint8_t packet_type, uint8_t* packet, int size);
+    void poll();
+    ///Setups the result callback
+    /**
+     @param pMethod : callback function
+     */
+    void setOnPacket( void (*pMethod)(uint8_t, uint8_t*, uint16_t) );
+  
+    ///Setups the result callback
+    /**
+    @param pItem : instance of class on which to execute the callback method
+    @param pMethod : callback method
+    */
+    class CDummy;
+    template<class T> 
+    void setOnPacket( T* pItem, void (T::*pMethod)(uint8_t, uint8_t*, uint16_t) )
+    {
+        m_pCb = NULL;
+        m_pCbItem = (CDummy*) pItem;
+        m_pCbMeth = (void (CDummy::*)(uint8_t, uint8_t*, uint16_t)) pMethod;
+    }
+    void clearOnPacket();
+private:
+    int ParseConfiguration();
+    void onPacket(uint8_t packet_type, uint8_t* packet, uint16_t size);
+    int m_dongle;
+    UsbDevice* m_pDev;
+    UsbEndpoint* m_pEpIntIn;
+    UsbEndpoint* m_pEpBulkIn;
+    UsbEndpoint* m_pEpBulkOut;
+    Timer m_timer;
+    int m_int_seq;
+    uint8_t m_int_buf[64];
+    int m_bulk_seq;
+    uint8_t m_bulk_buf[64];
+    CDummy* m_pCbItem;
+    void (CDummy::*m_pCbMeth)(uint8_t, uint8_t*, uint16_t);
+    void (*m_pCb)(uint8_t, uint8_t*, uint16_t);
+};
+#endif //USBBT_H
\ No newline at end of file