Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: FatFileSystem HighSpeedAnalogIn TB6612FNG2 mbed
Revision 0:373bcb197dc8, committed 2013-05-10
- Comitter:
- jksoft
- Date:
- Fri May 10 11:48:07 2013 +0000
- Commit message:
- ?????????
Changed in this revision
--- /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