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.
Fork of DISCO-L072CZ-LRWAN1_LoRa_PingPong by
Revision 12:02d779e8c4f6, committed 2018-05-19
- Comitter:
- wardm
- Date:
- Sat May 19 11:41:10 2018 +0000
- Parent:
- 11:9d7409ebfa57
- Commit message:
- Code for Technion Formula car sensors reader transmit
Changed in this revision
--- a/BufferedSerial.lib Fri Aug 18 07:45:44 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://mbed.org/users/sam_grove/code/BufferedSerial/#a0d37088b405
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BufferedSerial/Buffer.lib Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,1 @@ +https://mbed.org/users/sam_grove/code/Buffer/#89564915f2a7
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BufferedSerial/BufferedSerial.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,158 @@ +/** + * @file BufferedSerial.cpp + * @brief Software Buffer - Extends mbed Serial functionallity adding irq driven TX and RX + * @author sam grove + * @version 1.0 + * @see + * + * Copyright (c) 2013 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "BufferedSerial.h" +#include <stdarg.h> + +BufferedSerial::BufferedSerial(PinName tx, PinName rx, uint32_t buf_size, uint32_t tx_multiple, const char* name) + : RawSerial(tx, rx) , _rxbuf(buf_size), _txbuf((uint32_t)(tx_multiple*buf_size)) +{ + RawSerial::attach(this, &BufferedSerial::rxIrq, Serial::RxIrq); + this->_buf_size = buf_size; + this->_tx_multiple = tx_multiple; + return; +} + +BufferedSerial::~BufferedSerial(void) +{ + RawSerial::attach(NULL, RawSerial::RxIrq); + RawSerial::attach(NULL, RawSerial::TxIrq); + + return; +} + +int BufferedSerial::readable(void) +{ + return _rxbuf.available(); // note: look if things are in the buffer +} + +int BufferedSerial::writeable(void) +{ + return 1; // buffer allows overwriting by design, always true +} + +int BufferedSerial::getc(void) +{ + return _rxbuf; +} + +int BufferedSerial::putc(int c) +{ + _txbuf = (char)c; + BufferedSerial::prime(); + + return c; +} + +int BufferedSerial::puts(const char *s) +{ + if (s != NULL) { + const char* ptr = s; + + while(*(ptr) != 0) { + _txbuf = *(ptr++); + } + _txbuf = '\n'; // done per puts definition + BufferedSerial::prime(); + + return (ptr - s) + 1; + } + return 0; +} + +int BufferedSerial::printf(const char* format, ...) +{ + char buffer[this->_buf_size]; + memset(buffer,0,this->_buf_size); + int r = 0; + + va_list arg; + va_start(arg, format); + r = vsprintf(buffer, format, arg); + // this may not hit the heap but should alert the user anyways + if(r > this->_buf_size) { + error("%s %d buffer overwrite (max_buf_size: %d exceeded: %d)!\r\n", __FILE__, __LINE__,this->_buf_size,r); + va_end(arg); + return 0; + } + va_end(arg); + r = BufferedSerial::write(buffer, r); + + return r; +} + +ssize_t BufferedSerial::write(const void *s, size_t length) +{ + if (s != NULL && length > 0) { + const char* ptr = (const char*)s; + const char* end = ptr + length; + + while (ptr != end) { + _txbuf = *(ptr++); + } + BufferedSerial::prime(); + + return ptr - (const char*)s; + } + return 0; +} + + +void BufferedSerial::rxIrq(void) +{ + // read from the peripheral and make sure something is available + if(serial_readable(&_serial)) { + _rxbuf = serial_getc(&_serial); // if so load them into a buffer + } + + return; +} + +void BufferedSerial::txIrq(void) +{ + // see if there is room in the hardware fifo and if something is in the software fifo + while(serial_writable(&_serial)) { + if(_txbuf.available()) { + serial_putc(&_serial, (int)_txbuf.get()); + } else { + // disable the TX interrupt when there is nothing left to send + RawSerial::attach(NULL, RawSerial::TxIrq); + break; + } + } + + return; +} + +void BufferedSerial::prime(void) +{ + // if already busy then the irq will pick this up + if(serial_writable(&_serial)) { + RawSerial::attach(NULL, RawSerial::TxIrq); // make sure not to cause contention in the irq + BufferedSerial::txIrq(); // only write to hardware in one place + RawSerial::attach(this, &BufferedSerial::txIrq, RawSerial::TxIrq); + } + + return; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BufferedSerial/BufferedSerial.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,140 @@ + +/** + * @file BufferedSerial.h + * @brief Software Buffer - Extends mbed Serial functionallity adding irq driven TX and RX + * @author sam grove + * @version 1.0 + * @see + * + * Copyright (c) 2013 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BUFFEREDSERIAL_H +#define BUFFEREDSERIAL_H + +#include "mbed.h" +#include "MyBuffer.h" + +/** A serial port (UART) for communication with other serial devices + * + * Can be used for Full Duplex communication, or Simplex by specifying + * one pin as NC (Not Connected) + * + * Example: + * @code + * #include "mbed.h" + * #include "BufferedSerial.h" + * + * BufferedSerial pc(USBTX, USBRX); + * + * int main() + * { + * while(1) + * { + * Timer s; + * + * s.start(); + * pc.printf("Hello World - buffered\n"); + * int buffered_time = s.read_us(); + * wait(0.1f); // give time for the buffer to empty + * + * s.reset(); + * printf("Hello World - blocking\n"); + * int polled_time = s.read_us(); + * s.stop(); + * wait(0.1f); // give time for the buffer to empty + * + * pc.printf("printf buffered took %d us\n", buffered_time); + * pc.printf("printf blocking took %d us\n", polled_time); + * wait(0.5f); + * } + * } + * @endcode + */ + +/** + * @class BufferedSerial + * @brief Software buffers and interrupt driven tx and rx for Serial + */ +class BufferedSerial : public RawSerial +{ +private: + MyBuffer <char> _rxbuf; + MyBuffer <char> _txbuf; + uint32_t _buf_size; + uint32_t _tx_multiple; + + void rxIrq(void); + void txIrq(void); + void prime(void); + +public: + /** Create a BufferedSerial port, connected to the specified transmit and receive pins + * @param tx Transmit pin + * @param rx Receive pin + * @param buf_size printf() buffer size + * @param tx_multiple amount of max printf() present in the internal ring buffer at one time + * @param name optional name + * @note Either tx or rx may be specified as NC if unused + */ + BufferedSerial(PinName tx, PinName rx, uint32_t buf_size = 256, uint32_t tx_multiple = 4,const char* name=NULL); + + /** Destroy a BufferedSerial port + */ + virtual ~BufferedSerial(void); + + /** Check on how many bytes are in the rx buffer + * @return 1 if something exists, 0 otherwise + */ + virtual int readable(void); + + /** Check to see if the tx buffer has room + * @return 1 always has room and can overwrite previous content if too small / slow + */ + virtual int writeable(void); + + /** Get a single byte from the BufferedSerial Port. + * Should check readable() before calling this. + * @return A byte that came in on the Serial Port + */ + virtual int getc(void); + + /** Write a single byte to the BufferedSerial Port. + * @param c The byte to write to the Serial Port + * @return The byte that was written to the Serial Port Buffer + */ + virtual int putc(int c); + + /** Write a string to the BufferedSerial Port. Must be NULL terminated + * @param s The string to write to the Serial Port + * @return The number of bytes written to the Serial Port Buffer + */ + virtual int puts(const char *s); + + /** Write a formatted string to the BufferedSerial Port. + * @param format The string + format specifiers to write to the Serial Port + * @return The number of bytes written to the Serial Port Buffer + */ + virtual int printf(const char* format, ...); + + /** Write data to the Buffered Serial Port + * @param s A pointer to data to send + * @param length The amount of data being pointed to + * @return The number of bytes written to the Serial Port Buffer + */ + virtual ssize_t write(const void *s, std::size_t length); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/ChaN/ccsbcs.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,348 @@ +/*------------------------------------------------------------------------*/ +/* Unicode - Local code bidirectional converter (C)ChaN, 2015 */ +/* (SBCS code pages) */ +/*------------------------------------------------------------------------*/ +/* 437 U.S. +/ 720 Arabic +/ 737 Greek +/ 771 KBL +/ 775 Baltic +/ 850 Latin 1 +/ 852 Latin 2 +/ 855 Cyrillic +/ 857 Turkish +/ 860 Portuguese +/ 861 Icelandic +/ 862 Hebrew +/ 863 Canadian French +/ 864 Arabic +/ 865 Nordic +/ 866 Russian +/ 869 Greek 2 +*/ + +#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, 0x0650, 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 == 771 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP771(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, 0x2558, 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, 0x0104, 0x0105, 0x010C, 0x010D, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0118, 0x0119, 0x0116, 0x0117, 0x012E, 0x012F, 0x0160, 0x0161, 0x0172, 0x0173, 0x016A, 0x016B, 0x017D, 0x017E, 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 == 860 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP860(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E3, 0x00E0, 0x00C1, 0x00E7, 0x00EA, 0x00CA, 0x00E8, 0x00CD, 0x00D4, 0x00EC, 0x00C3, 0x00C2, + 0x00C9, 0x00C0, 0x00C8, 0x00F4, 0x00F5, 0x00F2, 0x00DA, 0x00F9, 0x00CC, 0x00D5, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x20A7, 0x00D3, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00D2, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 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 == 861 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP861(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00D0, 0x00F0, 0x00DE, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00FE, 0x00FB, 0x00DD, 0x00FD, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00C1, 0x00CD, 0x00D3, 0x00DA, 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 == 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 == 863 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP863(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00C2, 0x00E0, 0x00B6, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x2017, 0x00C0, + 0x00C9, 0x00C8, 0x00CA, 0x00F4, 0x00CB, 0x00CF, 0x00FB, 0x00F9, 0x00A4, 0x00D4, 0x00DC, 0x00A2, 0x00A3, 0x00D9, 0x00DB, 0x0192, + 0x00A6, 0x00B4, 0x00F3, 0x00FA, 0x00A8, 0x00BB, 0x00B3, 0x00AF, 0x00CE, 0x3210, 0x00AC, 0x00BD, 0x00BC, 0x00BE, 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, 0x2219, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 864 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP864(0x80-0xFF) to Unicode conversion table */ + 0x00B0, 0x00B7, 0x2219, 0x221A, 0x2592, 0x2500, 0x2502, 0x253C, 0x2524, 0x252C, 0x251C, 0x2534, 0x2510, 0x250C, 0x2514, 0x2518, + 0x03B2, 0x221E, 0x03C6, 0x00B1, 0x00BD, 0x00BC, 0x2248, 0x00AB, 0x00BB, 0xFEF7, 0xFEF8, 0x0000, 0x0000, 0xFEFB, 0xFEFC, 0x0000, + 0x00A0, 0x00AD, 0xFE82, 0x00A3, 0x00A4, 0xFE84, 0x0000, 0x20AC, 0xFE8E, 0xFE8F, 0xFE95, 0xFE99, 0x060C, 0xFE9D, 0xFEA1, 0xFEA5, + 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669, 0xFED1, 0x061B, 0xFEB1, 0xFEB5, 0xFEB9, 0x061F, + 0x00A2, 0xFE80, 0xFE81, 0xFE83, 0xFE85, 0xFECA, 0xFE8B, 0xFE8D, 0xFE91, 0xFE93, 0xFE97, 0xFE9B, 0xFE9F, 0xFEA3, 0xFEA7, 0xFEA9, + 0xFEAB, 0xFEAD, 0xFEAF, 0xFEB3, 0xFEB7, 0xFEBB, 0xFEBF, 0xFEC1, 0xFEC5, 0xFECB, 0xFECF, 0x00A6, 0x00AC, 0x00F7, 0x00D7, 0xFEC9, + 0x0640, 0xFED3, 0xFED7, 0xFEDB, 0xFEDF, 0xFEE3, 0xFEE7, 0xFEEB, 0xFEED, 0xFEEF, 0xFEF3, 0xFEBD, 0xFECC, 0xFECE, 0xFECD, 0xFEE1, + 0xFE7D, 0x0651, 0xFEE5, 0xFEE9, 0xFEEC, 0xFEF0, 0xFEF2, 0xFED0, 0xFED5, 0xFEF5, 0xFEF6, 0xFEDD, 0xFED9, 0xFEF1, 0x25A0, 0x0000 +}; + +#elif _CODE_PAGE == 865 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP865(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C5, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00A4, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x2558, 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 == 869 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP869(0x80-0xFF) to Unicode conversion table */ + 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x00B7, 0x0386, 0x00B7, 0x00B7, 0x00AC, 0x00A6, 0x2018, 0x2019, 0x0388, 0x2015, 0x0389, + 0x038A, 0x03AA, 0x038C, 0x00B7, 0x00B7, 0x038E, 0x03AB, 0x00A9, 0x038F, 0x00B2, 0x00B3, 0x03AC, 0x00A3, 0x03AD, 0x03AE, 0x03AF, + 0x03CA, 0x0390, 0x03CC, 0x03CD, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x00BD, 0x0398, 0x0399, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x039A, 0x039B, 0x039C, 0x039D, 0x2563, 0x2551, 0x2557, 0x255D, 0x039E, 0x039F, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0A30, 0x03A1, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x03A3, + 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03B1, 0x03B2, 0x03B3, 0x2518, 0x250C, 0x2588, 0x2584, 0x03B4, 0x03B5, 0x2580, + 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x0384, + 0x00AD, 0x00B1, 0x03C5, 0x03C6, 0x03C7, 0x00A7, 0x03C8, 0x0385, 0x00B0, 0x00A8, 0x03C9, 0x03CB, 0x03B0, 0x03CE, 0x25A0, 0x00A0 +}; + +#endif + + +#if !_TBLDEF || !_USE_LFN +#error This file is not needed at current configuration. Remove from the project. +#endif + + + + +WCHAR ff_convert ( /* Converted character, Returns zero on error */ + WCHAR chr, /* Character code to be converted */ + UINT dir /* 0: Unicode to OEM code, 1: OEM code to Unicode */ +) +{ + WCHAR c; + + + if (chr < 0x80) { /* ASCII */ + c = chr; + + } else { + if (dir) { /* OEM code to Unicode */ + c = (chr >= 0x100) ? 0 : Tbl[chr - 0x80]; + + } else { /* Unicode to OEM code */ + for (c = 0; c < 0x80; c++) { + if (chr == Tbl[c]) break; + } + c = (c + 0x80) & 0xFF; + } + } + + return c; +} + + + + +WCHAR ff_wtoupper ( /* Returns upper converted character */ + WCHAR chr /* Unicode character to be upper converted */ +) +{ + static const WCHAR lower[] = { /* Lower case characters to be converted */ + /* Latin Supplement */ 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,0xFF, + /* Latin Extended-A */ 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, + /* Latin Extended-B */ 0x183,0x185,0x188,0x18C,0x192,0x199,0x1A1,0x1A3,0x1A8,0x1AD,0x1B0,0x1B4,0x1B6,0x1B9,0x1BD,0x1C6,0x1C9,0x1CC,0x1CE,0x1D0,0x1D2,0x1D4,0x1D6,0x1D8,0x1DA,0x1DC,0x1DD,0x1DF,0x1E1,0x1E3,0x1E5,0x1E7,0x1E9,0x1EB,0x1ED,0x1EF,0x1F3,0x1F5,0x1FB,0x1FD,0x1FF,0x201,0x203,0x205,0x207,0x209,0x20B,0x20D,0x20F,0x211,0x213,0x215,0x217, + /* Greek, Coptic */ 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,0x3CB,0x3CC,0x3CD,0x3CE,0x3E3,0x3E5,0x3E7,0x3E9,0x3EB, + /* Cyrillic */ 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,0x452,0x453,0x454,0x455,0x456,0x457,0x458,0x459,0x45A,0x45B,0x45C,0x45E,0x45F,0x461,0x463,0x465,0x467,0x469,0x46B,0x46D,0x46F,0x471,0x473,0x475,0x477,0x479,0x47B,0x47D,0x47F,0x481,0x491,0x493,0x495,0x497,0x499,0x49B,0x49D,0x49F,0x4A1,0x4A3,0x4A5,0x4A7,0x4A9,0x4AB,0x4AD,0x4AF,0x4B1,0x4B3,0x4B5,0x4B7,0x4B9,0x4BB,0x4BD,0x4BF,0x4C2,0x4C4,0x4C8,0x4D1,0x4D3,0x4D5,0x4D7,0x4D9,0x4DB,0x4DD,0x4DF,0x4E1,0x4E3,0x4E5,0x4E7,0x4E9,0x4EB,0x4ED,0x4EF,0x4F1,0x4F3,0x4F5,0x4F9, + /* Armenian */ 0x561,0x562,0x563,0x564,0x565,0x566,0x567,0x568,0x569,0x56A,0x56B,0x56C,0x56D,0x56E,0x56F,0x570,0x571,0x572,0x573,0x574,0x575,0x576,0x577,0x578,0x579,0x57A,0x57B,0x57C,0x57D,0x57E,0x57F,0x580,0x581,0x582,0x583,0x584,0x585,0x586, + /* Latin Extended Additional */ 0x1E01,0x1E03,0x1E05,0x1E07,0x1E09,0x1E0B,0x1E0D,0x1E0F,0x1E11,0x1E13,0x1E15,0x1E17,0x1E19,0x1E1B,0x1E1D,0x1E1F,0x1E21,0x1E23,0x1E25,0x1E27,0x1E29,0x1E2B,0x1E2D,0x1E2F,0x1E31,0x1E33,0x1E35,0x1E37,0x1E39,0x1E3B,0x1E3D,0x1E3F,0x1E41,0x1E43,0x1E45,0x1E47,0x1E49,0x1E4B,0x1E4D,0x1E4F,0x1E51,0x1E53,0x1E55,0x1E57,0x1E59,0x1E5B,0x1E5D,0x1E5F,0x1E61,0x1E63,0x1E65,0x1E67,0x1E69,0x1E6B,0x1E6D,0x1E6F,0x1E71,0x1E73,0x1E75,0x1E77,0x1E79,0x1E7B,0x1E7D,0x1E7F,0x1E81,0x1E83,0x1E85,0x1E87,0x1E89,0x1E8B,0x1E8D,0x1E8F,0x1E91,0x1E93,0x1E95,0x1E97,0x1E99,0x1E9B,0x1E9D,0x1E9F,0x1EA1,0x1EA3,0x1EA5,0x1EA7,0x1EA9,0x1EAB,0x1EAD,0x1EAF,0x1EB1,0x1EB3,0x1EB5,0x1EB7,0x1EB9,0x1EBB,0x1EBD,0x1EBF,0x1EC1,0x1EC3,0x1EC5,0x1EC7,0x1EC9,0x1ECB,0x1ECD,0x1ECF,0x1ED1,0x1ED3,0x1ED5,0x1ED7,0x1ED9,0x1EDB,0x1EDD,0x1EDF,0x1EE1,0x1EE3,0x1EE5,0x1EE7,0x1EE9,0x1EEB,0x1EED,0x1EEF,0x1EF1,0x1EF3,0x1EF5,0x1EF7,0x1EF9, + /* Number forms */ 0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,0x217A,0x217B,0x217C,0x217D,0x217E,0x217F, + /* Full-width */ 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 + }; + static const WCHAR upper[] = { /* Upper case characters correspond to lower[] */ + 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, + 0x182,0x184,0x187,0x18B,0x191,0x198,0x1A0,0x1A2,0x1A7,0x1AC,0x1AF,0x1B3,0x1B5,0x1B8,0x1BC,0x1C4,0x1C7,0x1CA,0x1CD,0x1CF,0x1D1,0x1D3,0x1D5,0x1D7,0x1D9,0x1DB,0x18E,0x1DE,0x1E0,0x1E2,0x1E4,0x1E6,0x1E8,0x1EA,0x1EC,0x1EE,0x1F1,0x1F4,0x1FA,0x1FC,0x1FE,0x200,0x202,0x204,0x206,0x208,0x20A,0x20C,0x20E,0x210,0x212,0x214,0x216, + 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,0x3AB,0x38C,0x38E,0x38F,0x3E2,0x3E4,0x3E6,0x3E8,0x3EA, + 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,0x402,0x403,0x404,0x405,0x406,0x407,0x408,0x409,0x40A,0x40B,0x40C,0x40E,0x40F,0x460,0x462,0x464,0x466,0x468,0x46A,0x46C,0x46E,0x470,0x472,0x474,0x476,0x478,0x47A,0x47C,0x47E,0x480,0x490,0x492,0x494,0x496,0x498,0x49A,0x49C,0x49E,0x4A0,0x4A2,0x4A4,0x4A6,0x4A8,0x4AA,0x4AC,0x4AE,0x4B0,0x4B2,0x4B4,0x4B6,0x4B8,0x4BA,0x4BC,0x4BE,0x4C1,0x4C3,0x5C7,0x4D0,0x4D2,0x4D4,0x4D6,0x4D8,0x4DA,0x4DC,0x4DE,0x4E0,0x4E2,0x4E4,0x4E6,0x4E8,0x4EA,0x4EC,0x4EE,0x4F0,0x4F2,0x4F4,0x4F8, + 0x531,0x532,0x533,0x534,0x535,0x536,0x537,0x538,0x539,0x53A,0x53B,0x53C,0x53D,0x53E,0x53F,0x540,0x541,0x542,0x543,0x544,0x545,0x546,0x547,0x548,0x549,0x54A,0x54B,0x54C,0x54D,0x54E,0x54F,0x550,0x551,0x552,0x553,0x554,0x555,0x556, + 0x1E00,0x1E02,0x1E04,0x1E06,0x1E08,0x1E0A,0x1E0C,0x1E0E,0x1E10,0x1E12,0x1E14,0x1E16,0x1E18,0x1E1A,0x1E1C,0x1E1E,0x1E20,0x1E22,0x1E24,0x1E26,0x1E28,0x1E2A,0x1E2C,0x1E2E,0x1E30,0x1E32,0x1E34,0x1E36,0x1E38,0x1E3A,0x1E3C,0x1E3E,0x1E40,0x1E42,0x1E44,0x1E46,0x1E48,0x1E4A,0x1E4C,0x1E4E,0x1E50,0x1E52,0x1E54,0x1E56,0x1E58,0x1E5A,0x1E5C,0x1E5E,0x1E60,0x1E62,0x1E64,0x1E66,0x1E68,0x1E6A,0x1E6C,0x1E6E,0x1E70,0x1E72,0x1E74,0x1E76,0x1E78,0x1E7A,0x1E7C,0x1E7E,0x1E80,0x1E82,0x1E84,0x1E86,0x1E88,0x1E8A,0x1E8C,0x1E8E,0x1E90,0x1E92,0x1E94,0x1E96,0x1E98,0x1E9A,0x1E9C,0x1E9E,0x1EA0,0x1EA2,0x1EA4,0x1EA6,0x1EA8,0x1EAA,0x1EAC,0x1EAE,0x1EB0,0x1EB2,0x1EB4,0x1EB6,0x1EB8,0x1EBA,0x1EBC,0x1EBE,0x1EC0,0x1EC2,0x1EC4,0x1EC6,0x1EC8,0x1ECA,0x1ECC,0x1ECE,0x1ED0,0x1ED2,0x1ED4,0x1ED6,0x1ED8,0x1EDA,0x1EDC,0x1EDE,0x1EE0,0x1EE2,0x1EE4,0x1EE6,0x1EE8,0x1EEA,0x1EEC,0x1EEE,0x1EF0,0x1EF2,0x1EF4,0x1EF6,0x1EF8, + 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 + }; + UINT i, n, hi, li; + + + if (chr < 0x80) { /* ASCII characters (acceleration) */ + if (chr >= 0x61 && chr <= 0x7A) chr -= 0x20; + + } else { /* Non ASCII characters (table search) */ + n = 12; li = 0; hi = sizeof lower / sizeof lower[0]; + do { + i = li + (hi - li) / 2; + if (chr == lower[i]) break; + if (chr > lower[i]) li = i; else hi = i; + } while (--n); + if (n) chr = upper[i]; + } + + return chr; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/ChaN/diskio.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,117 @@ +/*-----------------------------------------------------------------------*/ +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2014 */ +/*-----------------------------------------------------------------------*/ +/* If a working storage control module is available, it should be */ +/* attached to the FatFs via a glue function rather than modifying it. */ +/* This is an example of glue functions to attach various exsisting */ +/* storage control modules to the FatFs module with a defined API. */ +/*-----------------------------------------------------------------------*/ + +#include "diskio.h" +#include "mbed_debug.h" +#include "FATFileSystem.h" + +using namespace mbed; + +/*-----------------------------------------------------------------------*/ +/* Get Drive Status */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_status ( + BYTE pdrv /* Physical drive nmuber to identify the drive */ +) +{ + debug_if(FFS_DBG, "disk_status on pdrv [%d]\n", pdrv); + return (DSTATUS)FATFileSystem::_ffs[pdrv]->disk_status(); +} + +/*-----------------------------------------------------------------------*/ +/* Inidialize a Drive */ +/*-----------------------------------------------------------------------*/ + +DSTATUS disk_initialize ( + BYTE pdrv /* Physical drive nmuber to identify the drive */ +) +{ + debug_if(FFS_DBG, "disk_initialize on pdrv [%d]\n", pdrv); + return (DSTATUS)FATFileSystem::_ffs[pdrv]->disk_initialize(); +} + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ +/*-----------------------------------------------------------------------*/ + +DRESULT disk_read ( + BYTE pdrv, /* Physical drive nmuber to identify the drive */ + BYTE* buff, /* Data buffer to store read data */ + DWORD sector, /* Sector address in LBA */ + UINT count /* Number of sectors to read */ +) +{ + debug_if(FFS_DBG, "disk_read(sector %d, count %d) on pdrv [%d]\n", sector, count, pdrv); + if (FATFileSystem::_ffs[pdrv]->disk_read((uint8_t*)buff, sector, count)) + return RES_PARERR; + else + return RES_OK; +} + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ +/*-----------------------------------------------------------------------*/ + +#if _USE_WRITE +DRESULT disk_write ( + BYTE pdrv, /* Physical drive nmuber to identify the drive */ + const BYTE* buff, /* Data to be written */ + DWORD sector, /* Sector address in LBA */ + UINT count /* Number of sectors to write */ +) +{ + debug_if(FFS_DBG, "disk_write(sector %d, count %d) on pdrv [%d]\n", sector, count, pdrv); + if (FATFileSystem::_ffs[pdrv]->disk_write((uint8_t*)buff, sector, count)) + return RES_PARERR; + else + return RES_OK; +} +#endif + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ +/*-----------------------------------------------------------------------*/ + +#if _USE_IOCTL +DRESULT disk_ioctl ( + BYTE pdrv, /* Physical drive nmuber (0..) */ + BYTE cmd, /* Control code */ + void* buff /* Buffer to send/receive control data */ +) +{ + debug_if(FFS_DBG, "disk_ioctl(%d)\n", cmd); + switch(cmd) { + case CTRL_SYNC: + if(FATFileSystem::_ffs[pdrv] == NULL) { + return RES_NOTRDY; + } else if(FATFileSystem::_ffs[pdrv]->disk_sync()) { + return RES_ERROR; + } + return RES_OK; + case GET_SECTOR_COUNT: + if(FATFileSystem::_ffs[pdrv] == NULL) { + return RES_NOTRDY; + } else { + DWORD res = FATFileSystem::_ffs[pdrv]->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; +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/ChaN/diskio.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,80 @@ +/*-----------------------------------------------------------------------/ +/ Low level disk interface modlue include file (C)ChaN, 2014 / +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO_DEFINED +#define _DISKIO_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif + +#define _USE_WRITE 1 /* 1: Enable disk_write function */ +#define _USE_IOCTL 1 /* 1: Enable 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 */ + + +DSTATUS disk_initialize (BYTE pdrv); +DSTATUS disk_status (BYTE pdrv); +DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); +DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); +DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); + + +/* 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 (Used by FatFs) */ +#define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */ +#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */ +#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */ +#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */ +#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */ + +/* Generic command (Not used by FatFs) */ +#define CTRL_POWER 5 /* Get/Set power status */ +#define CTRL_LOCK 6 /* Lock/Unlock media removal */ +#define CTRL_EJECT 7 /* Eject media */ +#define CTRL_FORMAT 8 /* Create physical format on the 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 */ + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/ChaN/ff.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,4691 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module R0.11a (C)ChaN, 2015 / +/-----------------------------------------------------------------------------/ +/ FatFs module is a free software that opened under license policy of +/ following conditions. +/ +/ Copyright (C) 2015, ChaN, all right reserved. +/ +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/----------------------------------------------------------------------------*/ + + +#include "ff.h" /* Declarations of FatFs API */ +#include "diskio.h" /* Declarations of disk I/O functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 64180 /* Revision ID */ +#error Wrong include file (ff.h). +#endif + + +/* Reentrancy related */ +#if _FS_REENTRANT +#if _USE_LFN == 1 +#error Static LFN work area cannot be used at thread-safe 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->err = (BYTE)(res); LEAVE_FF(fs, res); } + + +/* Definitions of sector size */ +#if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096) +#error Wrong sector size configuration +#endif +#if _MAX_SS == _MIN_SS +#define SS(fs) ((UINT)_MAX_SS) /* Fixed sector size */ +#else +#define SS(fs) ((fs)->ssize) /* Variable sector size */ +#endif + + +/* Timestamp feature */ +#if _FS_NORTC == 1 +#if _NORTC_YEAR < 1980 || _NORTC_YEAR > 2107 || _NORTC_MON < 1 || _NORTC_MON > 12 || _NORTC_MDAY < 1 || _NORTC_MDAY > 31 +#error Invalid _FS_NORTC settings +#endif +#define GET_FATTIME() ((DWORD)(_NORTC_YEAR - 1980) << 25 | (DWORD)_NORTC_MON << 21 | (DWORD)_NORTC_MDAY << 16) +#else +#define GET_FATTIME() get_fattime() +#endif + + +/* File access control feature */ +#if _FS_LOCK +#if _FS_READONLY +#error _FS_LOCK must be 0 at read-only configuration +#endif +typedef struct { + FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ + DWORD clu; /* Object ID 2, directory (0:root) */ + WORD idx; /* Object ID 3, directory index */ + WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ +} FILESEM; +#endif + + + +/* DBCS code ranges and SBCS upper conversion tables */ + +#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. */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x45,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,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 == 720 /* Arabic */ +#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 == 737 /* Greek */ +#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,0xEF,0xF5,0xF0,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 == 771 /* KBL */ +#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,0xDC,0xDE,0xDE, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic */ +#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 /* Latin 1 */ +#define _DF1S 0 +#define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \ + 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \ + 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 */ +#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,0xAC, \ + 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 */ +#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 */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,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,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,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 860 /* Portuguese */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \ + 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x86,0x8B,0x9F,0x96,0xA5,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 == 861 /* Icelandic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA4,0xA5,0xA6,0xA7,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 == 862 /* Hebrew */ +#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,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 == 863 /* Canadian-French */ +#define _DF1S 0 +#define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \ + 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \ + 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,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 == 864 /* Arabic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x45,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,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 == 865 /* Nordic */ +#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,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 == 866 /* Russian */ +#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 == 869 /* Greek 2 */ +#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,0x86,0x9C,0x8D,0x8F,0x90, \ + 0x91,0x90,0x92,0x95,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,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \ + 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \ + 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} + +#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 NSFLAG 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 (Differ from specs but correct for real DOS/Windows) */ +#define MIN_FAT16 4086U /* Minimum number of clusters of FAT16 */ +#define MIN_FAT32 65526U /* Minimum number of clusters of FAT32 */ + + +/* FatFs refers the members in the FAT structures as byte array instead of +/ structure members because the structure is not binary compatible between +/ different platforms */ + +#define BS_jmpBoot 0 /* x86 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 directory 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 (1) */ +#define BS_NTres 37 /* Error flag (1) */ +#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 directory first cluster (4) */ +#define BPB_FSInfo 48 /* Offset of FSINFO sector (2) */ +#define BPB_BkBootSec 50 /* Offset of backup boot sector (2) */ +#define BS_DrvNum32 64 /* Physical drive number (1) */ +#define BS_NTres32 65 /* Error flag (1) */ +#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 /* Signature word (2) */ + +#define DIR_Name 0 /* Short file name (11) */ +#define DIR_Attr 11 /* Attribute (1) */ +#define DIR_NTres 12 /* Lower case flag (1) */ +#define DIR_CrtTimeTenth 13 /* Created time sub-second (1) */ +#define DIR_CrtTime 14 /* Created time (2) */ +#define DIR_CrtDate 16 /* Created date (2) */ +#define DIR_LstAccDate 18 /* Last accessed 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 /* Checksum of corresponding SFN entry */ +#define LDIR_FstClusLO 26 /* Must be zero (0) */ +#define SZ_DIRE 32 /* Size of a directory entry */ +#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ +#define DDEM 0xE5 /* Deleted directory entry mark at DIR_Name[0] */ +#define RDDEM 0x05 /* Replacement of the character collides with DDEM */ + + + + +/*-------------------------------------------------------------------------- + + Module Private Work Area + +---------------------------------------------------------------------------*/ + +/* Remark: Uninitialized variables with static duration are guaranteed +/ zero/null at start-up. If not, either the linker or start-up routine +/ being used is not compliance with ANSI-C standard. +*/ + +#if _VOLUMES < 1 || _VOLUMES > 9 +#error Wrong _VOLUMES setting +#endif +static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ +static WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH && _VOLUMES >= 2 +static BYTE CurrVol; /* Current drive */ +#endif + +#if _FS_LOCK +static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */ +#endif + +#if _USE_LFN == 0 /* Non LFN feature */ +#define DEFINE_NAMEBUF BYTE sfn[12] +#define INIT_BUF(dobj) (dobj).fn = sfn +#define FREE_BUF() +#else +#if _MAX_LFN < 12 || _MAX_LFN > 255 +#error Wrong _MAX_LFN setting +#endif +#if _USE_LFN == 1 /* LFN feature with static working buffer */ +static WCHAR LfnBuf[_MAX_LFN + 1]; +#define DEFINE_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 DEFINE_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 DEFINE_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 _USE_LFN setting +#endif +#endif + +#ifdef _EXCVT +static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for SBCS extended characters */ +#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 (fs && + res != FR_NOT_ENABLED && + res != FR_INVALID_DRIVE && + res != FR_INVALID_OBJECT && + res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* File lock control functions */ +/*-----------------------------------------------------------------------*/ +#if _FS_LOCK + +static +FRESULT chk_lock ( /* Check if the file can be accessed */ + FATFS_DIR* dp, /* Directory object pointing the file to be checked */ + int acc /* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i, be; + + /* Search file semaphore table */ + for (i = be = 0; i < _FS_LOCK; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dp->fs && /* Check if the object matched with an open object */ + Files[i].clu == dp->sclust && + Files[i].idx == dp->index) break; + } else { /* Blank entry */ + be = 1; + } + } + if (i == _FS_LOCK) /* The object is not opened */ + return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new object? */ + + /* The object 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 object */ +{ + UINT i; + + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + return (i == _FS_LOCK) ? 0 : 1; +} + + +static +UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */ + FATFS_DIR* dp, /* Directory object pointing the file to register or increment */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i; + + + for (i = 0; i < _FS_LOCK; i++) { /* Find the object */ + if (Files[i].fs == dp->fs && + Files[i].clu == dp->sclust && + Files[i].idx == dp->index) break; + } + + if (i == _FS_LOCK) { /* Not opened. Register it as new. */ + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + if (i == _FS_LOCK) return 0; /* No free entry to register (int err) */ + Files[i].fs = dp->fs; + Files[i].clu = dp->sclust; + Files[i].idx = dp->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 object open counter */ + UINT i /* Semaphore index (1..) */ +) +{ + WORD n; + FRESULT res; + + + if (--i < _FS_LOCK) { /* Shift index number origin from 0 */ + n = Files[i].ctr; + if (n == 0x100) n = 0; /* If write mode open, delete the entry */ + if (n) n--; /* Decrement read mode open count */ + Files[i].ctr = n; + if (!n) Files[i].fs = 0; /* Delete the entry if open count gets zero */ + res = FR_OK; + } else { + res = FR_INT_ERR; /* Invalid index nunber */ + } + return res; +} + + +static +void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < _FS_LOCK; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Move/Flush disk access window in the file system object */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync_window ( /* FR_OK:succeeded, !=0:error */ + FATFS* fs /* File system object */ +) +{ + DWORD wsect; + UINT nf; + FRESULT res = FR_OK; + + + if (fs->wflag) { /* Write back the sector if it is dirty */ + wsect = fs->winsect; /* Current sector number */ + if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fs->wflag = 0; + if (wsect - fs->fatbase < fs->fsize) { /* Is it in the FAT area? */ + for (nf = fs->n_fats; nf >= 2; nf--) { /* Reflect the change to all FAT copies */ + wsect += fs->fsize; + disk_write(fs->drv, fs->win, wsect, 1); + } + } + } + } + return res; +} +#endif + + +static +FRESULT move_window ( /* FR_OK(0):succeeded, !=0:error */ + FATFS* fs, /* File system object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ +) +{ + FRESULT res = FR_OK; + + + if (sector != fs->winsect) { /* Window offset changed? */ +#if !_FS_READONLY + res = sync_window(fs); /* Write-back changes */ +#endif + if (res == FR_OK) { /* Fill sector window with new data */ + if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) { + sector = 0xFFFFFFFF; /* Invalidate window if data is not reliable */ + res = FR_DISK_ERR; + } + fs->winsect = sector; + } + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize file system and strage device */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync_fs ( /* FR_OK:succeeded, !=0:error */ + FATFS* fs /* File system object */ +) +{ + FRESULT res; + + + res = sync_window(fs); + if (res == FR_OK) { + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { + /* Create FSInfo structure */ + mem_set(fs->win, 0, SS(fs)); + 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 */ + fs->winsect = fs->volbase + 1; + disk_write(fs->drv, fs->win, fs->winsect, 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# */ +/*-----------------------------------------------------------------------*/ +/* Hidden API for hacks and disk tools */ + +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 */ +/*-----------------------------------------------------------------------*/ +/* Hidden API for hacks and disk tools */ + +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x0FFFFFFF:Cluster status */ + FATFS* fs, /* File system object */ + DWORD clst /* FAT index number (cluster number) to get the value */ +) +{ + UINT wc, bc; + BYTE *p; + DWORD val; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ + val = 1; /* Internal error */ + + } else { + val = 0xFFFFFFFF; /* Default value falls on disk error */ + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc = fs->win[bc++ % SS(fs)]; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc |= fs->win[bc % SS(fs)] << 8; + val = clst & 1 ? wc >> 4 : (wc & 0xFFF); + break; + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; + p = &fs->win[clst * 2 % SS(fs)]; + val = LD_WORD(p); + break; + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + p = &fs->win[clst * 4 % SS(fs)]; + val = LD_DWORD(p) & 0x0FFFFFFF; + break; + + default: + val = 1; /* Internal error */ + } + } + + return val; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ +/* Hidden API for hacks and disk tools */ + +#if !_FS_READONLY +FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ + FATFS* fs, /* File system object */ + DWORD clst, /* FAT index number (cluster number) to be changed */ + DWORD val /* New value to be set to the entry */ +) +{ + UINT bc; + BYTE *p; + FRESULT res; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ + res = FR_INT_ERR; + + } else { + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)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; + 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)); + fs->wflag = 1; + 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); + fs->wflag = 1; + 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); + fs->wflag = 1; + break; + + default : + res = FR_INT_ERR; + } + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ + FATFS* fs, /* File system object */ + DWORD clst /* Cluster# to remove a chain from */ +) +{ + FRESULT res; + DWORD nxt; +#if _USE_TRIM + DWORD scl = clst, ecl = clst, rt[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid 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_TRIM + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ + ecl = nxt; + } else { /* End of contiguous clusters */ + rt[0] = clust2sect(fs, scl); /* Start sector */ + rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs->drv, CTRL_TRIM, rt); /* 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: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; /* Invalid value */ + if (cs == 0xFFFFFFFF) return cs; /* A disk error occurred */ + 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) { /* Check 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 ( /* FR_OK(0):succeeded, !=0:error */ + FATFS_DIR* dp, /* Pointer to directory object */ + UINT idx /* Index of directory table */ +) +{ + DWORD clst, sect; + UINT ic; + + + dp->index = (WORD)idx; /* Current index */ + clst = dp->sclust; /* Table start cluster (0:root) */ + if (clst == 1 || clst >= dp->fs->n_fatent) /* Check start cluster range */ + return FR_INT_ERR; + if (!clst && dp->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ + clst = dp->fs->dirbase; + + if (clst == 0) { /* Static table (root-directory in FAT12/16) */ + if (idx >= dp->fs->n_rootdir) /* Is index out of range? */ + return FR_INT_ERR; + sect = dp->fs->dirbase; + } + else { /* Dynamic table (root-directory in FAT32 or sub-directory) */ + ic = SS(dp->fs) / SZ_DIRE * dp->fs->csize; /* Entries per cluster */ + while (idx >= ic) { /* Follow cluster chain */ + clst = get_fat(dp->fs, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= dp->fs->n_fatent) /* Reached to end of table or internal error */ + return FR_INT_ERR; + idx -= ic; + } + sect = clust2sect(dp->fs, clst); + } + dp->clust = clst; /* Current cluster# */ + if (!sect) return FR_INT_ERR; + dp->sect = sect + idx / (SS(dp->fs) / SZ_DIRE); /* Sector# of the directory entry */ + dp->dir = dp->fs->win + (idx % (SS(dp->fs) / SZ_DIRE)) * SZ_DIRE; /* Ptr to the entry in the sector */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory table index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */ + FATFS_DIR* dp, /* Pointer to the directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ +) +{ + DWORD clst; + UINT i; +#if !_FS_READONLY + UINT c; +#endif + + + i = dp->index + 1; + if (!(i & 0xFFFF) || !dp->sect) /* Report EOT when index has reached 65535 */ + return FR_NO_FILE; + + if (!(i % (SS(dp->fs) / SZ_DIRE))) { /* Sector changed? */ + dp->sect++; /* Next sector */ + + if (!dp->clust) { /* Static table */ + if (i >= dp->fs->n_rootdir) /* Report EOT if it reached end of static table */ + return FR_NO_FILE; + } + else { /* Dynamic table */ + if (((i / (SS(dp->fs) / SZ_DIRE)) & (dp->fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(dp->fs, dp->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + if (clst >= dp->fs->n_fatent) { /* If it reached end of dynamic table, */ +#if !_FS_READONLY + if (!stretch) return FR_NO_FILE; /* If do not stretch, report EOT */ + clst = create_chain(dp->fs, dp->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 (sync_window(dp->fs)) return FR_DISK_ERR;/* Flush disk access window */ + mem_set(dp->fs->win, 0, SS(dp->fs)); /* Clear window buffer */ + dp->fs->winsect = clust2sect(dp->fs, clst); /* Cluster start sector */ + for (c = 0; c < dp->fs->csize; c++) { /* Fill the new cluster with 0 */ + dp->fs->wflag = 1; + if (sync_window(dp->fs)) return FR_DISK_ERR; + dp->fs->winsect++; + } + dp->fs->winsect -= c; /* Rewind window offset */ +#else + if (!stretch) return FR_NO_FILE; /* If do not stretch, report EOT (this is to suppress warning) */ + return FR_NO_FILE; /* Report EOT */ +#endif + } + dp->clust = clst; /* Initialize data for new cluster */ + dp->sect = clust2sect(dp->fs, clst); + } + } + } + + dp->index = (WORD)i; /* Current index */ + dp->dir = dp->fs->win + (i % (SS(dp->fs) / SZ_DIRE)) * SZ_DIRE; /* Current entry in the window */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Reserve directory entry */ +/*-----------------------------------------------------------------------*/ + +#if !_FS_READONLY +static +FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ + FATFS_DIR* dp, /* Pointer to the directory object */ + UINT nent /* Number of contiguous entries to allocate (1-21) */ +) +{ + FRESULT res; + UINT n; + + + res = dir_sdi(dp, 0); + if (res == FR_OK) { + n = 0; + do { + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + if (dp->dir[0] == DDEM || dp->dir[0] == 0) { /* Is it a free entry? */ + if (++n == nent) break; /* A block of contiguous free entries is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dp, 1); /* Next entry with table stretch enabled */ + } while (res == FR_OK); + } + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Load/Store start cluster number */ +/*-----------------------------------------------------------------------*/ + +static +DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */ + FATFS* fs, /* Pointer to the fs object */ + const BYTE* dir /* Pointer to the SFN entry */ +) +{ + DWORD cl; + + cl = LD_WORD(dir + DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) + cl |= (DWORD)LD_WORD(dir + DIR_FstClusHI) << 16; + + return cl; +} + + +#if !_FS_READONLY +static +void st_clust ( + BYTE* dir, /* Pointer to the SFN entry */ + DWORD cl /* Value to be set */ +) +{ + ST_WORD(dir + DIR_FstClusLO, cl); + ST_WORD(dir + DIR_FstClusHI, cl >> 16); +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* 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 characters in the directory entry */ + + +static +int cmp_lfn ( /* 1:matched, 0:not matched */ + WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */ + BYTE* dir /* Pointer to the directory entry containing the part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (LD_WORD(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = LD_WORD(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc) { + if (i >= _MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) /* Compare it */ + return 0; /* Not matched */ + wc = uc; + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if ((dir[LDIR_Ord] & LLEF) && 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 or invalid LFN entry */ + WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ + BYTE* dir /* Pointer to the LFN entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (LD_WORD(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = LD_WORD(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc) { + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if (dir[LDIR_Ord] & LLEF) { /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; /* The part of LFN is valid */ +} + + +#if !_FS_READONLY +static +void fit_lfn ( + const WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ + BYTE* dir, /* Pointer to the LFN entry to be processed */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* Checksum of the corresponding SFN */ +) +{ + UINT i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set checksum */ + 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 working buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective character */ + ST_WORD(dir+LfnOfs[s], wc); /* Put it */ + if (!wc) wc = 0xFFFF; /* Padding characters following last character */ + } while (++s < 13); + if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLEF; /* 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 +static +void gen_numname ( + BYTE* dst, /* Pointer to the buffer to store numbered SFN */ + const BYTE* src, /* Pointer to SFN */ + const WCHAR* lfn, /* Pointer to LFN */ + UINT seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + WCHAR wc; + DWORD sr; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */ + sr = seq; + while (*lfn) { /* Create a CRC */ + wc = *lfn++; + for (i = 0; i < 16; i++) { + sr = (sr << 1) + (wc & 1); + wc >>= 1; + if (sr & 0x10000) sr ^= 0x11021; + } + } + seq = (UINT)sr; + } + + /* 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 checksum of an SFN entry */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +BYTE sum_sfn ( + const BYTE* dir /* Pointer to the SFN 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 ( /* FR_OK(0):succeeded, !=0:error */ + FATFS_DIR* dp /* 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(dp, 0); /* Rewind directory object */ + if (res != FR_OK) return res; + +#if _USE_LFN + ord = sum = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ +#endif + do { + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + dir = dp->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 == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (dp->lfn) { + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= ~LLEF; ord = c; /* LFN start order */ + dp->lfn_idx = dp->index; /* Start index of LFN */ + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dir, dp->fn, 11)) break; /* SFN matched? */ + ord = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + } + } +#else /* Non LFN configuration */ + if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dp->fn, 11)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 +static +FRESULT dir_read ( + FATFS_DIR* dp, /* Pointer to the directory object */ + int vol /* Filtered by 0:file/directory or 1:volume label */ +) +{ + FRESULT res; + BYTE a, c, *dir; +#if _USE_LFN + BYTE ord = 0xFF, sum = 0xFF; +#endif + + res = FR_NO_FILE; + while (dp->sect) { + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + dir = dp->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 */ + a = dir[DIR_Attr] & AM_MASK; +#if _USE_LFN /* LFN configuration */ + if (c == DDEM || (!_FS_RPATH && c == '.') || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= ~LLEF; ord = c; + dp->lfn_idx = dp->index; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */ + dp->lfn_idx = 0xFFFF; /* It has no LFN. */ + break; + } + } +#else /* Non LFN configuration */ + if (c != DDEM && (_FS_RPATH || c != '.') && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dp, 0); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dp->sect = 0; + + return res; +} +#endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */ + FATFS_DIR* dp /* Target directory with object name to be created */ +) +{ + FRESULT res; +#if _USE_LFN /* LFN configuration */ + UINT n, nent; + BYTE sn[12], *fn, sum; + WCHAR *lfn; + + + fn = dp->fn; lfn = dp->lfn; + mem_cpy(sn, fn, 12); + + if (_FS_RPATH && (sn[NSFLAG] & NS_DOT)) /* Cannot create dot entry */ + return FR_INVALID_NAME; + + if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + fn[NSFLAG] = 0; dp->lfn = 0; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + res = dir_find(dp); /* 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[NSFLAG] = sn[NSFLAG]; dp->lfn = lfn; + } + + if (sn[NSFLAG] & NS_LFN) { /* When LFN is to be created, allocate entries for an SFN + LFNs. */ + for (n = 0; lfn[n]; n++) ; + nent = (n + 25) / 13; + } else { /* Otherwise allocate an entry for an SFN */ + nent = 1; + } + res = dir_alloc(dp, nent); /* Allocate entries */ + + if (res == FR_OK && --nent) { /* Set LFN entry if needed */ + res = dir_sdi(dp, dp->index - nent); + if (res == FR_OK) { + sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ + do { /* Store LFN entries in bottom first */ + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + fit_lfn(dp->lfn, dp->dir, (BYTE)nent, sum); + dp->fs->wflag = 1; + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK && --nent); + } + } +#else /* Non LFN configuration */ + res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ +#endif + + if (res == FR_OK) { /* Set SFN entry */ + res = move_window(dp->fs, dp->sect); + if (res == FR_OK) { + mem_set(dp->dir, 0, SZ_DIRE); /* Clean the entry */ + mem_cpy(dp->dir, dp->fn, 11); /* Put SFN */ +#if _USE_LFN + dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + dp->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:Succeeded, FR_DISK_ERR:A disk error */ + FATFS_DIR* dp /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; +#if _USE_LFN /* LFN configuration */ + UINT i; + + i = dp->index; /* SFN index */ + res = dir_sdi(dp, (dp->lfn_idx == 0xFFFF) ? i : dp->lfn_idx); /* Goto the SFN or top of the LFN entries */ + if (res == FR_OK) { + do { + res = move_window(dp->fs, dp->sect); + if (res != FR_OK) break; + mem_set(dp->dir, 0, SZ_DIRE); /* Clear and mark the entry "deleted" */ + *dp->dir = DDEM; + dp->fs->wflag = 1; + if (dp->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */ + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } + +#else /* Non LFN configuration */ + res = dir_sdi(dp, dp->index); + if (res == FR_OK) { + res = move_window(dp->fs, dp->sect); + if (res == FR_OK) { + mem_set(dp->dir, 0, SZ_DIRE); /* Clear and mark the entry "deleted" */ + *dp->dir = DDEM; + dp->fs->wflag = 1; + } + } +#endif + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 +static +void get_fileinfo ( /* No return code */ + FATFS_DIR* dp, /* Pointer to the directory object */ + FILINFO* fno /* Pointer to the file information to be filled */ +) +{ + UINT i; + TCHAR *p, c; + BYTE *dir; +#if _USE_LFN + WCHAR w, *lfn; +#endif + + p = fno->fname; + if (dp->sect) { /* Get SFN */ + dir = dp->dir; + i = 0; + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) *p++ = '.'; /* Insert a . if extension is exist */ +#if _USE_LFN + if (IsUpper(c) && (dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY))) + c += 0x20; /* To lower */ +#if _LFN_UNICODE + if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dir[i])) + c = c << 8 | dir[i++]; + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; +#endif +#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 string by a \0 */ + +#if _USE_LFN + if (fno->lfname) { + i = 0; p = fno->lfname; + if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) { /* Get LFN if available */ + lfn = dp->lfn; + while ((w = *lfn++) != 0) { /* Get an LFN character */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (!w) { i = 0; break; } /* No LFN if it could not be converted */ + if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC (always false on SBCS cfg) */ + p[i++] = (TCHAR)(w >> 8); +#endif + if (i >= fno->lfsize - 1) { i = 0; break; } /* No LFN if buffer overflow */ + p[i++] = (TCHAR)w; + } + } + p[i] = 0; /* Terminate LFN string by a \0 */ + } +#endif +} +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pattern matching */ +/*-----------------------------------------------------------------------*/ +#if _USE_FIND && _FS_MINIMIZE <= 1 +static +WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */ + const TCHAR** ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */ +) +{ + WCHAR chr; + +#if !_LFN_UNICODE + chr = (BYTE)*(*ptr)++; /* Get a byte */ + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ + if (IsDBCS1(chr) && IsDBCS2(**ptr)) /* Get DBC 2nd byte if needed */ + chr = chr << 8 | (BYTE)*(*ptr)++; +#ifdef _EXCVT + if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ +#endif +#else + chr = ff_wtoupper(*(*ptr)++); /* Get a word and to upper */ +#endif + return chr; +} + + +static +int pattern_matching ( /* 0:mismatched, 1:matched */ + const TCHAR* pat, /* Matching pattern */ + const TCHAR* nam, /* String to be tested */ + int skip, /* Number of pre-skip chars (number of ?s) */ + int inf /* Infinite search (* specified) */ +) +{ + const TCHAR *pp, *np; + WCHAR pc, nc; + int nm, nx; + + + while (skip--) { /* Pre-skip name chars */ + if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ + } + if (!*pat && inf) return 1; /* (short circuit) */ + + do { + pp = pat; np = nam; /* Top of pattern and name to match */ + for (;;) { + if (*pp == '?' || *pp == '*') { /* Wildcard? */ + nm = nx = 0; + do { /* Analyze the wildcard chars */ + if (*pp++ == '?') nm++; else nx = 1; + } while (*pp == '?' || *pp == '*'); + if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ + nc = *np; break; /* Branch mismatched */ + } + pc = get_achar(&pp); /* Get a pattern char */ + nc = get_achar(&np); /* Get a name char */ + if (pc != nc) break; /* Branch mismatched? */ + if (!pc) return 1; /* Branch matched? (matched at end of both strings) */ + } + get_achar(&nam); /* nam++ */ + } while (inf && nc); /* Retry until end of name if infinite search is specified */ + + return 0; +} +#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pick a top segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ + FATFS_DIR* dp, /* Pointer to the directory object */ + const TCHAR** path /* Pointer to pointer to the segment in the path string */ +) +{ +#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 = dp->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 */ + w = (w << 8) + b; /* Create a DBC */ + if (!IsDBCS2(b)) + return FR_INVALID_NAME; /* Reject invalid sequence */ + } + 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 characters for LFN */ + return FR_INVALID_NAME; + lfn[di++] = w; /* Store the Unicode character */ + } + *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] == '.') || + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot entry? */ + lfn[di] = 0; + for (i = 0; i < 11; i++) /* Create dot name for SFN entry */ + dp->fn[i] = (i < di) ? '.' : ' '; + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Snip off trailing spaces and dots if exist */ + 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(dp->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 character */ + 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 character */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) w = ExCvt[w - 0x80]; /* Convert extended character 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) { /* Is this DBC? (always false at SBCS cfg) */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; continue; + } + dp->fn[i++] = (BYTE)(w >> 8); + } else { /* SBC */ + if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal characters 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; + } + } + } + } + dp->fn[i++] = (BYTE)w; + } + + if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + + 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 character, 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) */ + } + + dp->fn[NSFLAG] = 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++) ; /* Skip duplicated separator */ + sfn = dp->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[NSFLAG] = (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 character? */ + b |= 3; /* Eliminate NT flag */ +#ifdef _EXCVT + c = ExCvt[c - 0x80]; /* To upper extended characters (SBCS cfg) */ +#else +#if !_DF1S + return FR_INVALID_NAME; /* Reject extended characters (ASCII cfg) */ +#endif +#endif + } + if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false at 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 { /* SBC */ + 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] == DDEM) sfn[0] = RDDEM; /* When first character collides with DDEM, replace it with RDDEM */ + + 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[NSFLAG] = c; /* Store NT flag, File name is created */ + + return FR_OK; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + FATFS_DIR* dp, /* 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++; dp->sclust = 0; /* Strip it and start from the root directory */ + } else { /* No heading separator */ + dp->sclust = dp->fs->cdir; /* Start from the current directory */ + } +#else + if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ + path++; + dp->sclust = 0; /* Always start from the root directory */ +#endif + + if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ + res = dir_sdi(dp, 0); + dp->dir = 0; + } else { /* Follow path */ + for (;;) { + res = create_name(dp, &path); /* Get a segment name of the path */ + if (res != FR_OK) break; + res = dir_find(dp); /* Find an object with the sagment name */ + ns = dp->fn[NSFLAG]; + if (res != FR_OK) { /* Failed to find the object */ + if (res == FR_NO_FILE) { /* Object is not found */ + if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, */ + dp->sclust = 0; dp->dir = 0; /* it is the root directory and stay there */ + if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ + res = FR_OK; /* Ended at the root directroy. Function completed. */ + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ + } + } + break; + } + if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ + dir = dp->dir; /* Follow the sub-directory */ + if (!(dir[DIR_Attr] & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + res = FR_NO_PATH; break; + } + dp->sclust = ld_clust(dp->fs, dir); + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get logical drive number from path name */ +/*-----------------------------------------------------------------------*/ + +static +int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ + const TCHAR** path /* Pointer to pointer to the path name */ +) +{ + const TCHAR *tp, *tt; + UINT i; + int vol = -1; +#if _STR_VOLUME_ID /* Find string drive id */ + static const char* const str[] = {_VOLUME_STRS}; + const char *sp; + char c; + TCHAR tc; +#endif + + + if (*path) { /* If the pointer is not a null */ + for (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ; /* Find ':' in the path */ + if (*tt == ':') { /* If a ':' is exist in the path name */ + tp = *path; + i = *tp++ - '0'; + if (i < 10 && tp == tt) { /* Is there a numeric drive id? */ + if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = ++tt; + } + } +#if _STR_VOLUME_ID + else { /* No numeric drive number, find string drive id */ + i = 0; tt++; + do { + sp = str[i]; tp = *path; + do { /* Compare a string drive id with path name */ + c = *sp++; tc = *tp++; + if (IsLower(tc)) tc -= 0x20; + } while (c && (TCHAR)c == tc); + } while ((c || tp != tt) && ++i < _VOLUMES); /* Repeat for each id until pattern match */ + if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = tt; + } + } +#endif + return vol; + } +#if _FS_RPATH && _VOLUMES >= 2 + vol = CurrVol; /* Current drive */ +#else + vol = 0; /* Drive 0 */ +#endif + } + return vol; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load a sector and check if it is an FAT boot sector */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:Valid FAT-BS, 1:Valid BS but not FAT, 2:Not a BS, 3:Disk error */ + FATFS* fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ +) +{ + fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ + if (move_window(fs, sect) != FR_OK) /* Load boot record */ + return 3; + + if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check boot 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) /* Check "FAT" string */ + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Find logical drive and check if the volume is mounted */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ + FATFS** rfs, /* Pointer to pointer to the found file system object */ + const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ + BYTE wmode /* !=0: Check write protection for write access */ +) +{ + BYTE fmt, *pt; + int vol; + DSTATUS stat; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4]; + WORD nrsv; + FATFS *fs; + UINT i; + + + /* Get logical drive number from the path name */ + *rfs = 0; + vol = get_ldnumber(path); + if (vol < 0) return FR_INVALID_DRIVE; + + /* Check if the file system object is valid or not */ + fs = FatFs[vol]; /* Get pointer to the file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ + + ENTER_FF(fs); /* Lock the volume */ + *rfs = fs; /* Return pointer to the file system object */ + + if (fs->fs_type) { /* If the volume has been mounted */ + stat = disk_status(fs->drv); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ + if (!_FS_READONLY && wmode && (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 the physical drive */ + if (stat & STA_NOINIT) /* Check if the initialization succeeded */ + return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ + if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; +#if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */ + if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK + || SS(fs) < _MIN_SS || SS(fs) > _MAX_SS) return FR_DISK_ERR; +#endif + /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */ + bsect = 0; + fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT boot sector as SFD */ + if (fmt == 1 || (!fmt && (LD2PT(vol)))) { /* Not an FAT boot sector or forced partition number */ + for (i = 0; i < 4; i++) { /* Get partition offset */ + pt = fs->win + MBR_Table + i * SZ_PTE; + br[i] = pt[4] ? LD_DWORD(&pt[8]) : 0; + } + i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ + if (i) i--; + do { /* Find an FAT volume */ + bsect = br[i]; + fmt = bsect ? check_fs(fs, bsect) : 2; /* Check the partition */ + } while (!LD2PT(vol) && fmt && ++i < 4); + } + if (fmt == 3) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ + 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 = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + if (fs->n_fats != 1 && fs->n_fats != 2) /* (Must be 1 or 2) */ + return FR_NO_FILESYSTEM; + fasize *= fs->n_fats; /* Number of sectors for FAT area */ + + fs->csize = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + if (!fs->csize || (fs->csize & (fs->csize - 1))) /* (Must be power of 2) */ + return FR_NO_FILESYSTEM; + + fs->n_rootdir = LD_WORD(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZ_DIRE)) /* (Must be sector aligned) */ + return FR_NO_FILESYSTEM; + + 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; /* (Must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIRE); /* 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->volbase = bsect; /* Volume start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + fs->database = bsect + sysect; /* Data 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; /* (Needed 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) ? /* (Needed 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 the size needed) */ + return FR_NO_FILESYSTEM; + +#if !_FS_READONLY + /* Initialize cluster allocation information */ + fs->last_clust = fs->free_clust = 0xFFFFFFFF; + + /* Get fsinfo if available */ + fs->fsi_flag = 0x80; +#if (_FS_NOFSINFO & 3) != 3 + if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo == 1 */ + && LD_WORD(fs->win + BPB_FSInfo) == 1 + && move_window(fs, bsect + 1) == FR_OK) + { + fs->fsi_flag = 0; + if (LD_WORD(fs->win + BS_55AA) == 0xAA55 /* Load FSINFO data if available */ + && LD_DWORD(fs->win + FSI_LeadSig) == 0x41615252 + && LD_DWORD(fs->win + FSI_StrucSig) == 0x61417272) + { +#if (_FS_NOFSINFO & 1) == 0 + fs->free_clust = LD_DWORD(fs->win + FSI_Free_Count); +#endif +#if (_FS_NOFSINFO & 2) == 0 + fs->last_clust = LD_DWORD(fs->win + FSI_Nxt_Free); +#endif + } + } +#endif +#endif + fs->fs_type = fmt; /* FAT sub-type */ + fs->id = ++Fsid; /* File system mount ID */ +#if _FS_RPATH + fs->cdir = 0; /* Set current directory to root */ +#endif +#if _FS_LOCK /* Clear file lock semaphores */ + clear_lock(fs); +#endif + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/directory object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ + void* obj /* Pointer to the object FIL/FATFS_DIR to check validity */ +) +{ + FIL *fil = (FIL*)obj; /* Assuming offset of .fs and .id in the FIL/FATFS_DIR structure is identical */ + + + if (!fil || !fil->fs || !fil->fs->fs_type || fil->fs->id != fil->id || (disk_status(fil->fs->drv) & STA_NOINIT)) + return FR_INVALID_OBJECT; + + ENTER_FF(fil->fs); /* Lock file system */ + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +---------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + FATFS* fs, /* Pointer to the file system object (NULL:unmount)*/ + const TCHAR* path, /* Logical drive number to be mounted/unmounted */ + BYTE opt /* 0:Do not mount (delayed mount), 1:Mount immediately */ +) +{ + FATFS *cfs; + int vol; + FRESULT res; + const TCHAR *rp = path; + + + vol = get_ldnumber(&rp); + if (vol < 0) return FR_INVALID_DRIVE; + cfs = FatFs[vol]; /* Pointer to fs object */ + + if (cfs) { +#if _FS_LOCK + clear_lock(cfs); +#endif +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; +#endif + cfs->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((BYTE)vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + if (!fs || opt != 1) return FR_OK; /* Do not mount now, it will be mounted later */ + + res = find_volume(&fs, &path, 0); /* Force mounted the volume */ + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* 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; + DEFINE_NAMEBUF; +#if !_FS_READONLY + DWORD dw, cl; +#endif + + + if (!fp) return FR_INVALID_OBJECT; + fp->fs = 0; /* Clear file object */ + + /* Get logical drive number */ +#if !_FS_READONLY + mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW; + res = find_volume(&dj.fs, &path, (BYTE)(mode & ~FA_READ)); +#else + mode &= FA_READ; + res = find_volume(&dj.fs, &path, 0); +#endif + if (res == FR_OK) { + INIT_BUF(dj); + res = follow_path(&dj, path); /* Follow the file path */ + dir = dj.dir; +#if !_FS_READONLY /* R/W configuration */ + if (res == FR_OK) { + if (!dir) /* Default directory itself */ + res = FR_INVALID_NAME; +#if _FS_LOCK + 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)) { + 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_LOCK + 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(); + ST_DWORD(dir + DIR_CrtTime, dw);/* Set created time */ + ST_DWORD(dir + DIR_WrtTime, dw);/* Set modified time */ + dir[DIR_Attr] = 0; /* Reset attribute */ + ST_DWORD(dir + DIR_FileSize, 0);/* Reset file size */ + cl = ld_clust(dj.fs, dir); /* Get cluster chain */ + st_clust(dir, 0); /* Reset cluster */ + 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) { /* Following 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_LOCK + 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 */ + dir = dj.dir; + if (!dir) { /* Current directory 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->err = 0; /* Clear error flag */ + fp->sclust = ld_clust(dj.fs, 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; /* Validate file object */ + fp->id = fp->fs->id; + } + } + + 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; /* Clear read byte counter */ + + res = validate(fp); /* Check validity */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->err) /* Check error */ + LEAVE_FF(fp->fs, (FRESULT)fp->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, 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) - ((UINT)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) != FR_OK) /* 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; + bool need_sync = false; + + *bw = 0; /* Clear write byte counter */ + + res = validate(fp); /* Check validity */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->err) /* Check error */ + LEAVE_FF(fp->fs, (FRESULT)fp->err); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + if (fp->fptr + btw < fp->fptr) 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, */ + 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 (fp->sclust == 0) fp->sclust = clst; /* Set start cluster if the first write */ + +#if FLUSH_ON_NEW_CLUSTER + // We do not need to flush for the first cluster + if (fp->fptr != 0) { + need_sync = true; + } +#endif + } +#if _FS_TINY + if (fp->fs->winsect == fp->dsect && sync_window(fp->fs)) /* 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, cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if _FS_MINIMIZE <= 2 +#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 +#endif + wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ +#if FLUSH_ON_NEW_SECTOR + need_sync = true; +#endif + continue; + } +#if _FS_TINY + if (fp->fptr >= fp->fsize) { /* Avoid silly cache filling at growing edge */ + if (sync_window(fp->fs)) 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) - ((UINT)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) != FR_OK) /* 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 */ + + if (need_sync) { + f_sync (fp); + } + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD tm; + BYTE *dir; + + + res = validate(fp); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__WRITTEN) { /* Is there any change to the file? */ +#if !_FS_TINY + if (fp->flag & FA__DIRTY) { /* Write-back cached data if needed */ + 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 */ + tm = GET_FATTIME(); /* Update modified time */ + ST_DWORD(dir + DIR_WrtTime, tm); + ST_WORD(dir + DIR_LstAccDate, 0); + fp->flag &= ~FA__WRITTEN; + fp->fs->wflag = 1; + res = sync_fs(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 + res = f_sync(fp); /* Flush cached data */ + if (res == FR_OK) +#endif + { + res = validate(fp); /* Lock volume */ + if (res == FR_OK) { +#if _FS_REENTRANT + FATFS *fs = fp->fs; +#endif +#if _FS_LOCK + res = dec_lock(fp->lockid); /* Decrement file open counter */ + if (res == FR_OK) +#endif + fp->fs = 0; /* Invalidate file object */ +#if _FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Current Directory or Current Drive, Get Current Directory */ +/*-----------------------------------------------------------------------*/ + +#if _FS_RPATH >= 1 +#if _VOLUMES >= 2 +FRESULT f_chdrive ( + const TCHAR* path /* Drive number */ +) +{ + int vol; + + + vol = get_ldnumber(&path); + if (vol < 0) return FR_INVALID_DRIVE; + + CurrVol = (BYTE)vol; + + return FR_OK; +} +#endif + + +FRESULT f_chdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + FATFS_DIR dj; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 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.fs, 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* buff, /* Pointer to the directory path */ + UINT len /* Size of path */ +) +{ + FRESULT res; + FATFS_DIR dj; + UINT i, n; + DWORD ccl; + TCHAR *tp; + FILINFO fno; + DEFINE_NAMEBUF; + + + *buff = 0; + /* Get logical drive number */ + res = find_volume(&dj.fs, (const TCHAR**)&buff, 0); /* Get current volume */ + if (res == FR_OK) { + INIT_BUF(dj); + i = len; /* Bottom of buffer (directory stack base) */ + dj.sclust = dj.fs->cdir; /* Start to follow upper directory from current directory */ + while ((ccl = dj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ + res = dir_sdi(&dj, 1); /* Get parent directory */ + if (res != FR_OK) break; + res = dir_read(&dj, 0); + if (res != FR_OK) break; + dj.sclust = ld_clust(dj.fs, dj.dir); /* Goto parent directory */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child directory */ + res = dir_read(&dj, 0); + if (res != FR_OK) break; + if (ccl == ld_clust(dj.fs, 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 = buff; + fno.lfsize = i; +#endif + get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ + tp = fno.fname; +#if _USE_LFN + if (*buff) tp = buff; +#endif + for (n = 0; tp[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) buff[--i] = tp[--n]; + buff[--i] = '/'; + } + tp = buff; + if (res == FR_OK) { +#if _VOLUMES >= 2 + *tp++ = '0' + CurrVol; /* Put drive number */ + *tp++ = ':'; +#endif + if (i == len) { /* Root-directory */ + *tp++ = '/'; + } else { /* Sub-directroy */ + do /* Add stacked path str */ + *tp++ = buff[i++]; + while (i < len); + } + } + *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; + DWORD clst, bcs, nsect, ifptr; +#if _USE_FASTSEEK + DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; +#endif + + + res = validate(fp); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->err) /* Check error */ + LEAVE_FF(fp->fs, (FRESULT)fp->err); + +#if _USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + 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 */ + { + 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 Directory Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + FATFS_DIR* dp, /* Pointer to directory object to create */ + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + FATFS* fs; + DEFINE_NAMEBUF; + + + if (!dp) return FR_INVALID_OBJECT; + + /* Get logical drive number */ + res = find_volume(&fs, &path, 0); + if (res == FR_OK) { + dp->fs = fs; + INIT_BUF(*dp); + res = follow_path(dp, path); /* Follow the path to the directory */ + FREE_BUF(); + if (res == FR_OK) { /* Follow completed */ + if (dp->dir) { /* It is not the origin directory itself */ + if (dp->dir[DIR_Attr] & AM_DIR) /* The object is a sub directory */ + dp->sclust = ld_clust(fs, dp->dir); + else /* The object is a file */ + res = FR_NO_PATH; + } + if (res == FR_OK) { + dp->id = fs->id; + res = dir_sdi(dp, 0); /* Rewind directory */ +#if _FS_LOCK + if (res == FR_OK) { + if (dp->sclust) { + dp->lockid = inc_lock(dp, 0); /* Lock the sub directory */ + if (!dp->lockid) + res = FR_TOO_MANY_OPEN_FILES; + } else { + dp->lockid = 0; /* Root directory need not to be locked */ + } + } +#endif + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + if (res != FR_OK) dp->fs = 0; /* Invalidate the directory object if function faild */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Close Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_closedir ( + FATFS_DIR *dp /* Pointer to the directory object to be closed */ +) +{ + FRESULT res; + + + res = validate(dp); + if (res == FR_OK) { +#if _FS_REENTRANT + FATFS *fs = dp->fs; +#endif +#if _FS_LOCK + if (dp->lockid) /* Decrement sub-directory open counter */ + res = dec_lock(dp->lockid); + if (res == FR_OK) +#endif + dp->fs = 0; /* Invalidate directory object */ +#if _FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entries in Sequence */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + FATFS_DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DEFINE_NAMEBUF; + + + res = validate(dp); /* Check validity of the object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dp, 0); /* Rewind the directory object */ + } else { + INIT_BUF(*dp); + res = dir_read(dp, 0); /* Read an item */ + if (res == FR_NO_FILE) { /* Reached end of directory */ + dp->sect = 0; + res = FR_OK; + } + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dp, fno); /* Get the object information */ + res = dir_next(dp, 0); /* Increment index for next */ + if (res == FR_NO_FILE) { + dp->sect = 0; + res = FR_OK; + } + } + FREE_BUF(); + } + } + + LEAVE_FF(dp->fs, res); +} + + + +#if _USE_FIND +/*-----------------------------------------------------------------------*/ +/* Find next file */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findnext ( + FATFS_DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to the file information structure */ +) +{ + FRESULT res; + + + for (;;) { + res = f_readdir(dp, fno); /* Get a directory item */ + if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ +#if _USE_LFN + if (fno->lfname && pattern_matching(dp->pat, fno->lfname, 0, 0)) break; /* Test for LFN if exist */ +#endif + if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for SFN */ + } + return res; + +} + + + +/*-----------------------------------------------------------------------*/ +/* Find first file */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findfirst ( + FATFS_DIR* dp, /* Pointer to the blank directory object */ + FILINFO* fno, /* Pointer to the file information structure */ + const TCHAR* path, /* Pointer to the directory to open */ + const TCHAR* pattern /* Pointer to the matching pattern */ +) +{ + FRESULT res; + + + dp->pat = pattern; /* Save pointer to pattern string */ + res = f_opendir(dp, path); /* Open the target directory */ + if (res == FR_OK) + res = f_findnext(dp, fno); /* Find the first item */ + return res; +} + +#endif /* _USE_FIND */ + + + +#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; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 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 */ + if (fno) get_fileinfo(&dj, fno); + } else { /* It is root directory */ + 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, /* Path name of the logical drive number */ + DWORD* nclst, /* Pointer to a variable to return number of free clusters */ + FATFS** fatfs /* Pointer to return pointer to corresponding file system object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD nfree, clst, sect, stat; + UINT i; + BYTE fat, *p; + + + /* Get logical drive number */ + res = find_volume(fatfs, &path, 0); + fs = *fatfs; + if (res == FR_OK) { + /* If free_clust is valid, return it without full cluster scan */ + if (fs->free_clust <= fs->n_fatent - 2) { + *nclst = fs->free_clust; + } else { + /* Get number of free clusters */ + fat = fs->fs_type; + nfree = 0; + if (fat == FS_FAT12) { /* Sector unalighed entries: Search FAT via regular routine. */ + clst = 2; + do { + stat = get_fat(fs, clst); + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) nfree++; + } while (++clst < fs->n_fatent); + } else { /* Sector alighed entries: Accelerate the FAT search. */ + clst = fs->n_fatent; sect = fs->fatbase; + i = 0; p = 0; + do { + if (!i) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + p = fs->win; + i = SS(fs); + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) nfree++; + p += 2; i -= 2; + } else { + if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) nfree++; + p += 4; i -= 4; + } + } while (--clst); + } + fs->free_clust = nfree; /* free_clust is valid */ + fs->fsi_flag |= 1; /* FSInfo is to be updated */ + *nclst = nfree; /* Return the free clusters */ + } + } + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD ncl; + + + res = validate(fp); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->err) { /* Check error */ + res = (FRESULT)fp->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 !_FS_TINY + if (res == FR_OK && (fp->flag & FA__DIRTY)) { + if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) + res = FR_DISK_ERR; + else + fp->flag &= ~FA__DIRTY; + } +#endif + } + if (res != FR_OK) fp->err = (FRESULT)res; + } + + 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 = 0; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 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[NSFLAG] & NS_DOT)) + res = FR_INVALID_NAME; /* Cannot remove dot entry */ +#if _FS_LOCK + if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open object */ +#endif + if (res == FR_OK) { /* The object is accessible */ + dir = dj.dir; + if (!dir) { + res = FR_INVALID_NAME; /* Cannot remove the origin directory */ + } else { + if (dir[DIR_Attr] & AM_RDO) + res = FR_DENIED; /* Cannot remove R/O object */ + } + if (res == FR_OK) { + dclst = ld_clust(dj.fs, dir); + if (dclst && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-directory ? */ +#if _FS_RPATH + if (dclst == dj.fs->cdir) { /* Is it the current directory? */ + res = FR_DENIED; + } else +#endif + { + mem_cpy(&sdj, &dj, sizeof (FATFS_DIR)); /* Open the sub-directory */ + sdj.sclust = dclst; + res = dir_sdi(&sdj, 2); + if (res == FR_OK) { + res = dir_read(&sdj, 0); /* Read an item (excluding dot entries) */ + if (res == FR_OK) res = FR_DENIED; /* Not empty? (cannot remove) */ + if (res == FR_NO_FILE) res = FR_OK; /* Empty? (can remove) */ + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK && dclst) /* Remove the cluster chain if exist */ + res = remove_chain(dj.fs, dclst); + if (res == FR_OK) res = sync_fs(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, tm = GET_FATTIME(); + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 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[NSFLAG] & 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 = sync_window(dj.fs); + 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, ' ', 11); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + ST_DWORD(dir + DIR_WrtTime, tm); + st_clust(dir, dcl); + mem_cpy(dir + SZ_DIRE, dir, SZ_DIRE); /* Create ".." entry */ + dir[SZ_DIRE + 1] = '.'; pcl = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase) + pcl = 0; + st_clust(dir + SZ_DIRE, pcl); + for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + dj.fs->winsect = dsc++; + dj.fs->wflag = 1; + res = sync_window(dj.fs); + 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, tm); /* Created time */ + st_clust(dir, dcl); /* Table start cluster */ + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } + } + FREE_BUF(); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR* path, /* Pointer to the file path */ + BYTE attr, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + FATFS_DIR dj; + BYTE *dir; + DEFINE_NAMEBUF; + + + res = find_volume(&dj.fs, &path, 1); /* Get logical drive number */ + 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[NSFLAG] & 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] = (attr & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const TCHAR* path_old, /* Pointer to the object to be renamed */ + const TCHAR* path_new /* Pointer to the new name */ +) +{ + FRESULT res; + FATFS_DIR djo, djn; + BYTE buf[21], *dir; + DWORD dw; + DEFINE_NAMEBUF; + + + /* Get logical drive number of the source object */ + res = find_volume(&djo.fs, &path_old, 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[NSFLAG] & NS_DOT)) + res = FR_INVALID_NAME; +#if _FS_LOCK + 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 information about object except name */ + mem_cpy(&djn, &djo, sizeof (FATFS_DIR)); /* Duplicate the directory object */ + if (get_ldnumber(&path_new) >= 0) /* Snip drive number off and ignore it */ + res = follow_path(&djn, path_new); /* and make sure if new object name is not conflicting */ + else + res = FR_INVALID_DRIVE; + if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { +/* Start of critical section where any interruption can cause a cross-link */ + dir = djn.dir; /* Copy information about object except name */ + mem_cpy(dir + 13, buf + 2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + djo.fs->wflag = 1; + if ((dir[DIR_Attr] & AM_DIR) && djo.sclust != djn.sclust) { /* Update .. entry in the sub-directory if needed */ + dw = clust2sect(djo.fs, ld_clust(djo.fs, dir)); + if (!dw) { + res = FR_INT_ERR; + } else { + res = move_window(djo.fs, dw); + dir = djo.fs->win + SZ_DIRE * 1; /* Ptr to .. entry */ + if (res == FR_OK && dir[1] == '.') { + st_clust(dir, djn.sclust); + djo.fs->wflag = 1; + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) + res = sync_fs(djo.fs); + } +/* End of critical section */ + } + } + } + } + FREE_BUF(); + } + + LEAVE_FF(djo.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; + DEFINE_NAMEBUF; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 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[NSFLAG] & 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_fs(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + + +#if _USE_LABEL +/*-----------------------------------------------------------------------*/ +/* Get volume label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getlabel ( + const TCHAR* path, /* Path name of the logical drive number */ + TCHAR* label, /* Pointer to a buffer to return the volume label */ + DWORD* vsn /* Pointer to a variable to return the volume serial number */ +) +{ + FRESULT res; + FATFS_DIR dj; + UINT i, j; +#if _USE_LFN && _LFN_UNICODE + WCHAR w; +#endif + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &path, 0); + + /* Get volume label */ + if (res == FR_OK && label) { + dj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read(&dj, 1); /* Get an entry with AM_VOL */ + if (res == FR_OK) { /* A volume label is exist */ +#if _USE_LFN && _LFN_UNICODE + i = j = 0; + do { + w = (i < 11) ? dj.dir[i++] : ' '; + if (IsDBCS1(w) && i < 11 && IsDBCS2(dj.dir[i])) + w = w << 8 | dj.dir[i++]; + label[j++] = ff_convert(w, 1); /* OEM -> Unicode */ + } while (j < 11); +#else + mem_cpy(label, dj.dir, 11); +#endif + j = 11; + do { + label[j] = 0; + if (!j) break; + } while (label[--j] == ' '); + } + if (res == FR_NO_FILE) { /* No label, return nul string */ + label[0] = 0; + res = FR_OK; + } + } + } + + /* Get volume serial number */ + if (res == FR_OK && vsn) { + res = move_window(dj.fs, dj.fs->volbase); + if (res == FR_OK) { + i = dj.fs->fs_type == FS_FAT32 ? BS_VolID32 : BS_VolID; + *vsn = LD_DWORD(&dj.fs->win[i]); + } + } + + LEAVE_FF(dj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Set volume label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_setlabel ( + const TCHAR* label /* Pointer to the volume label to set */ +) +{ + FRESULT res; + FATFS_DIR dj; + BYTE vn[11]; + UINT i, j, sl; + WCHAR w; + DWORD tm; + + + /* Get logical drive number */ + res = find_volume(&dj.fs, &label, 1); + if (res) LEAVE_FF(dj.fs, res); + + /* Create a volume label in directory form */ + vn[0] = 0; + for (sl = 0; label[sl]; sl++) ; /* Get name length */ + for ( ; sl && label[sl - 1] == ' '; sl--) ; /* Remove trailing spaces */ + if (sl) { /* Create volume label in directory form */ + i = j = 0; + do { +#if _USE_LFN && _LFN_UNICODE + w = ff_convert(ff_wtoupper(label[i++]), 0); +#else + w = (BYTE)label[i++]; + if (IsDBCS1(w)) + w = (j < 10 && i < sl && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; +#if _USE_LFN + w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0); +#else + if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ +#ifdef _EXCVT + if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ +#else + if (!_DF1S && w >= 0x80) w = 0; /* Reject extended characters (ASCII cfg) */ +#endif +#endif +#endif + if (!w || chk_chr("\"*+,.:;<=>\?[]|\x7F", w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) /* Reject invalid characters for volume label */ + LEAVE_FF(dj.fs, FR_INVALID_NAME); + if (w >= 0x100) vn[j++] = (BYTE)(w >> 8); + vn[j++] = (BYTE)w; + } while (i < sl); + while (j < 11) vn[j++] = ' '; /* Fill remaining name field */ + if (vn[0] == DDEM) LEAVE_FF(dj.fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + } + + /* Set volume label */ + dj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read(&dj, 1); /* Get an entry with AM_VOL */ + if (res == FR_OK) { /* A volume label is found */ + if (vn[0]) { + mem_cpy(dj.dir, vn, 11); /* Change the volume label name */ + tm = GET_FATTIME(); + ST_DWORD(dj.dir + DIR_WrtTime, tm); + } else { + dj.dir[0] = DDEM; /* Remove the volume label */ + } + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } else { /* No volume label is found or error */ + if (res == FR_NO_FILE) { + res = FR_OK; + if (vn[0]) { /* Create volume label as new */ + res = dir_alloc(&dj, 1); /* Allocate an entry for volume label */ + if (res == FR_OK) { + mem_set(dj.dir, 0, SZ_DIRE); /* Set volume label */ + mem_cpy(dj.dir, vn, 11); + dj.dir[DIR_Attr] = AM_VOL; + tm = GET_FATTIME(); + ST_DWORD(dj.dir + DIR_WrtTime, tm); + dj.fs->wflag = 1; + res = sync_fs(dj.fs); + } + } + } + } + } + + LEAVE_FF(dj.fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_LABEL */ + + + +/*-----------------------------------------------------------------------*/ +/* 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 btf, /* 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; /* Clear transfer byte counter */ + + res = validate(fp); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->err) /* Check error */ + LEAVE_FF(fp->fs, (FRESULT)fp->err); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + + remain = fp->fsize - fp->fptr; + if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ + + for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream becomes busy */ + fp->fptr += rcnt, *bf += rcnt, btf -= 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) != FR_OK) /* 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 > btf) rcnt = btf; + 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 logical drive */ +/*-----------------------------------------------------------------------*/ +#define N_ROOTDIR 512 /* Number of root directory entries for FAT12/16 */ +#define N_FATS 1 /* Number of FATs (1 or 2) */ + + +FRESULT f_mkfs ( + const TCHAR* path, /* Logical drive number */ + BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */ + UINT au /* Size of allocation unit in unit of byte or sector */ +) +{ + 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}; + int vol; + 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; +#if _USE_TRIM + DWORD eb[2]; +#endif + + + /* Check mounted drive and clear work area */ + if (sfd > 1) return FR_INVALID_PARAMETER; + vol = get_ldnumber(&path); + if (vol < 0) return FR_INVALID_DRIVE; + fs = FatFs[vol]; + if (!fs) return FR_NOT_ENABLED; + fs->fs_type = 0; + pdrv = LD2PD(vol); /* Physical drive */ + part = LD2PT(vol); /* 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 != _MIN_SS /* Get disk sector size */ + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS || SS(fs) < _MIN_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 - 1)) au = 0; + if (!au) { /* AU auto selection */ + vs = n_vol / (2000 / (SS(fs) / 512)); + for (i = 0; vs < vst[i]; i++) ; + au = cst[i]; + } + if (au >= _MIN_SS) au /= SS(fs); /* Number of sectors per cluster */ + if (!au) au = 1; + if (au > 128) au = 128; + + /* Pre-compute number of clusters and FAT sub-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_DIRE / 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; + + /* Determine system ID in the partition table */ + if (fmt == FS_FAT32) { + sys = 0x0C; /* FAT32X */ + } else { + if (fmt == FS_FAT12 && n_vol < 0x10000) { + sys = 0x01; /* FAT12(<65536) */ + } else { + sys = (n_vol < 0x10000) ? 0x04 : 0x06; /* FAT16(<65536) : FAT12/16(>=65536) */ + } + } + + 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) /* Write it to teh MBR */ + return FR_DISK_ERR; + md = 0xF8; + } else { + if (sfd) { /* No partition table (SFD) */ + md = 0xF0; + } else { /* Create partition table (FDISK) */ + mem_set(fs->win, 0, SS(fs)); + tbl = fs->win + MBR_Table; /* Create partition 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); /* Partition 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 */ + 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 root directory 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 it to the 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 directory */ + } + 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 : (UINT)n_dir; + do { + if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) + return FR_DISK_ERR; + } while (--i); + +#if _USE_TRIM /* Erase data area if needed */ + { + eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1; + disk_ioctl(pdrv, CTRL_TRIM, 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 +/*-----------------------------------------------------------------------*/ +/* Create partition table on the 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] <= 100U) ? (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) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK; +} + + +#endif /* _MULTI_PARTITION */ +#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* fp /* Pointer to the file object */ +) +{ + int n = 0; + TCHAR c, *p = buff; + BYTE s[2]; + UINT rc; + + + while (n < len - 1) { /* Read characters until buffer gets filled */ +#if _USE_LFN && _LFN_UNICODE +#if _STRF_ENCODE == 3 /* Read a character in UTF-8 */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; + if (c >= 0x80) { + if (c < 0xC0) continue; /* Skip stray trailer */ + if (c < 0xE0) { /* Two-byte sequence */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = (c & 0x1F) << 6 | (s[0] & 0x3F); + if (c < 0x80) c = '?'; + } else { + if (c < 0xF0) { /* Three-byte sequence */ + f_read(fp, 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 sequence */ + c = '?'; + } + } + } +#elif _STRF_ENCODE == 2 /* Read a character in UTF-16BE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = s[1] + (s[0] << 8); +#elif _STRF_ENCODE == 1 /* Read a character in UTF-16LE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = s[0] + (s[1] << 8); +#else /* Read a character in ANSI/OEM */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; + if (IsDBCS1(c)) { + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = (c << 8) + s[0]; + } + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; +#endif +#else /* Read a character without conversion */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; +#endif + if (_USE_STRFUNC == 2 && c == '\r') continue; /* Strip '\r' */ + *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 */ +/*-----------------------------------------------------------------------*/ + +typedef struct { + FIL* fp; + int idx, nchr; + BYTE buf[64]; +} putbuff; + + +static +void putc_bfd ( + putbuff* pb, + TCHAR c +) +{ + UINT bw; + int i; + + + if (_USE_STRFUNC == 2 && c == '\n') /* LF -> CRLF conversion */ + putc_bfd(pb, '\r'); + + i = pb->idx; /* Buffer write index (-1:error) */ + if (i < 0) return; + +#if _USE_LFN && _LFN_UNICODE +#if _STRF_ENCODE == 3 /* Write a character in UTF-8 */ + if (c < 0x80) { /* 7-bit */ + pb->buf[i++] = (BYTE)c; + } else { + if (c < 0x800) { /* 11-bit */ + pb->buf[i++] = (BYTE)(0xC0 | c >> 6); + } else { /* 16-bit */ + pb->buf[i++] = (BYTE)(0xE0 | c >> 12); + pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F)); + } + pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F)); + } +#elif _STRF_ENCODE == 2 /* Write a character in UTF-16BE */ + pb->buf[i++] = (BYTE)(c >> 8); + pb->buf[i++] = (BYTE)c; +#elif _STRF_ENCODE == 1 /* Write a character in UTF-16LE */ + pb->buf[i++] = (BYTE)c; + pb->buf[i++] = (BYTE)(c >> 8); +#else /* Write a character in ANSI/OEM */ + c = ff_convert(c, 0); /* Unicode -> OEM */ + if (!c) c = '?'; + if (c >= 0x100) + pb->buf[i++] = (BYTE)(c >> 8); + pb->buf[i++] = (BYTE)c; +#endif +#else /* Write a character without conversion */ + pb->buf[i++] = (BYTE)c; +#endif + + if (i >= (int)(sizeof pb->buf) - 3) { /* Write buffered characters to the file */ + f_write(pb->fp, pb->buf, (UINT)i, &bw); + i = (bw == (UINT)i) ? 0 : -1; + } + pb->idx = i; + pb->nchr++; +} + + + +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + UINT nw; + + + pb.fp = fp; /* Initialize output buffer */ + pb.nchr = pb.idx = 0; + + putc_bfd(&pb, c); /* Put a character */ + + if ( pb.idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK + && (UINT)pb.idx == nw) return pb.nchr; + return EOF; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ + +int f_puts ( + const TCHAR* str, /* Pointer to the string to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + UINT nw; + + + pb.fp = fp; /* Initialize output buffer */ + pb.nchr = pb.idx = 0; + + while (*str) /* Put the string */ + putc_bfd(&pb, *str++); + + if ( pb.idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK + && (UINT)pb.idx == nw) return pb.nchr; + return EOF; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ + +int f_printf ( + FIL* fp, /* Pointer to the file object */ + const TCHAR* fmt, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + BYTE f, r; + UINT nw, i, j, w; + DWORD v; + TCHAR c, d, s[16], *p; + putbuff pb; + + + pb.fp = fp; /* Initialize output buffer */ + pb.nchr = pb.idx = 0; + + va_start(arp, fmt); + + for (;;) { + c = *fmt++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape character */ + putc_bfd(&pb, c); + continue; + } + w = f = 0; + c = *fmt++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *fmt++; + } else { + if (c == '-') { /* Flag: left justified */ + f = 2; c = *fmt++; + } + } + while (IsDigit(c)) { /* Precision */ + w = w * 10 + c - '0'; + c = *fmt++; + } + if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ + f |= 4; c = *fmt++; + } + 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++) ; + if (!(f & 2)) { + while (j++ < w) putc_bfd(&pb, ' '); + } + while (*p) putc_bfd(&pb, *p++); + while (j++ < w) putc_bfd(&pb, ' '); + continue; + case 'C' : /* Character */ + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); 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 (pass-through) */ + putc_bfd(&pb, c); continue; + } + + /* Get an argument and put it in numeral */ + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)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' : ' '; + while (!(f & 2) && j++ < w) putc_bfd(&pb, d); + do putc_bfd(&pb, s[--i]); while (i); + while (j++ < w) putc_bfd(&pb, d); + } + + va_end(arp); + + if ( pb.idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK + && (UINT)pb.idx == nw) return pb.nchr; + return EOF; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/ChaN/ff.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,350 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module include R0.11a (C)ChaN, 2015 +/----------------------------------------------------------------------------/ +/ FatFs module is a free software that opened under license policy of +/ following conditions. +/ +/ Copyright (C) 2015, ChaN, all right reserved. +/ +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/---------------------------------------------------------------------------*/ + + +#ifndef _FATFS +#define _FATFS 64180 /* 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) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */ +#define LD2PT(vol) 0 /* Find first valid 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 at 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 or 2) */ + BYTE wflag; /* win[] flag (b0:dirty) */ + BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ + WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ +#if _MAX_SS != _MIN_SS + 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 */ +#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 volbase; /* Volume start sector */ + 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 file data at tiny cfg) */ +} FATFS; + + + +/* File object structure (FIL) */ + +typedef struct { + FATFS* fs; /* Pointer to the related file system object (**do not change order**) */ + WORD id; /* Owner file system mount ID (**do not change order**) */ + BYTE flag; /* Status flags */ + BYTE err; /* Abort flag (error code) */ + DWORD fptr; /* File read/write pointer (Zeroed on file open) */ + DWORD fsize; /* File size */ + DWORD sclust; /* File start cluster (0:no cluster chain, always 0 when fsize is 0) */ + DWORD clust; /* Current cluster of fpter (not valid when fprt is 0) */ + DWORD dsect; /* Sector number appearing in buf[] (0:invalid) */ +#if !_FS_READONLY + DWORD dir_sect; /* Sector number containing the directory entry */ + BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */ +#endif +#if _USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */ +#endif +#if _FS_LOCK + UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ +#endif +#if !_FS_TINY + BYTE buf[_MAX_SS]; /* File private data read/write window */ +#endif +} FIL; + + + +/* Directory object structure (FATFS_DIR) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object (**do not change order**) */ + WORD id; /* Owner file system mount ID (**do not change order**) */ + 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 _FS_LOCK + UINT lockid; /* File lock ID (index of file semaphore table Files[]) */ +#endif +#if _USE_LFN + WCHAR* lfn; /* Pointer to the LFN working buffer */ + WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */ +#endif +#if _USE_FIND + const TCHAR* pat; /* Pointer to the name matching pattern */ +#endif +} FATFS_DIR; + + + +/* File information 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 occurred 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) Access denied due to prohibited access or directory full */ + FR_EXIST, /* (8) Access 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 sharing policy */ + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_LOCK */ + FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ +FRESULT f_close (FIL* fp); /* Close an open file object */ +FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */ +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */ +FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ +FRESULT f_lseek (FIL* fp, DWORD ofs); /* Move file pointer of a file object */ +FRESULT f_truncate (FIL* fp); /* Truncate file */ +FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */ +FRESULT f_opendir (FATFS_DIR* dp, const TCHAR* path); /* Open a directory */ +FRESULT f_closedir (FATFS_DIR* dp); /* Close an open directory */ +FRESULT f_readdir (FATFS_DIR* dp, FILINFO* fno); /* Read a directory item */ +FRESULT f_findfirst (FATFS_DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ +FRESULT f_findnext (FATFS_DIR* dp, FILINFO* fno); /* Find next file */ +FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ +FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ +FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ +FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ +FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of the file/dir */ +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change times-tamp of the file/dir */ +FRESULT f_chdir (const TCHAR* path); /* Change current directory */ +FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ +FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ +FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ +FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ +FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ +FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ +FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */ +FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */ +int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ +int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ +int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ +TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ + +#define f_eof(fp) ((int)((fp)->fptr == (fp)->fsize)) +#define f_error(fp) ((fp)->err) +#define f_tell(fp) ((fp)->fptr) +#define f_size(fp) ((fp)->fsize) +#define f_rewind(fp) f_lseek((fp), 0) +#define f_rewinddir(dp) f_readdir((dp), 0) + +#ifndef EOF +#define EOF (-1) +#endif + + + + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +#if !_FS_READONLY && !_FS_NORTC +DWORD get_fattime (void); +#endif + +/* Unicode support functions */ +#if _USE_LFN /* Unicode - OEM code conversion */ +WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */ +WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */ +#if _USE_LFN == 3 /* Memory functions */ +void* ff_memalloc (UINT msize); /* Allocate memory block */ +void ff_memfree (void* mblock); /* Free memory block */ +#endif +#endif + +/* Sync functions */ +#if _FS_REENTRANT +int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj); /* Create a sync object */ +int ff_req_grant (_SYNC_t sobj); /* Lock sync object */ +void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */ +int ff_del_syncobj (_SYNC_t sobj); /* 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 + +#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/ChaN/ffconf.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,285 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module configuration file R0.11a (C)ChaN, 2015 +/---------------------------------------------------------------------------*/ + +#define _FFCONF 64180 /* Revision ID */ + +#define FFS_DBG 0 + +/*---------------------------------------------------------------------------/ +/ Function Configurations +/---------------------------------------------------------------------------*/ + +#define _FS_READONLY 0 +/* This option switches read-only configuration. (0:Read/Write or 1:Read-only) +/ Read-only configuration removes writing API functions, f_write(), f_sync(), +/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() +/ and optional writing functions as well. */ + + +#define _FS_MINIMIZE 0 +/* This option defines minimization level to remove some basic API functions. +/ +/ 0: All basic functions are enabled. +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(), +/ f_truncate() and f_rename() function are removed. +/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. +/ 3: f_lseek() function is removed in addition to 2. */ + + +#define _USE_STRFUNC 0 +/* This option switches string functions, f_gets(), f_putc(), f_puts() and +/ f_printf(). +/ +/ 0: Disable string functions. +/ 1: Enable without LF-CRLF conversion. +/ 2: Enable with LF-CRLF conversion. */ + + +#define _USE_FIND 0 +/* This option switches filtered directory read feature and related functions, +/ f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */ + + +#define _USE_MKFS 1 +/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ + + +#define _USE_FASTSEEK 0 +/* This option switches fast seek feature. (0:Disable or 1:Enable) */ + + +#define _USE_LABEL 0 +/* This option switches volume label functions, f_getlabel() and f_setlabel(). +/ (0:Disable or 1:Enable) */ + + +#define _USE_FORWARD 0 +/* This option switches f_forward() function. (0:Disable or 1:Enable) +/ To enable it, also _FS_TINY need to be set to 1. */ + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/---------------------------------------------------------------------------*/ + +#define _CODE_PAGE 850 +/* This option specifies the OEM code page to be used on the target system. +/ Incorrect setting of the code page can cause a file open failure. +/ +/ 1 - ASCII (No extended character. Non-LFN cfg. only) +/ 437 - U.S. +/ 720 - Arabic +/ 737 - Greek +/ 771 - KBL +/ 775 - Baltic +/ 850 - Latin 1 +/ 852 - Latin 2 +/ 855 - Cyrillic +/ 857 - Turkish +/ 860 - Portuguese +/ 861 - Icelandic +/ 862 - Hebrew +/ 863 - Canadian French +/ 864 - Arabic +/ 865 - Nordic +/ 866 - Russian +/ 869 - Greek 2 +/ 932 - Japanese (DBCS) +/ 936 - Simplified Chinese (DBCS) +/ 949 - Korean (DBCS) +/ 950 - Traditional Chinese (DBCS) +*/ + + +#define _USE_LFN 1 +#define _MAX_LFN 255 +/* The _USE_LFN option switches the LFN feature. +/ +/ 0: Disable LFN feature. _MAX_LFN has no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ When enable the LFN feature, Unicode handling functions (option/unicode.c) must +/ be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. +/ When use stack for the working buffer, take care on stack overflow. When use heap +/ memory for the working buffer, memory management functions, ff_memalloc() and +/ ff_memfree(), must be added to the project. */ + + +#define _LFN_UNICODE 0 +/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode) +/ To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE +/ to 1. This option also affects behavior of string I/O functions. */ + + +#define _STRF_ENCODE 3 +/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to +/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). +/ +/ 0: ANSI/OEM +/ 1: UTF-16LE +/ 2: UTF-16BE +/ 3: UTF-8 +/ +/ When _LFN_UNICODE is 0, this option has no effect. */ + + +#define _FS_RPATH 0 +/* This option configures relative path feature. +/ +/ 0: Disable relative path feature and remove related functions. +/ 1: Enable relative path feature. f_chdir() and f_chdrive() are available. +/ 2: f_getcwd() function is available in addition to 1. +/ +/ Note that directory items read via f_readdir() are affected by this option. */ + + +/*---------------------------------------------------------------------------/ +/ Drive/Volume Configurations +/---------------------------------------------------------------------------*/ + +#define _VOLUMES 1 +/* Number of volumes (logical drives) to be used. */ + + +#define _STR_VOLUME_ID 0 +#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3" +/* _STR_VOLUME_ID option switches string volume ID feature. +/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive +/ number in the path name. _VOLUME_STRS defines the drive ID strings for each +/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for +/ the drive ID strings are: A-Z and 0-9. */ + + +#define _MULTI_PARTITION 0 +/* This option switches multi-partition feature. By default (0), each logical drive +/ number is bound to the same physical drive number and only an FAT volume found on +/ the physical drive will be mounted. When multi-partition feature is enabled (1), +/ each logical drive number is bound to arbitrary physical drive and partition +/ listed in the VolToPart[]. Also f_fdisk() funciton will be available. */ + + +#define _MIN_SS 512 +#define _MAX_SS 512 +/* These options configure the range of sector size to be supported. (512, 1024, +/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and +/ harddisk. But a larger value may be required for on-board flash memory and some +/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured +/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the +/ disk_ioctl() function. */ + + +#define _USE_TRIM 0 +/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable) +/ To enable Trim feature, also CTRL_TRIM command should be implemented to the +/ disk_ioctl() function. */ + + +#define _FS_NOFSINFO 0 +/* If you need to know correct free space on the FAT32 volume, set bit 0 of this +/ option, and f_getfree() function at first time after volume mount will force +/ a full FAT scan. Bit 1 controls the use of last allocated cluster number. +/ +/ bit0=0: Use free cluster count in the FSINFO if available. +/ bit0=1: Do not trust free cluster count in the FSINFO. +/ bit1=0: Use last allocated cluster number in the FSINFO if available. +/ bit1=1: Do not trust last allocated cluster number in the FSINFO. +*/ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/---------------------------------------------------------------------------*/ + +#define _FS_TINY 0 +/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) +/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS +/ bytes. Instead of private sector buffer eliminated from the file object, +/ common sector buffer in the file system object (FATFS) is used for the file +/ data transfer. */ + + +#define _FS_NORTC 0 +#define _NORTC_MON 1 +#define _NORTC_MDAY 1 +#define _NORTC_YEAR 2015 +/* The _FS_NORTC option switches timestamp feature. If the system does not have +/ an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable +/ the timestamp feature. All objects modified by FatFs will have a fixed timestamp +/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR. +/ When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need +/ to be added to the project to read current time form RTC. _NORTC_MON, +/ _NORTC_MDAY and _NORTC_YEAR have no effect. +/ These options have no effect at read-only configuration (_FS_READONLY == 1). */ + + +#define _FS_LOCK 0 +/* The _FS_LOCK option switches file lock feature to control duplicated file open +/ and illegal operation to open objects. This option must be 0 when _FS_READONLY +/ is 1. +/ +/ 0: Disable file lock feature. To avoid volume corruption, application program +/ should avoid illegal open, remove and rename to the open objects. +/ >0: Enable file lock feature. The value defines how many files/sub-directories +/ can be opened simultaneously under file lock control. Note that the file +/ lock feature is independent of re-entrancy. */ + + +#define _FS_REENTRANT 0 +#define _FS_TIMEOUT 1000 +#define _SYNC_t HANDLE +/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs +/ module itself. Note that regardless of this option, file access to different +/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs() +/ and f_fdisk() function, are always not re-entrant. Only file/directory access +/ to the same volume is under control of this feature. +/ +/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect. +/ 1: Enable re-entrancy. 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. Samples are available in +/ option/syscall.c. +/ +/ The _FS_TIMEOUT defines timeout period in unit of time tick. +/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, +/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be +/ included somewhere in the scope of ff.c. */ + + +#define _WORD_ACCESS 0 +/* The _WORD_ACCESS option is an only platform dependent option. It defines +/ which access method is used to the word data on the FAT volume. +/ +/ 0: Byte-by-byte access. Always compatible with all platforms. +/ 1: Word access. Do not choose this unless under both the following conditions. +/ +/ * Address misaligned memory access is always allowed to ALL instructions. +/ * Byte order on the memory is little-endian. +/ +/ If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size. +/ Following table shows allowable settings of some type of processors. +/ +/ ARM7TDMI 0 *2 ColdFire 0 *1 V850E 0 *2 +/ Cortex-M3 0 *3 Z80 0/1 V850ES 0/1 +/ Cortex-M0 0 *2 x86 0/1 TLCS-870 0/1 +/ AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1 +/ AVR32 0 *1 RL78 0 *2 R32C 0 *2 +/ PIC18 0/1 SH-2 0 *1 M16C 0/1 +/ PIC24 0 *2 H8S 0 *1 MSP430 0 *2 +/ PIC32 0 *1 H8/300H 0 *1 8051 0/1 +/ +/ *1:Big-endian. +/ *2:Unaligned memory access is not supported. +/ *3:Some compilers generate LDM/STM for mem_cpy function. +*/ + +#define FLUSH_ON_NEW_CLUSTER 0 /* Sync the file on every new cluster */ +#define FLUSH_ON_NEW_SECTOR 1 /* Sync the file on every new sector */ +/* Only one of these two defines needs to be set to 1. If both are set to 0 + the file is only sync when closed. + Clusters are group of sectors (eg: 8 sectors). Flushing on new cluster means + it would be less often than flushing on new sector. Sectors are generally + 512 Bytes long. */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/ChaN/integer.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,33 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _FF_INTEGER +#define _FF_INTEGER + +#ifdef _WIN32 /* Development platform */ + +#include <windows.h> +#include <tchar.h> + +#else /* Embedded platform */ + +/* This type MUST be 8-bit */ +typedef unsigned char BYTE; + +/* These types MUST be 16-bit */ +typedef short SHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; + +/* These types MUST be 16-bit or 32-bit */ +typedef int INT; +typedef unsigned int UINT; + +/* These types MUST be 32-bit */ +typedef long LONG; +typedef unsigned long DWORD; + +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/FATDirHandle.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,89 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * 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 <string.h> +#include "ff.h" +#include "FATDirHandle.h" + +using namespace mbed; + +FATDirHandle::FATDirHandle(const FATFS_DIR &the_dir) { + dir = the_dir; +} + +int FATDirHandle::closedir() { + int retval = f_closedir(&dir); + delete this; + return retval; +} + +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; +} + +ssize_t FATDirHandle::read(struct dirent *ent) { + struct dirent *temp = readdir(); + if (!temp) { + return 0; + } + + memcpy(ent, temp, sizeof(*ent)); + return 1; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/FATDirHandle.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,51 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * 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 MBED_FATDIRHANDLE_H +#define MBED_FATDIRHANDLE_H + +#include "DirHandle.h" + +using 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); + + virtual ssize_t read(struct dirent *ent); + virtual int close() { return closedir(); }; + virtual void seek(off_t offset) { seekdir(offset); }; + virtual off_t tell() { return telldir(); }; + virtual void rewind() { rewinddir(); }; + + private: + FATFS_DIR dir; + struct dirent cur_entry; + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/FATFileHandle.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,90 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * 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 "ff.h" +#include "ffconf.h" +#include "mbed_debug.h" + +#include "FATFileHandle.h" + +FATFileHandle::FATFileHandle(FIL fh) { + _fh = fh; +} + +int FATFileHandle::close() { + int retval = f_close(&_fh); + delete this; + return retval; +} + +ssize_t FATFileHandle::write(const void* buffer, size_t length) { + UINT n; + FRESULT res = f_write(&_fh, buffer, length, &n); + if (res) { + debug_if(FFS_DBG, "f_write() failed: %d", res); + return -1; + } + return n; +} + +ssize_t FATFileHandle::read(void* buffer, size_t length) { + debug_if(FFS_DBG, "read(%d)\n", length); + UINT n; + FRESULT res = f_read(&_fh, buffer, length, &n); + if (res) { + debug_if(FFS_DBG, "f_read() failed: %d\n", res); + return -1; + } + return n; +} + +int FATFileHandle::isatty() { + return 0; +} + +off_t FATFileHandle::lseek(off_t position, int whence) { + if (whence == SEEK_END) { + position += _fh.fsize; + } else if(whence==SEEK_CUR) { + position += _fh.fptr; + } + FRESULT res = f_lseek(&_fh, position); + if (res) { + debug_if(FFS_DBG, "lseek failed: %d\n", res); + return -1; + } else { + debug_if(FFS_DBG, "lseek OK, returning %i\n", _fh.fptr); + return _fh.fptr; + } +} + +int FATFileHandle::fsync() { + FRESULT res = f_sync(&_fh); + if (res) { + debug_if(FFS_DBG, "f_sync() failed: %d\n", res); + return -1; + } + return 0; +} + +off_t FATFileHandle::flen() { + return _fh.fsize; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/FATFileHandle.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,50 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * 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 MBED_FATFILEHANDLE_H +#define MBED_FATFILEHANDLE_H + +#include "FileHandle.h" + +using 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(); + + virtual off_t seek(off_t position, int whence) { return lseek(position, whence); } + virtual off_t size() { return flen(); } + +protected: + + FIL _fh; + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/FATFileSystem.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,174 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * 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" + +#include "ffconf.h" +#include "mbed_debug.h" + +#include "FATFileSystem.h" +#include "FATFileHandle.h" +#include "FATDirHandle.h" + +DWORD get_fattime(void) { + time_t rawtime; + time(&rawtime); + struct tm *ptm = localtime(&rawtime); + return (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 ); +} + +FATFileSystem *FATFileSystem::_ffs[_VOLUMES] = {0}; + +FATFileSystem::FATFileSystem(const char* n) : FileSystemLike(n) { + debug_if(FFS_DBG, "FATFileSystem(%s)\n", n); + for(int i=0; i<_VOLUMES; i++) { + if(_ffs[i] == 0) { + _ffs[i] = this; + _fsid[0] = '0' + i; + _fsid[1] = '\0'; + debug_if(FFS_DBG, "Mounting [%s] on ffs drive [%s]\n", getName(), _fsid); + f_mount(&_fs, _fsid, 0); + 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(NULL, _fsid, 0); + } + } +} + +FileHandle *FATFileSystem::open(const char* name, int flags) { + debug_if(FFS_DBG, "open(%s) on filesystem [%s], drv [%s]\n", name, getName(), _fsid); + char n[64]; + sprintf(n, "%s:/%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) { + debug_if(FFS_DBG, "f_open('w') failed: %d\n", res); + return NULL; + } + if (flags & O_APPEND) { + f_lseek(&fh, fh.fsize); + } + return new FATFileHandle(fh); +} + +int FATFileSystem::open(FileHandle **file, const char *name, int flags) { + FileHandle *temp = open(name, flags); + if (!temp) { + return -1; + } + + *file = temp; + return 0; +} + +int FATFileSystem::remove(const char *filename) { + FRESULT res = f_unlink(filename); + if (res) { + debug_if(FFS_DBG, "f_unlink() failed: %d\n", res); + return -1; + } + return 0; +} + +int FATFileSystem::rename(const char *oldname, const char *newname) { + FRESULT res = f_rename(oldname, newname); + if (res) { + debug_if(FFS_DBG, "f_rename() failed: %d\n", res); + return -1; + } + return 0; +} + +int FATFileSystem::format() { + FRESULT res = f_mkfs(_fsid, 0, 512); // Logical drive number, Partitioning rule, Allocation unit size (bytes per cluster) + if (res) { + debug_if(FFS_DBG, "f_mkfs() failed: %d\n", 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::open(DirHandle **dir, const char *name) { + DirHandle *temp = opendir(name); + if (!temp) { + return -1; + } + + *dir = temp; + return 0; +} + +int FATFileSystem::mkdir(const char *name, mode_t mode) { + FRESULT res = f_mkdir(name); + return res == 0 ? 0 : -1; +} + +int FATFileSystem::mount() { + FRESULT res = f_mount(&_fs, _fsid, 1); + return res == 0 ? 0 : -1; +} + +int FATFileSystem::unmount() { + if (disk_sync()) + return -1; + FRESULT res = f_mount(NULL, _fsid, 0); + return res == 0 ? 0 : -1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/FATFileSystem.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,96 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * 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 MBED_FATFILESYSTEM_H +#define MBED_FATFILESYSTEM_H + +#include "FileSystemLike.h" +#include "FileHandle.h" +#include "ff.h" +#include <stdint.h> + +using namespace mbed; + +/** + * FATFileSystem based on ChaN's Fat Filesystem library v0.8 + */ +class FATFileSystem : public FileSystemLike { +public: + + FATFileSystem(const char* n); + virtual ~FATFileSystem(); + + static FATFileSystem * _ffs[_VOLUMES]; // FATFileSystem objects, as parallel to FatFs drives array + FATFS _fs; // Work area (file system object) for logical drive + char _fsid[2]; + + /** + * Opens a file on the filesystem + */ + virtual FileHandle *open(const char* name, int flags); + virtual int open(FileHandle **file, const char *name, int flags); + + /** + * Removes a file path + */ + virtual int remove(const char *filename); + + /** + * Renames a file + */ + virtual int rename(const char *oldname, const char *newname); + + /** + * Formats a logical drive, FDISK artitioning rule, 512 bytes per cluster + */ + virtual int format(); + + /** + * Opens a directory on the filesystem + */ + virtual DirHandle *opendir(const char *name); + virtual int open(DirHandle **dir, const char *name); + + /** + * Creates a directory path + */ + virtual int mkdir(const char *name, mode_t mode); + + /** + * Mounts the filesystem + */ + virtual int mount(); + + /** + * Unmounts the filesystem + */ + virtual int unmount(); + + virtual int disk_initialize() { return 0; } + virtual int disk_status() { return 0; } + virtual int disk_read(uint8_t *buffer, uint32_t sector, uint32_t count) = 0; + virtual int disk_write(const uint8_t *buffer, uint32_t sector, uint32_t count) = 0; + virtual int disk_sync() { return 0; } + virtual uint32_t disk_sectors() = 0; + +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/MemFileSystem.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,77 @@ +/* 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 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDCard/SDFileSystem.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,441 @@ +/* mbed Microcontroller Library - SDFileSystem + * Copyright (c) 2008-2009, sford + */ + +// VERY DRAFT CODE! Needs serious rework/refactoring + +/* Introduction + * ------------ + * SD and MMC cards support a number of interfaces, but common to them all + * is one based on SPI. This is the one I'm implmenting because it means + * it is much more portable even though not so performant, and we already + * have the mbed SPI Interface! + * + * The main reference I'm using is Chapter 7, "SPI Mode" of: + * http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf + * + * SPI Startup + * ----------- + * The SD card powers up in SD mode. The SPI interface mode is selected by + * asserting CS low and sending the reset command (CMD0). The card will + * respond with a (R1) response. + * + * CMD8 is optionally sent to determine the voltage range supported, and + * indirectly determine whether it is a version 1.x SD/non-SD card or + * version 2.x. I'll just ignore this for now. + * + * ACMD41 is repeatedly issued to initialise the card, until "in idle" + * (bit 0) of the R1 response goes to '0', indicating it is initialised. + * + * You should also indicate whether the host supports High Capicity cards, + * and check whether the card is high capacity - i'll also ignore this + * + * SPI Protocol + * ------------ + * The SD SPI protocol is based on transactions made up of 8-bit words, with + * the host starting every bus transaction by asserting the CS signal low. The + * card always responds to commands, data blocks and errors. + * + * The protocol supports a CRC, but by default it is off (except for the + * first reset CMD0, where the CRC can just be pre-calculated, and CMD8) + * I'll leave the CRC off I think! + * + * Standard capacity cards have variable data block sizes, whereas High + * Capacity cards fix the size of data block to 512 bytes. I'll therefore + * just always use the Standard Capacity cards with a block size of 512 bytes. + * This is set with CMD16. + * + * You can read and write single blocks (CMD17, CMD25) or multiple blocks + * (CMD18, CMD25). For simplicity, I'll just use single block accesses. When + * the card gets a read command, it responds with a response token, and then + * a data token or an error. + * + * SPI Command Format + * ------------------ + * Commands are 6-bytes long, containing the command, 32-bit argument, and CRC. + * + * +---------------+------------+------------+-----------+----------+--------------+ + * | 01 | cmd[5:0] | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | crc[6:0] | 1 | + * +---------------+------------+------------+-----------+----------+--------------+ + * + * As I'm not using CRC, I can fix that byte to what is needed for CMD0 (0x95) + * + * All Application Specific commands shall be preceded with APP_CMD (CMD55). + * + * SPI Response Format + * ------------------- + * The main response format (R1) is a status byte (normally zero). Key flags: + * idle - 1 if the card is in an idle state/initialising + * cmd - 1 if an illegal command code was detected + * + * +-------------------------------------------------+ + * R1 | 0 | arg | addr | seq | crc | cmd | erase | idle | + * +-------------------------------------------------+ + * + * R1b is the same, except it is followed by a busy signal (zeros) until + * the first non-zero byte when it is ready again. + * + * Data Response Token + * ------------------- + * Every data block written to the card is acknowledged by a byte + * response token + * + * +----------------------+ + * | xxx | 0 | status | 1 | + * +----------------------+ + * 010 - OK! + * 101 - CRC Error + * 110 - Write Error + * + * Single Block Read and Write + * --------------------------- + * + * Block transfers have a byte header, followed by the data, followed + * by a 16-bit CRC. In our case, the data will always be 512 bytes. + * + * +------+---------+---------+- - - -+---------+-----------+----------+ + * | 0xFE | data[0] | data[1] | | data[n] | crc[15:8] | crc[7:0] | + * +------+---------+---------+- - - -+---------+-----------+----------+ + */ + +#include "SDFileSystem.h" +#include <cstdint> +#define SD_COMMAND_TIMEOUT 5000 + +SDFileSystem::SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name) : + FATFileSystem(name), _spi(mosi, miso, sclk), _cs(cs) { + _cs = 1; +} + +#define R1_IDLE_STATE (1 << 0) +#define R1_ERASE_RESET (1 << 1) +#define R1_ILLEGAL_COMMAND (1 << 2) +#define R1_COM_CRC_ERROR (1 << 3) +#define R1_ERASE_SEQUENCE_ERROR (1 << 4) +#define R1_ADDRESS_ERROR (1 << 5) +#define R1_PARAMETER_ERROR (1 << 6) + +// Types +// - v1.x Standard Capacity +// - v2.x Standard Capacity +// - v2.x High Capacity +// - Not recognised as an SD Card + +#define SDCARD_FAIL 0 +#define SDCARD_V1 1 +#define SDCARD_V2 2 +#define SDCARD_V2HC 3 + +int SDFileSystem::initialise_card() { + // Set to 100kHz for initialisation, and clock card with cs = 1 + _spi.frequency(100000); + _cs = 1; + for(int i=0; i<16; i++) { + _spi.write(0xFF); + } + + // send CMD0, should return with all zeros except IDLE STATE set (bit 0) + if(_cmd(0, 0) != R1_IDLE_STATE) { + fprintf(stderr, "No disk, or could not put SD card in to SPI idle state\n"); + return SDCARD_FAIL; + } + + // send CMD8 to determine whther it is ver 2.x + int r = _cmd8(); + if(r == R1_IDLE_STATE) { + return initialise_card_v2(); + } else if(r == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND)) { + return initialise_card_v1(); + } else { + fprintf(stderr, "Not in idle state after sending CMD8 (not an SD card?)\n"); + return SDCARD_FAIL; + } +} + +int SDFileSystem::initialise_card_v1() { + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + _cmd(55, 0); + if(_cmd(41, 0) == 0) { + return SDCARD_V1; + } + } + + fprintf(stderr, "Timeout waiting for v1.x card\n"); + return SDCARD_FAIL; +} + +int SDFileSystem::initialise_card_v2() { + + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + _cmd(55, 0); + if(_cmd(41, 0) == 0) { + _cmd58(); + return SDCARD_V2; + } + } + + fprintf(stderr, "Timeout waiting for v2.x card\n"); + return SDCARD_FAIL; +} + +int SDFileSystem::disk_initialize() { + + int i = initialise_card(); +// printf("init card = %d\n", i); +// printf("OK\n"); + + _sectors = _sd_sectors(); + + // Set block length to 512 (CMD16) + if(_cmd(16, 512) != 0) { + fprintf(stderr, "Set 512-byte block timed out\n"); + return 1; + } + + _spi.frequency(1000000); // Set to 1MHz for data transfer + return 0; +} + +int SDFileSystem::disk_write(const char *buffer, int block_number) { + // set write address for single block (CMD24) + if(_cmd(24, block_number * 512) != 0) { + return 1; + } + + // send the data block + _write(buffer, 512); + return 0; +} + +int SDFileSystem::disk_read(char *buffer, int block_number) { + // set read address for single block (CMD17) + if(_cmd(17, block_number * 512) != 0) { + return 1; + } + + // receive the data + _read(buffer, 512); + return 0; +} + +int SDFileSystem::disk_status() { return 0; } +int SDFileSystem::disk_sync() { return 0; } +std::uint32_t SDFileSystem::disk_sectors() { return _sectors; } + +// PRIVATE FUNCTIONS + +int SDFileSystem::_cmd(int cmd, int arg) { + _cs = 0; + + // send a command + _spi.write(0x40 | cmd); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if(!(response & 0x80)) { + _cs = 1; + _spi.write(0xFF); + return response; + } + } + _cs = 1; + _spi.write(0xFF); + return -1; // timeout +} +int SDFileSystem::_cmdx(int cmd, int arg) { + _cs = 0; + + // send a command + _spi.write(0x40 | cmd); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if(!(response & 0x80)) { + return response; + } + } + _cs = 1; + _spi.write(0xFF); + return -1; // timeout +} + + +int SDFileSystem::_cmd58() { + _cs = 0; + int arg = 0; + + // send a command + _spi.write(0x40 | 58); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if(!(response & 0x80)) { + int ocr = _spi.write(0xFF) << 24; + ocr |= _spi.write(0xFF) << 16; + ocr |= _spi.write(0xFF) << 8; + ocr |= _spi.write(0xFF) << 0; +// printf("OCR = 0x%08X\n", ocr); + _cs = 1; + _spi.write(0xFF); + return response; + } + } + _cs = 1; + _spi.write(0xFF); + return -1; // timeout +} + +int SDFileSystem::_cmd8() { + _cs = 0; + + // send a command + _spi.write(0x40 | 8); // CMD8 + _spi.write(0x00); // reserved + _spi.write(0x00); // reserved + _spi.write(0x01); // 3.3v + _spi.write(0xAA); // check pattern + _spi.write(0x87); // crc + + // wait for the repsonse (response[7] == 0) + for(int i=0; i<SD_COMMAND_TIMEOUT * 1000; i++) { + char response[5]; + response[0] = _spi.write(0xFF); + if(!(response[0] & 0x80)) { + for(int j=1; j<5; j++) { + response[i] = _spi.write(0xFF); + } + _cs = 1; + _spi.write(0xFF); + return response[0]; + } + } + _cs = 1; + _spi.write(0xFF); + return -1; // timeout +} + +int SDFileSystem::_read(char *buffer, int length) { + _cs = 0; + + // read until start byte (0xFF) + while(_spi.write(0xFF) != 0xFE); + + // read data + for(int i=0; i<length; i++) { + buffer[i] = _spi.write(0xFF); + } + _spi.write(0xFF); // checksum + _spi.write(0xFF); + + _cs = 1; + _spi.write(0xFF); + return 0; +} + +int SDFileSystem::_write(const char *buffer, int length) { + _cs = 0; + + // indicate start of block + _spi.write(0xFE); + + // write the data + for(int i=0; i<length; i++) { + _spi.write(buffer[i]); + } + + // write the checksum + _spi.write(0xFF); + _spi.write(0xFF); + + // check the repsonse token + if((_spi.write(0xFF) & 0x1F) != 0x05) { + _cs = 1; + _spi.write(0xFF); + return 1; + } + + // wait for write to finish + while(_spi.write(0xFF) == 0); + + _cs = 1; + _spi.write(0xFF); + return 0; +} + +static int ext_bits(char *data, int msb, int lsb) { + int bits = 0; + int size = 1 + msb - lsb; + for(int i=0; i<size; i++) { + int position = lsb + i; + int byte = 15 - (position >> 3); + int bit = position & 0x7; + int value = (data[byte] >> bit) & 1; + bits |= value << i; + } + return bits; +} + +int SDFileSystem::_sd_sectors() { + + // CMD9, Response R2 (R1 byte + 16-byte block read) + if(_cmdx(9, 0) != 0) { + fprintf(stderr, "Didn't get a response from the disk\n"); + return 0; + } + + char csd[16]; + if(_read(csd, 16) != 0) { + fprintf(stderr, "Couldn't read csd response from disk\n"); + return 0; + } + + // csd_structure : csd[127:126] + // c_size : csd[73:62] + // c_size_mult : csd[49:47] + // read_bl_len : csd[83:80] - the *maximum* read block length + + int csd_structure = ext_bits(csd, 127, 126); + int c_size = ext_bits(csd, 73, 62); + int c_size_mult = ext_bits(csd, 49, 47); + int read_bl_len = ext_bits(csd, 83, 80); + +// printf("CSD_STRUCT = %d\n", csd_structure); + + if(csd_structure != 0) { + fprintf(stderr, "This disk tastes funny! I only know about type 0 CSD structures\n"); + return 0; + } + + // memory capacity = BLOCKNR * BLOCK_LEN + // where + // BLOCKNR = (C_SIZE+1) * MULT + // MULT = 2^(C_SIZE_MULT+2) (C_SIZE_MULT < 8) + // BLOCK_LEN = 2^READ_BL_LEN, (READ_BL_LEN < 12) + + int block_len = 1 << read_bl_len; + int mult = 1 << (c_size_mult + 2); + int blocknr = (c_size + 1) * mult; + int capacity = blocknr * block_len; + + int blocks = capacity / 512; + + return blocks; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDCard/SDFileSystem.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,66 @@ +/* mbed Microcontroller Library - SDFileSystem + * Copyright (c) 2008-2009, sford + */ + +// VERY DRAFT CODE!!! + +#ifndef SDFILESYSTEM_H +#define SDFILESYSTEM_H +#include <cstdint> +#include "mbed.h" +#include "FATFileSystem.h" + +/* Class: SDFileSystem + * Access the filesystem on an SD Card using SPI + * + * Example: + * > SDFileSystem sd(p5, p6, p7, p12, "sd"); + * > + * > int main() { + * > FILE *fp = fopen("/sd/myfile.txt", "w"); + * > fprintf(fp, "Hello World!\n"); + * > fclose(fp); + * > } + */ +class SDFileSystem : public FATFileSystem { +public: + + /* Constructor: SDFileSystem + * Create the File System for accessing an SD Card using SPI + * + * Variables: + * mosi - SPI mosi pin connected to SD Card + * miso - SPI miso pin conencted to SD Card + * sclk - SPI sclk pin connected to SD Card + * cs - DigitalOut pin used as SD Card chip select + * name - The name used to access the filesystem + */ + SDFileSystem(PinName mosi, PinName miso, PinName sclk, PinName cs, const char* name); + virtual int disk_initialize(); + virtual int disk_write(const char *buffer, int block_number); + virtual int disk_read(char *buffer, int block_number); + virtual int disk_status(); + virtual int disk_sync(); +virtual std::uint32_t disk_sectors(); + +protected: + + int _cmd(int cmd, int arg); + int _cmdx(int cmd, int arg); + int _cmd8(); + int _cmd58(); + int initialise_card(); + int initialise_card_v1(); + int initialise_card_v2(); + + + int _read(char *buffer, int length); + int _write(const char *buffer, int length); + int _sd_sectors(); + int _sectors; + + SPI _spi; + DigitalOut _cs; +}; + +#endif
--- a/SX1276GenericLib.lib Fri Aug 18 07:45:44 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://developer.mbed.org/users/Helmut64/code/SX1276GenericLib/#49d19df5bbce
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/Arduino-mbed-APIs/Callback-A.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,4228 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifdef ARDUINO + +#ifndef MBED_CALLBACK_H +#define MBED_CALLBACK_H + + +#include <string.h> +#include <stdint.h> +#include <new> +//#include "platform/mbed_assert.h" +//#include "platform/mbed_toolchain.h" +#ifdef ARDUINO +#undef F +#endif + +// namespace mbed { +/** \addtogroup platform */ + + +/** Callback class based on template specialization + * + * @note Synchronization level: Not protected + * @ingroup platform + */ +template <typename F> +class Callback; + +// Internal sfinae declarations +// +// These are used to eliminate overloads based on type attributes +// 1. Does a function object have a call operator +// 2. Does a function object fit in the available storage +// +// These eliminations are handled cleanly by the compiler and avoid +// massive and misleading error messages when confronted with an +// invalid type (or worse, runtime failures) +namespace detail { + struct nil {}; + + template <bool B, typename R = nil> + struct enable_if { typedef R type; }; + + template <typename R> + struct enable_if<false, R> {}; + + template <typename M, M> + struct is_type { + static const bool value = true; + }; +} + +#define MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, M) \ + typename detail::enable_if< \ + detail::is_type<M, &F::operator()>::value && \ + sizeof(F) <= sizeof(uintptr_t) \ + >::type = detail::nil() + +/** Callback class based on template specialization + * + * @note Synchronization level: Not protected + * @ingroup platform + */ +template <typename R> +class Callback<R()> { +public: + /** Create a Callback with a static function + * @param func Static function to attach + */ + Callback(R (*func)() = 0) { + if (!func) { + _ops = 0; + } else { + generate(func); + } + } + + /** Attach a Callback + * @param func The Callback to attach + */ + Callback(const Callback<R()> &func) { + if (func._ops) { + func._ops->move(this, &func); + } + _ops = func._ops; + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(U *obj, R (T::*method)()) { + generate(method_context<T, R (T::*)()>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(const U *obj, R (T::*method)() const) { + generate(method_context<const T, R (T::*)() const>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(volatile U *obj, R (T::*method)() volatile) { + generate(method_context<volatile T, R (T::*)() volatile>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(const volatile U *obj, R (T::*method)() const volatile) { + generate(method_context<const volatile T, R (T::*)() const volatile>(obj, method)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(T*), U *arg) { + generate(function_context<R (*)(T*), T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(const T*), const U *arg) { + generate(function_context<R (*)(const T*), const T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(volatile T*), volatile U *arg) { + generate(function_context<R (*)(volatile T*), volatile T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(const volatile T*), const volatile U *arg) { + generate(function_context<R (*)(const volatile T*), const volatile T>(func, arg)); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)())) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)() const)) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)() volatile)) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)() const volatile)) { + generate(f); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(U *obj, R (*func)(T*)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(const U *obj, R (*func)(const T*)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(volatile U *obj, R (*func)(volatile T*)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(const volatile U *obj, R (*func)(const volatile T*)) { + new (this) Callback(func, obj); + } + + /** Destroy a callback + */ + ~Callback() { + if (_ops) { + _ops->dtor(this); + } + } + + /** Attach a static function + * @param func Static function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + void attach(R (*func)()) { + this->~Callback(); + new (this) Callback(func); + } + + /** Attach a Callback + * @param func The Callback to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + void attach(const Callback<R()> &func) { + this->~Callback(); + new (this) Callback(func); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(U *obj, R (T::*method)()) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(const U *obj, R (T::*method)() const) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(volatile U *obj, R (T::*method)() volatile) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(const volatile U *obj, R (T::*method)() const volatile) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(T*), U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(const T*), const U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(volatile T*), volatile U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(const volatile T*), const volatile U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)())) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)() const)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)() volatile)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)() const volatile)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(U *obj, R (*func)(T*)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(const U *obj, R (*func)(const T*)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(volatile U *obj, R (*func)(volatile T*)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(const volatile U *obj, R (*func)(const volatile T*)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Assign a callback + */ + Callback &operator=(const Callback &that) { + if (this != &that) { + this->~Callback(); + new (this) Callback(that); + } + + return *this; + } + + /** Call the attached function + */ + R call() const { + return _ops->call(this); + } + + /** Call the attached function + */ + R operator()() const { + return call(); + } + + /** Test if function has been attached + */ + operator bool() const { + return _ops; + } + + /** Test for equality + */ + friend bool operator==(const Callback &l, const Callback &r) { + return memcmp(&l, &r, sizeof(Callback)) == 0; + } + + /** Test for inequality + */ + friend bool operator!=(const Callback &l, const Callback &r) { + return !(l == r); + } + + /** Static thunk for passing as C-style function + * @param func Callback to call passed as void pointer + * @return the value as determined by func which is of + * type and determined by the signiture of func + */ + static R thunk(void *func) { + return static_cast<Callback*>(func)->call(); + } + +private: + // Stored as pointer to function and pointer to optional object + // Function pointer is stored as union of possible function types + // to garuntee proper size and alignment + struct _class; + union { + void (*_staticfunc)(); + void (*_boundfunc)(_class*); + void (_class::*_methodfunc)(); + } _func; + void *_obj; + + // Dynamically dispatched operations + const struct ops { + R (*call)(const void*); + void (*move)(void*, const void*); + void (*dtor)(void*); + } *_ops; + + + // Generate operations for function object + template <typename F> + void generate(const F &f) { + static const ops ops = { + &Callback::function_call<F>, + &Callback::function_move<F>, + &Callback::function_dtor<F>, + }; + //MBED_STATIC_ASSERT(sizeof(Callback) - sizeof(_ops) >= sizeof(F), + // "Type F must not exceed the size of the Callback class"); + new (this) F(f); + _ops = &ops; + } + + // Function attributes + template <typename F> + static R function_call(const void *p) { + return (*(F*)p)(); + } + + template <typename F> + static void function_move(void *d, const void *p) { + new (d) F(*(F*)p); + } + + template <typename F> + static void function_dtor(void *p) { + ((F*)p)->~F(); + } + + // Wrappers for functions with context + template <typename O, typename M> + struct method_context { + M method; + O *obj; + + method_context(O *obj, M method) + : method(method), obj(obj) {} + + R operator()() const { + return (obj->*method)(); + } + }; + + template <typename F, typename A> + struct function_context { + F func; + A *arg; + + function_context(F func, A *arg) + : func(func), arg(arg) {} + + R operator()() const { + return func(arg); + } + }; +}; + +/** Callback class based on template specialization + * + * @note Synchronization level: Not protected + * @ingroup platform + */ +template <typename R, typename A0> +class Callback<R(A0)> { +public: + /** Create a Callback with a static function + * @param func Static function to attach + */ + Callback(R (*func)(A0) = 0) { + if (!func) { + _ops = 0; + } else { + generate(func); + } + } + + /** Attach a Callback + * @param func The Callback to attach + */ + Callback(const Callback<R(A0)> &func) { + if (func._ops) { + func._ops->move(this, &func); + } + _ops = func._ops; + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(U *obj, R (T::*method)(A0)) { + generate(method_context<T, R (T::*)(A0)>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(const U *obj, R (T::*method)(A0) const) { + generate(method_context<const T, R (T::*)(A0) const>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(volatile U *obj, R (T::*method)(A0) volatile) { + generate(method_context<volatile T, R (T::*)(A0) volatile>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(const volatile U *obj, R (T::*method)(A0) const volatile) { + generate(method_context<const volatile T, R (T::*)(A0) const volatile>(obj, method)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(T*, A0), U *arg) { + generate(function_context<R (*)(T*, A0), T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(const T*, A0), const U *arg) { + generate(function_context<R (*)(const T*, A0), const T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(volatile T*, A0), volatile U *arg) { + generate(function_context<R (*)(volatile T*, A0), volatile T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(const volatile T*, A0), const volatile U *arg) { + generate(function_context<R (*)(const volatile T*, A0), const volatile T>(func, arg)); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0))) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0) const)) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0) volatile)) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0) const volatile)) { + generate(f); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(U *obj, R (*func)(T*, A0)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(const U *obj, R (*func)(const T*, A0)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(volatile U *obj, R (*func)(volatile T*, A0)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(const volatile U *obj, R (*func)(const volatile T*, A0)) { + new (this) Callback(func, obj); + } + + /** Destroy a callback + */ + ~Callback() { + if (_ops) { + _ops->dtor(this); + } + } + + /** Attach a static function + * @param func Static function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + void attach(R (*func)(A0)) { + this->~Callback(); + new (this) Callback(func); + } + + /** Attach a Callback + * @param func The Callback to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + void attach(const Callback<R(A0)> &func) { + this->~Callback(); + new (this) Callback(func); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(U *obj, R (T::*method)(A0)) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(const U *obj, R (T::*method)(A0) const) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(volatile U *obj, R (T::*method)(A0) volatile) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(const volatile U *obj, R (T::*method)(A0) const volatile) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(T*, A0), U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(const T*, A0), const U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(volatile T*, A0), volatile U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(const volatile T*, A0), const volatile U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0))) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0) const)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0) volatile)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0) const volatile)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(U *obj, R (*func)(T*, A0)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(const U *obj, R (*func)(const T*, A0)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(volatile U *obj, R (*func)(volatile T*, A0)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(const volatile U *obj, R (*func)(const volatile T*, A0)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Assign a callback + */ + Callback &operator=(const Callback &that) { + if (this != &that) { + this->~Callback(); + new (this) Callback(that); + } + + return *this; + } + + /** Call the attached function + */ + R call(A0 a0) const { + MBED_ASSERT(_ops); + return _ops->call(this, a0); + } + + /** Call the attached function + */ + R operator()(A0 a0) const { + return call(a0); + } + + /** Test if function has been attached + */ + operator bool() const { + return _ops; + } + + /** Test for equality + */ + friend bool operator==(const Callback &l, const Callback &r) { + return memcmp(&l, &r, sizeof(Callback)) == 0; + } + + /** Test for inequality + */ + friend bool operator!=(const Callback &l, const Callback &r) { + return !(l == r); + } + + /** Static thunk for passing as C-style function + * @param func Callback to call passed as void pointer + * @param a0 An argument to be called with function func + * @return the value as determined by func which is of + * type and determined by the signiture of func + */ + static R thunk(void *func, A0 a0) { + return static_cast<Callback*>(func)->call(a0); + } + +private: + // Stored as pointer to function and pointer to optional object + // Function pointer is stored as union of possible function types + // to garuntee proper size and alignment + struct _class; + union { + void (*_staticfunc)(A0); + void (*_boundfunc)(_class*, A0); + void (_class::*_methodfunc)(A0); + } _func; + void *_obj; + + // Dynamically dispatched operations + const struct ops { + R (*call)(const void*, A0); + void (*move)(void*, const void*); + void (*dtor)(void*); + } *_ops; + + // Generate operations for function object + template <typename F> + void generate(const F &f) { + static const ops ops = { + &Callback::function_call<F>, + &Callback::function_move<F>, + &Callback::function_dtor<F>, + }; + new (this) F(f); + _ops = &ops; + } + + // Function attributes + template <typename F> + static R function_call(const void *p, A0 a0) { + return (*(F*)p)(a0); + } + + template <typename F> + static void function_move(void *d, const void *p) { + new (d) F(*(F*)p); + } + + template <typename F> + static void function_dtor(void *p) { + ((F*)p)->~F(); + } + + // Wrappers for functions with context + template <typename O, typename M> + struct method_context { + M method; + O *obj; + + method_context(O *obj, M method) + : method(method), obj(obj) {} + + R operator()(A0 a0) const { + return (obj->*method)(a0); + } + }; + + template <typename F, typename A> + struct function_context { + F func; + A *arg; + + function_context(F func, A *arg) + : func(func), arg(arg) {} + + R operator()(A0 a0) const { + return func(arg, a0); + } + }; +}; + +/** Callback class based on template specialization + * + * @note Synchronization level: Not protected + * @ingroup platform + */ +template <typename R, typename A0, typename A1> +class Callback<R(A0, A1)> { +public: + /** Create a Callback with a static function + * @param func Static function to attach + */ + Callback(R (*func)(A0, A1) = 0) { + if (!func) { + _ops = 0; + } else { + generate(func); + } + } + + /** Attach a Callback + * @param func The Callback to attach + */ + Callback(const Callback<R(A0, A1)> &func) { + if (func._ops) { + func._ops->move(this, &func); + } + _ops = func._ops; + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(U *obj, R (T::*method)(A0, A1)) { + generate(method_context<T, R (T::*)(A0, A1)>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(const U *obj, R (T::*method)(A0, A1) const) { + generate(method_context<const T, R (T::*)(A0, A1) const>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(volatile U *obj, R (T::*method)(A0, A1) volatile) { + generate(method_context<volatile T, R (T::*)(A0, A1) volatile>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(const volatile U *obj, R (T::*method)(A0, A1) const volatile) { + generate(method_context<const volatile T, R (T::*)(A0, A1) const volatile>(obj, method)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(T*, A0, A1), U *arg) { + generate(function_context<R (*)(T*, A0, A1), T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(const T*, A0, A1), const U *arg) { + generate(function_context<R (*)(const T*, A0, A1), const T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(volatile T*, A0, A1), volatile U *arg) { + generate(function_context<R (*)(volatile T*, A0, A1), volatile T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(const volatile T*, A0, A1), const volatile U *arg) { + generate(function_context<R (*)(const volatile T*, A0, A1), const volatile T>(func, arg)); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1))) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1) const)) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1) volatile)) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1) const volatile)) { + generate(f); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(U *obj, R (*func)(T*, A0, A1)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(const U *obj, R (*func)(const T*, A0, A1)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(volatile U *obj, R (*func)(volatile T*, A0, A1)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(const volatile U *obj, R (*func)(const volatile T*, A0, A1)) { + new (this) Callback(func, obj); + } + + /** Destroy a callback + */ + ~Callback() { + if (_ops) { + _ops->dtor(this); + } + } + + /** Attach a static function + * @param func Static function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + void attach(R (*func)(A0, A1)) { + this->~Callback(); + new (this) Callback(func); + } + + /** Attach a Callback + * @param func The Callback to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + void attach(const Callback<R(A0, A1)> &func) { + this->~Callback(); + new (this) Callback(func); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(U *obj, R (T::*method)(A0, A1)) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(const U *obj, R (T::*method)(A0, A1) const) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(volatile U *obj, R (T::*method)(A0, A1) volatile) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(const volatile U *obj, R (T::*method)(A0, A1) const volatile) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(T*, A0, A1), U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(const T*, A0, A1), const U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(volatile T*, A0, A1), volatile U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(const volatile T*, A0, A1), const volatile U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1))) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1) const)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1) volatile)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1) const volatile)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(U *obj, R (*func)(T*, A0, A1)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(const U *obj, R (*func)(const T*, A0, A1)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(volatile U *obj, R (*func)(volatile T*, A0, A1)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(const volatile U *obj, R (*func)(const volatile T*, A0, A1)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Assign a callback + */ + Callback &operator=(const Callback &that) { + if (this != &that) { + this->~Callback(); + new (this) Callback(that); + } + + return *this; + } + + /** Call the attached function + */ + R call(A0 a0, A1 a1) const { + MBED_ASSERT(_ops); + return _ops->call(this, a0, a1); + } + + /** Call the attached function + */ + R operator()(A0 a0, A1 a1) const { + return call(a0, a1); + } + + /** Test if function has been attached + */ + operator bool() const { + return _ops; + } + + /** Test for equality + */ + friend bool operator==(const Callback &l, const Callback &r) { + return memcmp(&l, &r, sizeof(Callback)) == 0; + } + + /** Test for inequality + */ + friend bool operator!=(const Callback &l, const Callback &r) { + return !(l == r); + } + + /** Static thunk for passing as C-style function + * @param func Callback to call passed as void pointer + * @param a0 An argument to be called with function func + * @param a1 An argument to be called with function func + * @return the value as determined by func which is of + * type and determined by the signiture of func + */ + static R thunk(void *func, A0 a0, A1 a1) { + return static_cast<Callback*>(func)->call(a0, a1); + } + +private: + // Stored as pointer to function and pointer to optional object + // Function pointer is stored as union of possible function types + // to garuntee proper size and alignment + struct _class; + union { + void (*_staticfunc)(A0, A1); + void (*_boundfunc)(_class*, A0, A1); + void (_class::*_methodfunc)(A0, A1); + } _func; + void *_obj; + + // Dynamically dispatched operations + const struct ops { + R (*call)(const void*, A0, A1); + void (*move)(void*, const void*); + void (*dtor)(void*); + } *_ops; + + // Generate operations for function object + template <typename F> + void generate(const F &f) { + static const ops ops = { + &Callback::function_call<F>, + &Callback::function_move<F>, + &Callback::function_dtor<F>, + }; + new (this) F(f); + _ops = &ops; + } + + // Function attributes + template <typename F> + static R function_call(const void *p, A0 a0, A1 a1) { + return (*(F*)p)(a0, a1); + } + + template <typename F> + static void function_move(void *d, const void *p) { + new (d) F(*(F*)p); + } + + template <typename F> + static void function_dtor(void *p) { + ((F*)p)->~F(); + } + + // Wrappers for functions with context + template <typename O, typename M> + struct method_context { + M method; + O *obj; + + method_context(O *obj, M method) + : method(method), obj(obj) {} + + R operator()(A0 a0, A1 a1) const { + return (obj->*method)(a0, a1); + } + }; + + template <typename F, typename A> + struct function_context { + F func; + A *arg; + + function_context(F func, A *arg) + : func(func), arg(arg) {} + + R operator()(A0 a0, A1 a1) const { + return func(arg, a0, a1); + } + }; +}; + +/** Callback class based on template specialization + * + * @note Synchronization level: Not protected + * @ingroup platform + */ +template <typename R, typename A0, typename A1, typename A2> +class Callback<R(A0, A1, A2)> { +public: + /** Create a Callback with a static function + * @param func Static function to attach + */ + Callback(R (*func)(A0, A1, A2) = 0) { + if (!func) { + _ops = 0; + } else { + generate(func); + } + } + + /** Attach a Callback + * @param func The Callback to attach + */ + Callback(const Callback<R(A0, A1, A2)> &func) { + if (func._ops) { + func._ops->move(this, &func); + } + _ops = func._ops; + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(U *obj, R (T::*method)(A0, A1, A2)) { + generate(method_context<T, R (T::*)(A0, A1, A2)>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(const U *obj, R (T::*method)(A0, A1, A2) const) { + generate(method_context<const T, R (T::*)(A0, A1, A2) const>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(volatile U *obj, R (T::*method)(A0, A1, A2) volatile) { + generate(method_context<volatile T, R (T::*)(A0, A1, A2) volatile>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(const volatile U *obj, R (T::*method)(A0, A1, A2) const volatile) { + generate(method_context<const volatile T, R (T::*)(A0, A1, A2) const volatile>(obj, method)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(T*, A0, A1, A2), U *arg) { + generate(function_context<R (*)(T*, A0, A1, A2), T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(const T*, A0, A1, A2), const U *arg) { + generate(function_context<R (*)(const T*, A0, A1, A2), const T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(volatile T*, A0, A1, A2), volatile U *arg) { + generate(function_context<R (*)(volatile T*, A0, A1, A2), volatile T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(const volatile T*, A0, A1, A2), const volatile U *arg) { + generate(function_context<R (*)(const volatile T*, A0, A1, A2), const volatile T>(func, arg)); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2))) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2) const)) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2) volatile)) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2) const volatile)) { + generate(f); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(U *obj, R (*func)(T*, A0, A1, A2)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(const U *obj, R (*func)(const T*, A0, A1, A2)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(volatile U *obj, R (*func)(volatile T*, A0, A1, A2)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(const volatile U *obj, R (*func)(const volatile T*, A0, A1, A2)) { + new (this) Callback(func, obj); + } + + /** Destroy a callback + */ + ~Callback() { + if (_ops) { + _ops->dtor(this); + } + } + + /** Attach a static function + * @param func Static function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + void attach(R (*func)(A0, A1, A2)) { + this->~Callback(); + new (this) Callback(func); + } + + /** Attach a Callback + * @param func The Callback to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + void attach(const Callback<R(A0, A1, A2)> &func) { + this->~Callback(); + new (this) Callback(func); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(U *obj, R (T::*method)(A0, A1, A2)) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(const U *obj, R (T::*method)(A0, A1, A2) const) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(volatile U *obj, R (T::*method)(A0, A1, A2) volatile) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(const volatile U *obj, R (T::*method)(A0, A1, A2) const volatile) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(T*, A0, A1, A2), U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(const T*, A0, A1, A2), const U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(volatile T*, A0, A1, A2), volatile U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(const volatile T*, A0, A1, A2), const volatile U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2))) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2) const)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2) volatile)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2) const volatile)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(U *obj, R (*func)(T*, A0, A1, A2)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(const U *obj, R (*func)(const T*, A0, A1, A2)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(volatile U *obj, R (*func)(volatile T*, A0, A1, A2)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(const volatile U *obj, R (*func)(const volatile T*, A0, A1, A2)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Assign a callback + */ + Callback &operator=(const Callback &that) { + if (this != &that) { + this->~Callback(); + new (this) Callback(that); + } + + return *this; + } + + /** Call the attached function + */ + R call(A0 a0, A1 a1, A2 a2) const { + MBED_ASSERT(_ops); + return _ops->call(this, a0, a1, a2); + } + + /** Call the attached function + */ + R operator()(A0 a0, A1 a1, A2 a2) const { + return call(a0, a1, a2); + } + + /** Test if function has been attached + */ + operator bool() const { + return _ops; + } + + /** Test for equality + */ + friend bool operator==(const Callback &l, const Callback &r) { + return memcmp(&l, &r, sizeof(Callback)) == 0; + } + + /** Test for inequality + */ + friend bool operator!=(const Callback &l, const Callback &r) { + return !(l == r); + } + + /** Static thunk for passing as C-style function + * @param func Callback to call passed as void pointer + * @param a0 An argument to be called with function func + * @param a1 An argument to be called with function func + * @param a2 An argument to be called with function func + * @return the value as determined by func which is of + * type and determined by the signiture of func + */ + static R thunk(void *func, A0 a0, A1 a1, A2 a2) { + return static_cast<Callback*>(func)->call(a0, a1, a2); + } + +private: + // Stored as pointer to function and pointer to optional object + // Function pointer is stored as union of possible function types + // to garuntee proper size and alignment + struct _class; + union { + void (*_staticfunc)(A0, A1, A2); + void (*_boundfunc)(_class*, A0, A1, A2); + void (_class::*_methodfunc)(A0, A1, A2); + } _func; + void *_obj; + + // Dynamically dispatched operations + const struct ops { + R (*call)(const void*, A0, A1, A2); + void (*move)(void*, const void*); + void (*dtor)(void*); + } *_ops; + + // Generate operations for function object + template <typename F> + void generate(const F &f) { + static const ops ops = { + &Callback::function_call<F>, + &Callback::function_move<F>, + &Callback::function_dtor<F>, + }; + + new (this) F(f); + _ops = &ops; + } + + // Function attributes + template <typename F> + static R function_call(const void *p, A0 a0, A1 a1, A2 a2) { + return (*(F*)p)(a0, a1, a2); + } + + template <typename F> + static void function_move(void *d, const void *p) { + new (d) F(*(F*)p); + } + + template <typename F> + static void function_dtor(void *p) { + ((F*)p)->~F(); + } + + // Wrappers for functions with context + template <typename O, typename M> + struct method_context { + M method; + O *obj; + + method_context(O *obj, M method) + : method(method), obj(obj) {} + + R operator()(A0 a0, A1 a1, A2 a2) const { + return (obj->*method)(a0, a1, a2); + } + }; + + template <typename F, typename A> + struct function_context { + F func; + A *arg; + + function_context(F func, A *arg) + : func(func), arg(arg) {} + + R operator()(A0 a0, A1 a1, A2 a2) const { + return func(arg, a0, a1, a2); + } + }; +}; + +/** Callback class based on template specialization + * + * @note Synchronization level: Not protected + * @ingroup platform + */ +template <typename R, typename A0, typename A1, typename A2, typename A3> +class Callback<R(A0, A1, A2, A3)> { +public: + /** Create a Callback with a static function + * @param func Static function to attach + */ + Callback(R (*func)(A0, A1, A2, A3) = 0) { + if (!func) { + _ops = 0; + } else { + generate(func); + } + } + + /** Attach a Callback + * @param func The Callback to attach + */ + Callback(const Callback<R(A0, A1, A2, A3)> &func) { + if (func._ops) { + func._ops->move(this, &func); + } + _ops = func._ops; + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(U *obj, R (T::*method)(A0, A1, A2, A3)) { + generate(method_context<T, R (T::*)(A0, A1, A2, A3)>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(const U *obj, R (T::*method)(A0, A1, A2, A3) const) { + generate(method_context<const T, R (T::*)(A0, A1, A2, A3) const>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(volatile U *obj, R (T::*method)(A0, A1, A2, A3) volatile) { + generate(method_context<volatile T, R (T::*)(A0, A1, A2, A3) volatile>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(const volatile U *obj, R (T::*method)(A0, A1, A2, A3) const volatile) { + generate(method_context<const volatile T, R (T::*)(A0, A1, A2, A3) const volatile>(obj, method)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(T*, A0, A1, A2, A3), U *arg) { + generate(function_context<R (*)(T*, A0, A1, A2, A3), T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(const T*, A0, A1, A2, A3), const U *arg) { + generate(function_context<R (*)(const T*, A0, A1, A2, A3), const T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(volatile T*, A0, A1, A2, A3), volatile U *arg) { + generate(function_context<R (*)(volatile T*, A0, A1, A2, A3), volatile T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(const volatile T*, A0, A1, A2, A3), const volatile U *arg) { + generate(function_context<R (*)(const volatile T*, A0, A1, A2, A3), const volatile T>(func, arg)); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3))) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3) const)) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3) volatile)) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3) const volatile)) { + generate(f); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(U *obj, R (*func)(T*, A0, A1, A2, A3)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(const U *obj, R (*func)(const T*, A0, A1, A2, A3)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(volatile U *obj, R (*func)(volatile T*, A0, A1, A2, A3)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(const volatile U *obj, R (*func)(const volatile T*, A0, A1, A2, A3)) { + new (this) Callback(func, obj); + } + + /** Destroy a callback + */ + ~Callback() { + if (_ops) { + _ops->dtor(this); + } + } + + /** Attach a static function + * @param func Static function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + void attach(R (*func)(A0, A1, A2, A3)) { + this->~Callback(); + new (this) Callback(func); + } + + /** Attach a Callback + * @param func The Callback to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + void attach(const Callback<R(A0, A1, A2, A3)> &func) { + this->~Callback(); + new (this) Callback(func); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(U *obj, R (T::*method)(A0, A1, A2, A3)) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(const U *obj, R (T::*method)(A0, A1, A2, A3) const) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(volatile U *obj, R (T::*method)(A0, A1, A2, A3) volatile) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(const volatile U *obj, R (T::*method)(A0, A1, A2, A3) const volatile) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(T*, A0, A1, A2, A3), U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(const T*, A0, A1, A2, A3), const U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(volatile T*, A0, A1, A2, A3), volatile U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(const volatile T*, A0, A1, A2, A3), const volatile U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3))) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3) const)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3) volatile)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3) const volatile)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(U *obj, R (*func)(T*, A0, A1, A2, A3)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(const U *obj, R (*func)(const T*, A0, A1, A2, A3)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(volatile U *obj, R (*func)(volatile T*, A0, A1, A2, A3)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(const volatile U *obj, R (*func)(const volatile T*, A0, A1, A2, A3)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Assign a callback + */ + Callback &operator=(const Callback &that) { + if (this != &that) { + this->~Callback(); + new (this) Callback(that); + } + + return *this; + } + + /** Call the attached function + */ + R call(A0 a0, A1 a1, A2 a2, A3 a3) const { + MBED_ASSERT(_ops); + return _ops->call(this, a0, a1, a2, a3); + } + + /** Call the attached function + */ + R operator()(A0 a0, A1 a1, A2 a2, A3 a3) const { + return call(a0, a1, a2, a3); + } + + /** Test if function has been attached + */ + operator bool() const { + return _ops; + } + + /** Test for equality + */ + friend bool operator==(const Callback &l, const Callback &r) { + return memcmp(&l, &r, sizeof(Callback)) == 0; + } + + /** Test for inequality + */ + friend bool operator!=(const Callback &l, const Callback &r) { + return !(l == r); + } + + /** Static thunk for passing as C-style function + * @param func Callback to call passed as void pointer + * @param a0 An argument to be called with function func + * @param a1 An argument to be called with function func + * @param a2 An argument to be called with function func + * @param a3 An argument to be called with function func + * @return the value as determined by func which is of + * type and determined by the signiture of func + */ + static R thunk(void *func, A0 a0, A1 a1, A2 a2, A3 a3) { + return static_cast<Callback*>(func)->call(a0, a1, a2, a3); + } + +private: + // Stored as pointer to function and pointer to optional object + // Function pointer is stored as union of possible function types + // to garuntee proper size and alignment + struct _class; + union { + void (*_staticfunc)(A0, A1, A2, A3); + void (*_boundfunc)(_class*, A0, A1, A2, A3); + void (_class::*_methodfunc)(A0, A1, A2, A3); + } _func; + void *_obj; + + // Dynamically dispatched operations + const struct ops { + R (*call)(const void*, A0, A1, A2, A3); + void (*move)(void*, const void*); + void (*dtor)(void*); + } *_ops; + + // Generate operations for function object + template <typename F> + void generate(const F &f) { + static const ops ops = { + &Callback::function_call<F>, + &Callback::function_move<F>, + &Callback::function_dtor<F>, + }; + + new (this) F(f); + _ops = &ops; + } + + // Function attributes + template <typename F> + static R function_call(const void *p, A0 a0, A1 a1, A2 a2, A3 a3) { + return (*(F*)p)(a0, a1, a2, a3); + } + + template <typename F> + static void function_move(void *d, const void *p) { + new (d) F(*(F*)p); + } + + template <typename F> + static void function_dtor(void *p) { + ((F*)p)->~F(); + } + + // Wrappers for functions with context + template <typename O, typename M> + struct method_context { + M method; + O *obj; + + method_context(O *obj, M method) + : method(method), obj(obj) {} + + R operator()(A0 a0, A1 a1, A2 a2, A3 a3) const { + return (obj->*method)(a0, a1, a2, a3); + } + }; + + template <typename F, typename A> + struct function_context { + F func; + A *arg; + + function_context(F func, A *arg) + : func(func), arg(arg) {} + + R operator()(A0 a0, A1 a1, A2 a2, A3 a3) const { + return func(arg, a0, a1, a2, a3); + } + }; +}; + +/** Callback class based on template specialization + * + * @note Synchronization level: Not protected + * @ingroup platform + */ +template <typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +class Callback<R(A0, A1, A2, A3, A4)> { +public: + /** Create a Callback with a static function + * @param func Static function to attach + */ + Callback(R (*func)(A0, A1, A2, A3, A4) = 0) { + if (!func) { + _ops = 0; + } else { + generate(func); + } + } + + /** Attach a Callback + * @param func The Callback to attach + */ + Callback(const Callback<R(A0, A1, A2, A3, A4)> &func) { + if (func._ops) { + func._ops->move(this, &func); + } + _ops = func._ops; + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(U *obj, R (T::*method)(A0, A1, A2, A3, A4)) { + generate(method_context<T, R (T::*)(A0, A1, A2, A3, A4)>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(const U *obj, R (T::*method)(A0, A1, A2, A3, A4) const) { + generate(method_context<const T, R (T::*)(A0, A1, A2, A3, A4) const>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(volatile U *obj, R (T::*method)(A0, A1, A2, A3, A4) volatile) { + generate(method_context<volatile T, R (T::*)(A0, A1, A2, A3, A4) volatile>(obj, method)); + } + + /** Create a Callback with a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + */ + template<typename T, typename U> + Callback(const volatile U *obj, R (T::*method)(A0, A1, A2, A3, A4) const volatile) { + generate(method_context<const volatile T, R (T::*)(A0, A1, A2, A3, A4) const volatile>(obj, method)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(T*, A0, A1, A2, A3, A4), U *arg) { + generate(function_context<R (*)(T*, A0, A1, A2, A3, A4), T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(const T*, A0, A1, A2, A3, A4), const U *arg) { + generate(function_context<R (*)(const T*, A0, A1, A2, A3, A4), const T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(volatile T*, A0, A1, A2, A3, A4), volatile U *arg) { + generate(function_context<R (*)(volatile T*, A0, A1, A2, A3, A4), volatile T>(func, arg)); + } + + /** Create a Callback with a static function and bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + */ + template<typename T, typename U> + Callback(R (*func)(const volatile T*, A0, A1, A2, A3, A4), const volatile U *arg) { + generate(function_context<R (*)(const volatile T*, A0, A1, A2, A3, A4), const volatile T>(func, arg)); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3, A4))) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3, A4) const)) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3, A4) volatile)) { + generate(f); + } + + /** Create a Callback with a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + */ + template <typename F> + Callback(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3, A4) const volatile)) { + generate(f); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(U *obj, R (*func)(T*, A0, A1, A2, A3, A4)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(const U *obj, R (*func)(const T*, A0, A1, A2, A3, A4)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(volatile U *obj, R (*func)(volatile T*, A0, A1, A2, A3, A4)) { + new (this) Callback(func, obj); + } + + /** Create a Callback with a static function and bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to Callback(func, arg) + */ + template<typename T, typename U> + Callback(const volatile U *obj, R (*func)(const volatile T*, A0, A1, A2, A3, A4)) { + new (this) Callback(func, obj); + } + + /** Destroy a callback + */ + ~Callback() { + if (_ops) { + _ops->dtor(this); + } + } + + /** Attach a static function + * @param func Static function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + void attach(R (*func)(A0, A1, A2, A3, A4)) { + this->~Callback(); + new (this) Callback(func); + } + + /** Attach a Callback + * @param func The Callback to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + void attach(const Callback<R(A0, A1, A2, A3, A4)> &func) { + this->~Callback(); + new (this) Callback(func); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(U *obj, R (T::*method)(A0, A1, A2, A3, A4)) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(const U *obj, R (T::*method)(A0, A1, A2, A3, A4) const) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(volatile U *obj, R (T::*method)(A0, A1, A2, A3, A4) volatile) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a member function + * @param obj Pointer to object to invoke member function on + * @param method Member function to attach + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template<typename T, typename U> + void attach(const volatile U *obj, R (T::*method)(A0, A1, A2, A3, A4) const volatile) { + this->~Callback(); + new (this) Callback(obj, method); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(T*, A0, A1, A2, A3, A4), U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(const T*, A0, A1, A2, A3, A4), const U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(volatile T*, A0, A1, A2, A3, A4), volatile U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a static function with a bound pointer + * @param func Static function to attach + * @param arg Pointer argument to function + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename T, typename U> + void attach(R (*func)(const volatile T*, A0, A1, A2, A3, A4), const volatile U *arg) { + this->~Callback(); + new (this) Callback(func, arg); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3, A4))) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3, A4) const)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3, A4) volatile)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a function object + * @param f Function object to attach + * @note The function object is limited to a single word of storage + * @deprecated + * Replaced by simple assignment 'Callback cb = func' + */ + template <typename F> + void attach(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R (F::*)(A0, A1, A2, A3, A4) const volatile)) { + this->~Callback(); + new (this) Callback(f); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(U *obj, R (*func)(T*, A0, A1, A2, A3, A4)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(const U *obj, R (*func)(const T*, A0, A1, A2, A3, A4)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(volatile U *obj, R (*func)(volatile T*, A0, A1, A2, A3, A4)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Attach a static function with a bound pointer + * @param obj Pointer to object to bind to function + * @param func Static function to attach + * @deprecated + * Arguments to callback have been reordered to attach(func, arg) + */ + template <typename T, typename U> + void attach(const volatile U *obj, R (*func)(const volatile T*, A0, A1, A2, A3, A4)) { + this->~Callback(); + new (this) Callback(func, obj); + } + + /** Assign a callback + */ + Callback &operator=(const Callback &that) { + if (this != &that) { + this->~Callback(); + new (this) Callback(that); + } + + return *this; + } + + /** Call the attached function + */ + R call(A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) const { + MBED_ASSERT(_ops); + return _ops->call(this, a0, a1, a2, a3, a4); + } + + /** Call the attached function + */ + R operator()(A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) const { + return call(a0, a1, a2, a3, a4); + } + + /** Test if function has been attached + */ + operator bool() const { + return _ops; + } + + /** Test for equality + */ + friend bool operator==(const Callback &l, const Callback &r) { + return memcmp(&l, &r, sizeof(Callback)) == 0; + } + + /** Test for inequality + */ + friend bool operator!=(const Callback &l, const Callback &r) { + return !(l == r); + } + + /** Static thunk for passing as C-style function + * @param func Callback to call passed as void pointer + * @param a0 An argument to be called with function func + * @param a1 An argument to be called with function func + * @param a2 An argument to be called with function func + * @param a3 An argument to be called with function func + * @param a4 An argument to be called with function func + * @return the value as determined by func which is of + * type and determined by the signiture of func + */ + static R thunk(void *func, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return static_cast<Callback*>(func)->call(a0, a1, a2, a3, a4); + } + +private: + // Stored as pointer to function and pointer to optional object + // Function pointer is stored as union of possible function types + // to garuntee proper size and alignment + struct _class; + union { + void (*_staticfunc)(A0, A1, A2, A3, A4); + void (*_boundfunc)(_class*, A0, A1, A2, A3, A4); + void (_class::*_methodfunc)(A0, A1, A2, A3, A4); + } _func; + void *_obj; + + // Dynamically dispatched operations + const struct ops { + R (*call)(const void*, A0, A1, A2, A3, A4); + void (*move)(void*, const void*); + void (*dtor)(void*); + } *_ops; + + // Generate operations for function object + template <typename F> + void generate(const F &f) { + static const ops ops = { + &Callback::function_call<F>, + &Callback::function_move<F>, + &Callback::function_dtor<F>, + }; + + new (this) F(f); + _ops = &ops; + } + + // Function attributes + template <typename F> + static R function_call(const void *p, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + return (*(F*)p)(a0, a1, a2, a3, a4); + } + + template <typename F> + static void function_move(void *d, const void *p) { + new (d) F(*(F*)p); + } + + template <typename F> + static void function_dtor(void *p) { + ((F*)p)->~F(); + } + + // Wrappers for functions with context + template <typename O, typename M> + struct method_context { + M method; + O *obj; + + method_context(O *obj, M method) + : method(method), obj(obj) {} + + R operator()(A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) const { + return (obj->*method)(a0, a1, a2, a3, a4); + } + }; + + template <typename F, typename A> + struct function_context { + F func; + A *arg; + + function_context(F func, A *arg) + : func(func), arg(arg) {} + + R operator()(A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) const { + return func(arg, a0, a1, a2, a3, a4); + } + }; +}; + +// Internally used event type +typedef Callback<void(int)> event_callback_t; + + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @return Callback with infered type + */ +template <typename R> +Callback<R()> callback(R (*func)() = 0) { + return Callback<R()>(func); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @return Callback with infered type + */ +template <typename R> +Callback<R()> callback(const Callback<R()> &func) { + return Callback<R()>(func); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R> +Callback<R()> callback(U *obj, R (T::*method)()) { + return Callback<R()>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R> +Callback<R()> callback(const U *obj, R (T::*method)() const) { + return Callback<R()>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R> +Callback<R()> callback(volatile U *obj, R (T::*method)() volatile) { + return Callback<R()>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R> +Callback<R()> callback(const volatile U *obj, R (T::*method)() const volatile) { + return Callback<R()>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R> +Callback<R()> callback(R (*func)(T*), U *arg) { + return Callback<R()>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R> +Callback<R()> callback(R (*func)(const T*), const U *arg) { + return Callback<R()>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R> +Callback<R()> callback(R (*func)(volatile T*), volatile U *arg) { + return Callback<R()>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R> +Callback<R()> callback(R (*func)(const volatile T*), const volatile U *arg) { + return Callback<R()>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R> +Callback<R()> callback(U *obj, R (*func)(T*)) { + return Callback<R()>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R> +Callback<R()> callback(const U *obj, R (*func)(const T*)) { + return Callback<R()>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R> +Callback<R()> callback(volatile U *obj, R (*func)(volatile T*)) { + return Callback<R()>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R> +Callback<R()> callback(const volatile U *obj, R (*func)(const volatile T*)) { + return Callback<R()>(func, obj); +} + + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @return Callback with infered type + */ +template <typename R, typename A0> +Callback<R(A0)> callback(R (*func)(A0) = 0) { + return Callback<R(A0)>(func); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @return Callback with infered type + */ +template <typename R, typename A0> +Callback<R(A0)> callback(const Callback<R(A0)> &func) { + return Callback<R(A0)>(func); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0> +Callback<R(A0)> callback(U *obj, R (T::*method)(A0)) { + return Callback<R(A0)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0> +Callback<R(A0)> callback(const U *obj, R (T::*method)(A0) const) { + return Callback<R(A0)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0> +Callback<R(A0)> callback(volatile U *obj, R (T::*method)(A0) volatile) { + return Callback<R(A0)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0> +Callback<R(A0)> callback(const volatile U *obj, R (T::*method)(A0) const volatile) { + return Callback<R(A0)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0> +Callback<R(A0)> callback(R (*func)(T*, A0), U *arg) { + return Callback<R(A0)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0> +Callback<R(A0)> callback(R (*func)(const T*, A0), const U *arg) { + return Callback<R(A0)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0> +Callback<R(A0)> callback(R (*func)(volatile T*, A0), volatile U *arg) { + return Callback<R(A0)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0> +Callback<R(A0)> callback(R (*func)(const volatile T*, A0), const volatile U *arg) { + return Callback<R(A0)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0> +Callback<R(A0)> callback(U *obj, R (*func)(T*, A0)) { + return Callback<R(A0)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0> +Callback<R(A0)> callback(const U *obj, R (*func)(const T*, A0)) { + return Callback<R(A0)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0> +Callback<R(A0)> callback(volatile U *obj, R (*func)(volatile T*, A0)) { + return Callback<R(A0)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0> +Callback<R(A0)> callback(const volatile U *obj, R (*func)(const volatile T*, A0)) { + return Callback<R(A0)>(func, obj); +} + + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @return Callback with infered type + */ +template <typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(R (*func)(A0, A1) = 0) { + return Callback<R(A0, A1)>(func); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @return Callback with infered type + */ +template <typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(const Callback<R(A0, A1)> &func) { + return Callback<R(A0, A1)>(func); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(U *obj, R (T::*method)(A0, A1)) { + return Callback<R(A0, A1)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(const U *obj, R (T::*method)(A0, A1) const) { + return Callback<R(A0, A1)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(volatile U *obj, R (T::*method)(A0, A1) volatile) { + return Callback<R(A0, A1)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(const volatile U *obj, R (T::*method)(A0, A1) const volatile) { + return Callback<R(A0, A1)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(R (*func)(T*, A0, A1), U *arg) { + return Callback<R(A0, A1)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(R (*func)(const T*, A0, A1), const U *arg) { + return Callback<R(A0, A1)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(R (*func)(volatile T*, A0, A1), volatile U *arg) { + return Callback<R(A0, A1)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(R (*func)(const volatile T*, A0, A1), const volatile U *arg) { + return Callback<R(A0, A1)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(U *obj, R (*func)(T*, A0, A1)) { + return Callback<R(A0, A1)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(const U *obj, R (*func)(const T*, A0, A1)) { + return Callback<R(A0, A1)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(volatile U *obj, R (*func)(volatile T*, A0, A1)) { + return Callback<R(A0, A1)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1> +Callback<R(A0, A1)> callback(const volatile U *obj, R (*func)(const volatile T*, A0, A1)) { + return Callback<R(A0, A1)>(func, obj); +} + + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @return Callback with infered type + */ +template <typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(R (*func)(A0, A1, A2) = 0) { + return Callback<R(A0, A1, A2)>(func); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @return Callback with infered type + */ +template <typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(const Callback<R(A0, A1, A2)> &func) { + return Callback<R(A0, A1, A2)>(func); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(U *obj, R (T::*method)(A0, A1, A2)) { + return Callback<R(A0, A1, A2)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(const U *obj, R (T::*method)(A0, A1, A2) const) { + return Callback<R(A0, A1, A2)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(volatile U *obj, R (T::*method)(A0, A1, A2) volatile) { + return Callback<R(A0, A1, A2)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(const volatile U *obj, R (T::*method)(A0, A1, A2) const volatile) { + return Callback<R(A0, A1, A2)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(R (*func)(T*, A0, A1, A2), U *arg) { + return Callback<R(A0, A1, A2)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(R (*func)(const T*, A0, A1, A2), const U *arg) { + return Callback<R(A0, A1, A2)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(R (*func)(volatile T*, A0, A1, A2), volatile U *arg) { + return Callback<R(A0, A1, A2)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(R (*func)(const volatile T*, A0, A1, A2), const volatile U *arg) { + return Callback<R(A0, A1, A2)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(U *obj, R (*func)(T*, A0, A1, A2)) { + return Callback<R(A0, A1, A2)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(const U *obj, R (*func)(const T*, A0, A1, A2)) { + return Callback<R(A0, A1, A2)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(volatile U *obj, R (*func)(volatile T*, A0, A1, A2)) { + return Callback<R(A0, A1, A2)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2> +Callback<R(A0, A1, A2)> callback(const volatile U *obj, R (*func)(const volatile T*, A0, A1, A2)) { + return Callback<R(A0, A1, A2)>(func, obj); +} + + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @return Callback with infered type + */ +template <typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(R (*func)(A0, A1, A2, A3) = 0) { + return Callback<R(A0, A1, A2, A3)>(func); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @return Callback with infered type + */ +template <typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(const Callback<R(A0, A1, A2, A3)> &func) { + return Callback<R(A0, A1, A2, A3)>(func); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(U *obj, R (T::*method)(A0, A1, A2, A3)) { + return Callback<R(A0, A1, A2, A3)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(const U *obj, R (T::*method)(A0, A1, A2, A3) const) { + return Callback<R(A0, A1, A2, A3)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(volatile U *obj, R (T::*method)(A0, A1, A2, A3) volatile) { + return Callback<R(A0, A1, A2, A3)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(const volatile U *obj, R (T::*method)(A0, A1, A2, A3) const volatile) { + return Callback<R(A0, A1, A2, A3)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(R (*func)(T*, A0, A1, A2, A3), U *arg) { + return Callback<R(A0, A1, A2, A3)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(R (*func)(const T*, A0, A1, A2, A3), const U *arg) { + return Callback<R(A0, A1, A2, A3)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(R (*func)(volatile T*, A0, A1, A2, A3), volatile U *arg) { + return Callback<R(A0, A1, A2, A3)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(R (*func)(const volatile T*, A0, A1, A2, A3), const volatile U *arg) { + return Callback<R(A0, A1, A2, A3)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(U *obj, R (*func)(T*, A0, A1, A2, A3)) { + return Callback<R(A0, A1, A2, A3)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(const U *obj, R (*func)(const T*, A0, A1, A2, A3)) { + return Callback<R(A0, A1, A2, A3)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(volatile U *obj, R (*func)(volatile T*, A0, A1, A2, A3)) { + return Callback<R(A0, A1, A2, A3)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3> +Callback<R(A0, A1, A2, A3)> callback(const volatile U *obj, R (*func)(const volatile T*, A0, A1, A2, A3)) { + return Callback<R(A0, A1, A2, A3)>(func, obj); +} + + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @return Callback with infered type + */ +template <typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(R (*func)(A0, A1, A2, A3, A4) = 0) { + return Callback<R(A0, A1, A2, A3, A4)>(func); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @return Callback with infered type + */ +template <typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(const Callback<R(A0, A1, A2, A3, A4)> &func) { + return Callback<R(A0, A1, A2, A3, A4)>(func); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(U *obj, R (T::*method)(A0, A1, A2, A3, A4)) { + return Callback<R(A0, A1, A2, A3, A4)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(const U *obj, R (T::*method)(A0, A1, A2, A3, A4) const) { + return Callback<R(A0, A1, A2, A3, A4)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(volatile U *obj, R (T::*method)(A0, A1, A2, A3, A4) volatile) { + return Callback<R(A0, A1, A2, A3, A4)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param method Member function to attach + * @return Callback with infered type + */ +template<typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(const volatile U *obj, R (T::*method)(A0, A1, A2, A3, A4) const volatile) { + return Callback<R(A0, A1, A2, A3, A4)>(obj, method); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(R (*func)(T*, A0, A1, A2, A3, A4), U *arg) { + return Callback<R(A0, A1, A2, A3, A4)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(R (*func)(const T*, A0, A1, A2, A3, A4), const U *arg) { + return Callback<R(A0, A1, A2, A3, A4)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(R (*func)(volatile T*, A0, A1, A2, A3, A4), volatile U *arg) { + return Callback<R(A0, A1, A2, A3, A4)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param func Static function to attach + * @param arg Pointer argument to function + * @return Callback with infered type + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(R (*func)(const volatile T*, A0, A1, A2, A3, A4), const volatile U *arg) { + return Callback<R(A0, A1, A2, A3, A4)>(func, arg); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(U *obj, R (*func)(T*, A0, A1, A2, A3, A4)) { + return Callback<R(A0, A1, A2, A3, A4)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(const U *obj, R (*func)(const T*, A0, A1, A2, A3, A4)) { + return Callback<R(A0, A1, A2, A3, A4)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(volatile U *obj, R (*func)(volatile T*, A0, A1, A2, A3, A4)) { + return Callback<R(A0, A1, A2, A3, A4)>(func, obj); +} + +/** Create a callback class with type infered from the arguments + * + * @param obj Optional pointer to object to bind to function + * @param func Static function to attach + * @return Callback with infered type + * @deprecated + * Arguments to callback have been reordered to callback(func, arg) + */ +template <typename T, typename U, typename R, typename A0, typename A1, typename A2, typename A3, typename A4> +Callback<R(A0, A1, A2, A3, A4)> callback(const volatile U *obj, R (*func)(const volatile T*, A0, A1, A2, A3, A4)) { + return Callback<R(A0, A1, A2, A3, A4)>(func, obj); +} + + +// } // namespace mbed + +#endif +#endif // Arduino
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/Arduino-mbed-APIs/arduino-d21.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,424 @@ +/* + * The file is Licensed under the Apache License, Version 2.0 + * (c) 2017 Helmut Tschemernjak + * 30826 Garbsen (Hannover) Germany + */ + +#ifdef ARDUINO + +using namespace std; + +#include "arduino-mbed.h" +#include "arduino-util.h" + + + +#if defined(__SAMD21G18A__) || defined(__SAMD21J18A__) +/* + * __SAMD21J18A__ is the SamD21 Explained Board + * __SAMD21G18A__ is Genuino Zero-Board (compatible with the LoRa board) + */ + +int +CPUID(uint8_t *buf, int maxSize, uint32_t xorval) +{ + int f1 = 0x55d5f559; // D21 128-bit UUID, first 32 bit. + int f2 = 0x55d5f515; // D21 128-bit UUID, next 96 bit. + + if (maxSize >= 16 ) { + int cnt = 0; + int fa = f1 ^ xorval; + uint32_t *first = (uint32_t *)fa; + uint8_t *dst = (uint8_t *)first; + for (int i = 0; i < (int)sizeof(uint32_t); i++) + *buf++ = *dst++; + cnt += 4; + int fb = f2 ^ xorval; + uint32_t *next = (uint32_t *)fb; + dst = (uint8_t *)next; + for (int i = 0; i < (int)sizeof(uint32_t)*3; i++) + *buf++ = *dst++; + cnt += 12; + return cnt; + } + + return 0; +} + +/* + * see tcc.h is automatically included from: + * Arduino15/packages/arduino/tools/CMSIS-Atmel/1.1.0/CMSIS/ + * Device/ATMEL/samd21/include/component/tcc.h + * See also tcc.c (ASF/mbed, e.g. Tcc_get_count_value) + */ +static void initTimer(Tcc *t); +static uint32_t getTimerCount(Tcc *t); + +/* + * The Atmel D21 has three TCC timer, other models have more. + */ +const struct TCC_config { + Tcc *tcc_ptr; + IRQn_Type tcc_irq; + uint8_t nbits; +} TCC_data[] { + { TCC0, TCC0_IRQn, 24 }, + { TCC1, TCC1_IRQn, 24 }, + { TCC2, TCC2_IRQn, 16 }, + { NULL, (IRQn_Type)NULL, 0 } +}; + +/* + * We preferably use the TCC timers because it supports 24-bit counters + * versus TC Timer which supports only 8 or 16 bit counters only. + * TCC0/1/2 timer work on the D21 using Arduino Zero. + */ +#define USE_TCC_TIMEOUT 0 // 0=TCC0, 1=TTC1, 2=TTC2 (see TCC_data) +#define USE_TCC_TICKER 1 + + +/* + * every 21333 ns equals one tick (1/(48000000/1024)) // prescaler 1024, 48 MHz + * every 61035 ns equals one tick (1/(32768/2)) // prescaler 2, 32 kHz + * COUNT*DIVIDER*SECS until interrupt + * CPU 48 MHz = (65536*1024)/1.398636s + * RTC 32 kHz = (65536*2)/4.0s + */ +#define NS_PER_CLOCK_CPU 21333 // ns secs per clock +#define NS_PER_CLOCK_RTC 61035 // ns secs per clock + +#define NS_PER_CLOCK NS_PER_CLOCK_RTC + +/* ----------------- TICKER TIMER CODE ----------------------*/ + +/* + * The global ns_counter contains the time in ns from the last time + * the counter has been wrapped. It cannot be used directly because the + * current counter has to be added fore using it. Use instead + * ns_getTicker(), us_ ns_getTicker(), ms_getTicker() + */ + +uint64_t ticker_ns; +static bool initTickerDone = false; + +uint64_t ns_getTicker(void) +{ + Tcc *t = TCC_data[USE_TCC_TICKER].tcc_ptr; + if (!initTickerDone) { + initTimer(t); + initTickerDone = true; + + // set counter top to max 16 bit for testing + // t->PER.bit.PER = 0xffff; + // while (t->SYNCBUSY.bit.PER == 1); // wait for sync + + t->CTRLA.reg |= TCC_CTRLA_ENABLE ; // Enable TC + while (t->SYNCBUSY.bit.ENABLE == 1); // wait for sync + } + + /* + * if we are called from the interrupt level, the counter contains + * somehow wrong data, therfore we needs to read it twice. + * Another option was to add a little wait (loop 500x) + * in the TCC_TIMEOUT interrupt handler. + */ + if (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) // check if we are in the interrupt + getTimerCount(t); + + uint64_t counter_us = (uint64_t)NS_PER_CLOCK * (uint64_t)getTimerCount(t); + uint64_t ns = ticker_ns + counter_us; + + return ns; +} + +#if USE_TCC_TICKER == 0 +void TCC0_Handler() +#elif USE_TCC_TICKER == 1 +void TCC1_Handler() +#elif USE_TCC_TICKER == 2 +void TCC2_Handler() +#endif +{ + Tcc *t = TCC_data[USE_TCC_TICKER].tcc_ptr; + /* + * Overflow means the timer top exeeded + */ + if (t->INTFLAG.bit.OVF == 1) { // A overflow caused the interrupt + t->INTFLAG.bit.OVF = 1; // writing a one clears the flag ovf flag + // ser->println("T_OVF"); + + /* + * reading the count once is needed, otherwise + * it will not wrap correct. + */ + getTimerCount(t); + + int bits = TCC_data[USE_TCC_TICKER].nbits; + int maxCounts = (uint32_t)(1<<bits); + + ticker_ns += (uint64_t)NS_PER_CLOCK * (uint64_t)maxCounts; + } + if (t->INTFLAG.bit.MC0 == 1) { // A compare to cc0 caused the interrupt + t->INTFLAG.bit.MC0 = 1; // writing a one clears the MCO (match capture) flag + // ser->println("T_MC0"); + } +} + +/* ----------------- SUPPORT CODE FOR TCC TIMERS----------------------*/ + +static bool initTimerDone = false; + +static void initTimer(Tcc *t) +{ + + /* + * enable clock for TCC, see gclk.h + * GCLK_CLKCTRL_GEN_GCLK0 for 48 Mhz CPU + * GCLK_CLKCTRL_GEN_GCLK1 for 32k extern crystal XOSC32K (ifdef CRYSTALLESS) + * GCLK_CLKCTRL_GEN_GCLK1 for 32k internal OSC32K + * see Arduino: arduino/hardware/samd/1.6.15/cores/arduino/startup.c + * Use TCC_CTRLA_PRESCALER_DIV1024 for for 48 Mhz clock + * Use TCC_CTRLA_PRESCALER_DIV2 for 32k clock + */ + if (t == TCC0 || t == TCC1) { + REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK1 | GCLK_CLKCTRL_ID_TCC0_TCC1); + } else if (t == TCC2) { + REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK1 | GCLK_CLKCTRL_ID_TCC2_TC3_Val); + } + while (GCLK->STATUS.bit.SYNCBUSY == 1); // wait for sync + + t->CTRLA.reg &= ~TCC_CTRLA_ENABLE; // Disable TCC + while (t->SYNCBUSY.bit.ENABLE == 1); // wait for sync + + t->CTRLA.reg |= (TCC_CTRLA_PRESCALER_DIV2 | TCC_CTRLA_RUNSTDBY); // Set perscaler + + t->WAVE.reg |= TCC_WAVE_WAVEGEN_NFRQ; // Set wave form configuration + while (t->SYNCBUSY.bit.WAVE == 1); // wait for sync + + t->PER.bit.PER = 0xffffff; // set counter top to max 24 bit + while (t->SYNCBUSY.bit.PER == 1); // wait for sync + + // the compare counter TC->CC[0].reg will be set in the startTimer + // after the timeout calculation is known. + + // Interrupts + t->INTENSET.reg = 0; // disable all interrupts + t->INTENSET.bit.OVF = 1; // enable overfollow + t->INTENSET.bit.MC0 = 1; // enable compare match to CC0 + + const struct TCC_config *cp = &TCC_data[0]; + while (cp->tcc_ptr) { + if (cp->tcc_ptr == t) { + NVIC_EnableIRQ(cp->tcc_irq); // Enable InterruptVector + break; + } + cp++; + } +} + + +#if 0 +// Atmel ASF Code +static uint32_t getTimerCount(Tcc *t) +{ + uint32_t last_cmd; + /* Wait last command done */ + do { + while (t->SYNCBUSY.bit.CTRLB); /* Wait for sync */ + + last_cmd = t->CTRLBSET.reg & TCC_CTRLBSET_CMD_Msk; + if (TCC_CTRLBSET_CMD_NONE == last_cmd) { + /* Issue read command and break */ + t->CTRLBSET.bit.CMD = TCC_CTRLBSET_CMD_READSYNC_Val; + break; + } else if (TCC_CTRLBSET_CMD_READSYNC == last_cmd) { + /* Command have been issued */ + break; + } + } while (1); + + while (t->SYNCBUSY.bit.COUNT); /* Wait for sync */ + + return t->COUNT.reg; +} +#endif + + +static uint32_t getTimerCount(Tcc *t) +{ + + noInterrupts(); + + while (t->SYNCBUSY.bit.CTRLB); /* Wait for sync */ + + t->CTRLBSET.bit.CMD = TCC_CTRLBSET_CMD_READSYNC_Val; /* Issue read command and break */ + + while (t->SYNCBUSY.bit.COUNT); /* Wait for sync */ + + uint32_t count = t->COUNT.reg; + + interrupts(); + + return count; +} + + +Tcc *getTimeout_tcc(void) +{ + return TCC_data[USE_TCC_TIMEOUT].tcc_ptr; +} + + +void stopTimer(Tcc *t) +{ + t->CTRLA.reg &= ~TCC_CTRLA_ENABLE; // Disable TC + while (t->SYNCBUSY.bit.ENABLE == 1); // wait for sync +} + + +/* ----------------- TIMEOUT TIMER CODE ----------------------*/ + +void startTimer(Tcc *t, uint64_t delay_ns) +{ + if (!initTimerDone) { + initTimer(t); // initial setup with stopped timer + initTimerDone = true; + } + + stopTimer(t); // avoid timer interrupts while calculating + + /* + * every 21333 ns equals one tick (1/(48000000/1024)) + * COUNT*DIVIDER*SECS until interrupt + * 48 Mhz = (65536*1024)/1.398636s + */ + uint64_t nclocks = (uint64_t)delay_ns; + nclocks /= (uint64_t)NS_PER_CLOCK; + int nCounts = nclocks; + + int bits = TCC_data[USE_TCC_TIMEOUT].nbits; + int maxCounts = (uint32_t)(1<<bits)-1; + + if (nCounts > maxCounts) // if count exceeds timer capacity + nCounts = maxCounts; // set the largest posible count. + if (nCounts <= 0) + nCounts = 1; + t->CC[0].bit.CC = nCounts; + while (t->SYNCBUSY.bit.CC0 == 1); // wait for sync + + t->CTRLA.reg |= TCC_CTRLA_ENABLE ; // Enable TC + while (t->SYNCBUSY.bit.ENABLE == 1); // wait for sync +#if 0 + ser->print(ms_getTicker(), DEC); + ser->print(" startTimer: nCounts="); + ser->println(nCounts, DEC); +#endif +} + + +#if USE_TCC_TIMEOUT == 0 +void TCC0_Handler() +#elif USE_TCC_TIMEOUT == 1 +void TCC1_Handler() +#elif USE_TCC_TIMEOUT == 2 +void TCC2_Handler() +#endif +{ + Tcc *t = TCC_data[USE_TCC_TIMEOUT].tcc_ptr; + uint64_t nsecs = ns_getTicker(); + + /* + * Overflow means the max timer exeeded, we need restart the timer + * Interrupts and + */ + if (t->INTFLAG.bit.OVF == 1) { // A overflow caused the interrupt + t->INTFLAG.bit.OVF = 1; // writing a one clears the flag ovf flag + } + + if (t->INTFLAG.bit.MC0 == 1) { // A compare to cc0 caused the interrupt + //ser->print("MC0\r\n"); + t->INTFLAG.bit.MC0 = 1; // writing a one clears the MCO (match capture) flag + } + + t->CTRLA.reg &= ~TCC_CTRLA_ENABLE; // Disable TC + while (t->SYNCBUSY.bit.ENABLE == 1); // wait for sync + + for (int i = 0; i < MAX_TIMEOUTS-1; i++) { + struct TimeoutVector *tvp = &TimeOuts[i]; + if (tvp->timer && nsecs >= tvp->timer->_timeout) { + Timeout *saveTimer = tvp->timer; + tvp->timer = NULL; + Timeout::_irq_handler(saveTimer); + } + } + /* + * we need to restart the timer for remaining interrupts + * Another reason is that we stopped this counter, in case there are + * remaining counts, we need to re-schedule the counter. + */ + Timeout::restart(); +} + + +/* ----------------- D21 sleep() and deepsleep() code ----------------------*/ + +void sleep(void) +{ + /* + * If we use the native USB port our Serial is SerialUSB + * and if the SerialUSB and connected we should + * not enter into sleep mode because this kills the Arduino USB emulation + */ + SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // disbale SysTick + uint32_t saved_ms = ms_getTicker(); + + if (SerialUSB_active) { + __DSB(); // ensures the completion of memory accesses + __WFI(); // wait for interrupt + } else { +#if 0 // (SAMD20 || SAMD21) + /* Errata: Make sure that the Flash does not power all the way down + * when in sleep mode. */ + NVMCTRL->CTRLB.bit.SLEEPPRM = NVMCTRL_CTRLB_SLEEPPRM_DISABLED_Val; +#endif + + SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; // clear deep sleep + PM->SLEEP.reg = 2; // SYSTEM_SLEEPMODE_IDLE_2 IDLE 2 sleep mode. + + __DSB(); // ensures the completion of memory accesses + __WFI(); // wait for interrupt + } + + int count = ms_getTicker() - saved_ms; + if (count > 0) { // update the Arduino Systicks + for (int i = 0; i < count; i++) { + SysTick_Handler(); + } + } + SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // enable SysTick +} + +/* + * TODO + * Check if we need to disable the USB GCLK->CLKCTRL.reg (see USBCore.cpp) + * Check what else we need to disable? + */ + +void deepsleep(void) +{ +#if 0 // (SAMD20 || SAMD21) + /* Errata: Make sure that the Flash does not power all the way down + * when in sleep mode. */ + NVMCTRL->CTRLB.bit.SLEEPPRM = NVMCTRL_CTRLB_SLEEPPRM_DISABLED_Val; +#endif + + SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // standby mode + //EIC->WAKEUP.bit.WAKEUPEN3 = 1; // enable wakeup on Pin 12/PA19/EXTINT[3] see variants.h + + __DSB(); // ensures the completion of memory accesses + __WFI(); // wait for interrupt +} + +#endif // D21 TCC Timer, sleep, etc- + +#endif // ARDUINO
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/Arduino-mbed-APIs/arduino-mbed.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,335 @@ +/* + * The file is Licensed under the Apache License, Version 2.0 + * (c) 2017 Helmut Tschemernjak + * 30826 Garbsen (Hannover) Germany + */ + +#ifdef ARDUINO + +using namespace std; + +#include "arduino-mbed.h" +#include "arduino-util.h" + +Stream *ser; +bool SerialUSB_active = false; + +void InitSerial(Stream *serial, int timeout_ms) { + ser = serial; + if (serial == (Stream *)&SerialUSB) { + uint32_t start = ms_getTicker(); + + SerialUSB_active = true; + while(!SerialUSB) { + if (ms_getTicker() > start + timeout_ms) { + SerialUSB_active = false; + break; + } + } + if (!SerialUSB_active) { + USB->DEVICE.CTRLA.bit.SWRST = 1; // disconnect the USB Port + while (USB->DEVICE.CTRLA.bit.SWRST == 1); + } + } +} + +static void pinInt00(void); +static void pinInt01(void); +static void pinInt02(void); +static void pinInt03(void); +static void pinInt04(void); +static void pinInt05(void); +static void pinInt06(void); +static void pinInt07(void); +static void pinInt08(void); +static void pinInt09(void); +static void pinInt10(void); +static void pinInt11(void); +static void pinInt12(void); +static void pinInt13(void); +static void pinInt14(void); +static void pinInt15(void); +static void pinInt16(void); +static void pinInt17(void); +static void pinInt18(void); +static void pinInt19(void); +static void pinInt20(void); +static void pinInt21(void); +static void pinInt22(void); +static void pinInt23(void); +static void pinInt24(void); +static void pinInt25(void); +static void pinInt26(void); +static void pinInt27(void); +static void pinInt28(void); +static void pinInt29(void); +static void pinInt30(void); +static void pinInt31(void); +static void pinInt32(void); +static void pinInt33(void); +static void pinInt34(void); +static void pinInt35(void); +static void pinInt36(void); +static void pinInt37(void); +static void pinInt38(void); +static void pinInt39(void); +static void pinInt40(void); +static void pinInt41(void); +static void pinInt42(void); +static void pinInt43(void); +static void pinInt44(void); +static void pinInt45(void); +static void pinInt46(void); +static void pinInt47(void); + + + +#define MAX_MCU_PINS 48 +class InterruptIn; +struct intPtrTable { + void (*func)(void); + InterruptIn *context; +} intPtrTable[MAX_MCU_PINS] = { + { pinInt00, NULL }, + { pinInt01, NULL }, + { pinInt02, NULL }, + { pinInt03, NULL }, + { pinInt04, NULL }, + { pinInt05, NULL }, + { pinInt06, NULL }, + { pinInt07, NULL }, + { pinInt08, NULL }, + { pinInt09, NULL }, + { pinInt10, NULL }, + { pinInt11, NULL }, + { pinInt12, NULL }, + { pinInt13, NULL }, + { pinInt14, NULL }, + { pinInt15, NULL }, + { pinInt16, NULL }, + { pinInt17, NULL }, + { pinInt18, NULL }, + { pinInt19, NULL }, + { pinInt20, NULL }, + { pinInt21, NULL }, + { pinInt22, NULL }, + { pinInt23, NULL }, + { pinInt24, NULL }, + { pinInt25, NULL }, + { pinInt26, NULL }, + { pinInt27, NULL }, + { pinInt28, NULL }, + { pinInt29, NULL }, + { pinInt30, NULL }, + { pinInt31, NULL }, + { pinInt32, NULL }, + { pinInt33, NULL }, + { pinInt34, NULL }, + { pinInt35, NULL }, + { pinInt36, NULL }, + { pinInt37, NULL }, + { pinInt38, NULL }, + { pinInt39, NULL }, + { pinInt40, NULL }, + { pinInt41, NULL }, + { pinInt42, NULL }, + { pinInt43, NULL }, + { pinInt44, NULL }, + { pinInt45, NULL }, + { pinInt46, NULL }, + { pinInt47, NULL } +}; // our max MCUs pins + + + +static void pinInt00(void) { InterruptIn::_irq_handler(intPtrTable[0].context); } +static void pinInt01(void) { InterruptIn::_irq_handler(intPtrTable[1].context); } +static void pinInt02(void) { InterruptIn::_irq_handler(intPtrTable[2].context); } +static void pinInt03(void) { InterruptIn::_irq_handler(intPtrTable[3].context); } +static void pinInt04(void) { InterruptIn::_irq_handler(intPtrTable[4].context); } +static void pinInt05(void) { InterruptIn::_irq_handler(intPtrTable[5].context); } +static void pinInt06(void) { InterruptIn::_irq_handler(intPtrTable[6].context); } +static void pinInt07(void) { InterruptIn::_irq_handler(intPtrTable[7].context); } +static void pinInt08(void) { InterruptIn::_irq_handler(intPtrTable[8].context); } +static void pinInt09(void) { InterruptIn::_irq_handler(intPtrTable[9].context); } +static void pinInt10(void) { InterruptIn::_irq_handler(intPtrTable[10].context); } +static void pinInt11(void) { InterruptIn::_irq_handler(intPtrTable[11].context); } +static void pinInt12(void) { InterruptIn::_irq_handler(intPtrTable[12].context); } +static void pinInt13(void) { InterruptIn::_irq_handler(intPtrTable[13].context); } +static void pinInt14(void) { InterruptIn::_irq_handler(intPtrTable[14].context); } +static void pinInt15(void) { InterruptIn::_irq_handler(intPtrTable[15].context); } +static void pinInt16(void) { InterruptIn::_irq_handler(intPtrTable[16].context); } +static void pinInt17(void) { InterruptIn::_irq_handler(intPtrTable[17].context); } +static void pinInt18(void) { InterruptIn::_irq_handler(intPtrTable[18].context); } +static void pinInt19(void) { InterruptIn::_irq_handler(intPtrTable[19].context); } +static void pinInt20(void) { InterruptIn::_irq_handler(intPtrTable[20].context); } +static void pinInt21(void) { InterruptIn::_irq_handler(intPtrTable[21].context); } +static void pinInt22(void) { InterruptIn::_irq_handler(intPtrTable[22].context); } +static void pinInt23(void) { InterruptIn::_irq_handler(intPtrTable[23].context); } +static void pinInt24(void) { InterruptIn::_irq_handler(intPtrTable[24].context); } +static void pinInt25(void) { InterruptIn::_irq_handler(intPtrTable[25].context); } +static void pinInt26(void) { InterruptIn::_irq_handler(intPtrTable[26].context); } +static void pinInt27(void) { InterruptIn::_irq_handler(intPtrTable[27].context); } +static void pinInt28(void) { InterruptIn::_irq_handler(intPtrTable[28].context); } +static void pinInt29(void) { InterruptIn::_irq_handler(intPtrTable[29].context); } +static void pinInt30(void) { InterruptIn::_irq_handler(intPtrTable[30].context); } +static void pinInt31(void) { InterruptIn::_irq_handler(intPtrTable[31].context); } +static void pinInt32(void) { InterruptIn::_irq_handler(intPtrTable[32].context); } +static void pinInt33(void) { InterruptIn::_irq_handler(intPtrTable[33].context); } +static void pinInt34(void) { InterruptIn::_irq_handler(intPtrTable[34].context); } +static void pinInt35(void) { InterruptIn::_irq_handler(intPtrTable[35].context); } +static void pinInt36(void) { InterruptIn::_irq_handler(intPtrTable[36].context); } +static void pinInt37(void) { InterruptIn::_irq_handler(intPtrTable[37].context); } +static void pinInt38(void) { InterruptIn::_irq_handler(intPtrTable[38].context); } +static void pinInt39(void) { InterruptIn::_irq_handler(intPtrTable[39].context); } +static void pinInt40(void) { InterruptIn::_irq_handler(intPtrTable[40].context); } +static void pinInt41(void) { InterruptIn::_irq_handler(intPtrTable[41].context); } +static void pinInt42(void) { InterruptIn::_irq_handler(intPtrTable[42].context); } +static void pinInt43(void) { InterruptIn::_irq_handler(intPtrTable[43].context); } +static void pinInt44(void) { InterruptIn::_irq_handler(intPtrTable[44].context); } +static void pinInt45(void) { InterruptIn::_irq_handler(intPtrTable[45].context); } +static void pinInt46(void) { InterruptIn::_irq_handler(intPtrTable[46].context); } +static void pinInt47(void) { InterruptIn::_irq_handler(intPtrTable[47].context); } + + +void wait_ms(uint32_t ms) +{ + uint32_t start = ms_getTicker(); + + while (true) { + uint32_t t = ms_getTicker(); + if (t < start) // warp. + start = 0; + if (t > (start + ms)) + break; + } +} + +struct TimeoutVector TimeOuts[MAX_TIMEOUTS]; + +void +InterruptIn::rise(Callback<void()> func) { + if (_gpioPin >= MAX_MCU_PINS-1) + return; + if (func) { + _func = func; + intPtrTable[_gpioPin].context = this; + attachInterrupt(MYdigitalPinToInterrupt(_gpioPin), intPtrTable[_gpioPin].func, RISING); + } else { + _func = InterruptIn::donothing; + intPtrTable[_gpioPin].context = NULL; + detachInterrupt(_gpioPin); + } +}; + +void +InterruptIn::fall(Callback<void()> func) { + if (func) { + _func = func; + intPtrTable[_gpioPin].context = this; + attachInterrupt(MYdigitalPinToInterrupt(_gpioPin), intPtrTable[_gpioPin].func, FALLING); + } else { + _func = InterruptIn::donothing; + intPtrTable[_gpioPin].context = NULL; + detachInterrupt(_gpioPin); + } +} + + +uint32_t s_getTicker(void) +{ + long long ns = ns_getTicker(); + ns /= (long long)1000000000; // to secs + + int secs = ns; + return secs; +} + + +uint32_t ms_getTicker(void) +{ + uint32_t us = us_getTicker(); + + us /= 1000; // to ms + return us; +} + +uint32_t us_getTicker(void) +{ + long long ns = ns_getTicker(); + + ns /= (long long)1000; // to us + uint32_t us = ns & 0xffffffff; + + return us; +} + + +void +Timeout::insert(void) +{ + noInterrupts(); + for (int i = 0; i < MAX_TIMEOUTS-1; i++) { + struct TimeoutVector *tvp = &TimeOuts[i]; + if (tvp->timer == this) // already here, timer has been restartet. + break; + if (tvp->timer == NULL) { + tvp->timer = this; + break; + } + } + interrupts(); +} + +void +Timeout::remove(void) +{ + noInterrupts(); + for (int i = 0; i < MAX_TIMEOUTS-1; i++) { + struct TimeoutVector *tvp = &TimeOuts[i]; + if (tvp->timer == this) { + tvp->timer = NULL; + break; + } + } + interrupts(); +} + + +void +Timeout::restart() +{ + Tcc *t = getTimeout_tcc(); + uint64_t timeout = ~0; + + /* + * find the lowest timeout value which is our the next timeout + * zero means stop the timer. + */ + noInterrupts(); + for (int i = 0; i < MAX_TIMEOUTS-1; i++) { + struct TimeoutVector *tvp = &TimeOuts[i]; + if (tvp->timer) { + if (tvp->timer->_timeout < timeout) { + timeout = tvp->timer->_timeout; + } + } + } + interrupts(); + + if (timeout == (uint64_t)~0) { + stopTimer(t); + return; + } + + uint64_t nsecs = ns_getTicker(); + + if (timeout > nsecs) { + startTimer(t, (uint64_t)timeout - (uint64_t)nsecs); + return; + } else { + startTimer(t, (uint64_t)1); // just one nsec to trigger interrrupt + } +} + +#endif // ARDUINO
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/Arduino-mbed-APIs/arduino-mbed.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,330 @@ +/* + * The file is Licensed under the Apache License, Version 2.0 + * (c) 2017 Helmut Tschemernjak + * 30826 Garbsen (Hannover) Germany + */ + + + +#ifdef ARDUINO +#ifndef __ARDUINO_MBED_H__ +#define __ARDUINO_MBED_H__ + +#include <arduino.h> +#include "Callback-A.h" +#include <SPI.h> +#undef min +#undef max +#undef map + +typedef int PinName; +#define NC -1 +/* we need to redefine out dprintf because stdio.h uses the same name */ +#define dprint dxprintf +#if ARDUINO_SAMD_VARIANT_COMPLIANCE >= 10606 + #define MYdigitalPinToInterrupt(x) digitalPinToInterrupt(x) +#else + #define MYdigitalPinToInterrupt(x) (x) +#endif + +void InitSerial(Stream *serial, int timeout_ms); +extern Stream *ser; +extern bool SerialUSB_active; + +/* + * Arduino_d21.cpp + */ +extern void startTimer(Tcc *t, uint64_t delay_ns); +extern void stopTimer(Tcc *t); +extern uint64_t ns_getTicker(void); +extern Tcc *getTimeout_tcc(void); +extern int CPUID(uint8_t *buf, int maxSize, uint32_t xorval); + + +extern void sleep(void); +extern void deepsleep(void); + +#define MAX_TIMEOUTS 10 +class Timeout; +struct TimeoutVector { + Timeout *timer; +}; +extern TimeoutVector TimeOuts[]; + + +/* + * Arduino-mbed.cpp + */ +extern uint32_t s_getTicker(void); +extern uint32_t ms_getTicker(void); +extern uint32_t us_getTicker(void); +extern void wait_ms(uint32_t ms); + + +enum PinMode { + PullUp = 1, + PullDown = 2, +}; + +class DigitalInOut { +public: + DigitalInOut(PinName pin) { + _gpioPin = pin; + } + void write(int value) { + digitalWrite(_gpioPin, value == 0 ? LOW : HIGH); + }; + + void output() { + pinMode(_gpioPin, OUTPUT); + }; + + void input() { + pinMode(_gpioPin, INPUT); + }; + + void mode(PinMode pull) { + switch(pull) { + case PullUp: + pinMode(_gpioPin, INPUT_PULLUP); + break; + case PullDown: + pinMode(_gpioPin, INPUT_PULLDOWN); + break; + } + } + + int read() { + if (digitalRead(_gpioPin) == HIGH) + return 1; + else + return 0; + }; + operator int() { + return read(); + }; + + DigitalInOut& operator= (int value) { + // Underlying write is thread safe + write(value); + return *this; + } + + DigitalInOut& operator= (DigitalInOut& rhs) { + write(rhs.read()); + return *this; + } + +private: + int _gpioPin; +}; + +class DigitalOut : public DigitalInOut { +public: + + DigitalOut(PinName pin) : DigitalInOut(pin) { + output(); + } + + DigitalOut& operator= (int value) { + write(value); + return *this; + } + +}; + +class DigitalIn : public DigitalInOut { +public: + + DigitalIn(PinName pin) : DigitalInOut(pin) { + input(); + } +}; + +class XSPI { +public: + XSPI(PinName mosi, PinName miso, PinName sclk) { + _mosi = mosi; + _miso = miso; + _sclk = sclk; + if (mosi == PIN_SPI_MOSI && miso == PIN_SPI_MISO && sclk == PIN_SPI_SCK) + _spi = &SPI; +#if SPI_INTERFACES_COUNT > 1 + else if (mosi == PIN_SPI1_MOSI && miso == PIN_SPI1_MISO && sclk == PIN_SPI1_SCK) + _spi = &SPI1; +#endif +#if SPI_INTERFACES_COUNT > 2 + else if (mosi == PIN_SPI2_MOSI && miso == PIN_SPI2_MISO && sclk == PIN_SPI2_SCK) + _spi = &SPI2; +#endif + else { + _spi = NULL; + return; + } + _hz = 1000000; + _mode = SPI_MODE0; + _spi->beginTransaction(SPISettings(_hz, MSBFIRST, _mode)); + } + ~XSPI() { + _spi->endTransaction(); + }; + + void format(int bits, int mode = 0) { + if (mode == 0) + _mode = SPI_MODE0; + else if (mode == 1) + _mode = SPI_MODE1; + else if (mode == 2) + _mode = SPI_MODE2; + else if (mode == 3) + _mode = SPI_MODE3; + else + _mode = SPI_MODE0; + _bits = bits; + _spi->endTransaction(); + _spi->beginTransaction(SPISettings(_hz, MSBFIRST, _mode)); + } + void frequency(int hz) { + _hz = hz; + _spi->endTransaction(); + _spi->beginTransaction(SPISettings(_hz, MSBFIRST, _mode)); + } + + int write(int value) { + int ret = _spi->transfer(value); + return ret; + } + +private: + SPIClass *_spi; + int _hz; + int _mode; + int _bits; + int _mosi, _miso, _sclk; +}; + +class InterruptIn { +public: + static void donothing(void) { + } + + InterruptIn(PinName pin) : _func() { + _gpioPin = pin; + _func = InterruptIn::donothing; + pinMode(_gpioPin, INPUT); + } + + ~InterruptIn() { + detachInterrupt(MYdigitalPinToInterrupt(_gpioPin)); + }; + + static void _irq_handler(InterruptIn *id) { + if (id) + id->_func(); + } + + void rise(Callback<void()> func); + + void fall(Callback<void()> func); + + void mode(PinMode pull) { + switch(pull) { + case PullUp: + pinMode(_gpioPin, INPUT_PULLUP); + break; + case PullDown: + pinMode(_gpioPin, INPUT_PULLDOWN); + break; + } + } +private: + int _gpioPin; + Callback<void()> _func; +}; + + + +class Timer { +public: + void start(void) { + _time = ns_getTicker(); + } + uint32_t read_sec(void) { + int64_t n = ns_getTicker() - (uint64_t)_time; + n /= (uint64_t)1000000000; + return n; + } + uint32_t read_ms(void) { + int64_t n = ns_getTicker() - (uint64_t)_time; + n /= (uint64_t)1000000; + return n; + } + uint32_t read_us(void) { + int64_t n = ns_getTicker() - (uint64_t)_time; + n /= (uint64_t)1000; + return n; + } +private: + uint64_t _time; +}; + + +class Timeout { +public: + Timeout() : _func() { + } + ~Timeout() { + detach(); + } + + void attach_sec(Callback<void()> func, uint32_t secs) { + if (secs == 0) + return detach(); + _func = func; + _timeout = ns_getTicker() + (uint64_t)secs * (uint64_t)1000000000; + insert(); + restart(); + } + + void attach(Callback<void()> func, uint32_t msecs) { + if (msecs == 0) + return detach(); + _func = func; + _timeout = ns_getTicker() + (uint64_t)msecs * (uint64_t)1000000; + insert(); + restart(); + } + + void attach_us(Callback<void()> func, long usecs) { + if (usecs == 0) + return detach(); + _func = func; + _timeout = ns_getTicker() + (uint64_t)usecs * (uint64_t)1000; + insert(); + restart(); + } + + void detach(void) { + _func = NULL; + remove(); + restart(); + } + + static void _irq_handler(Timeout *tp) { + if (tp) { + tp->_func(); + } + } + + static void restart(void); + uint64_t _timeout; // in ns this lasts for 539 years. +protected: + void insert(void); + void remove(void); +private: + Callback<void()> _func; +}; + +#endif // __ARDUINO_MBED_H__ + +#endif // ARDUINO
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/Arduino-mbed-APIs/arduino-util.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,99 @@ +#ifdef ARDUINO + +#include <Arduino.h> +#include "arduino-util.h" +#include <cstdarg> +#include <stdio.h> + + +char tmpbuf[160]; +extern int us_getTicker(void); +extern int s_getTicker(void); +extern Stream *ser; + +void +dprintf(const char *format, ...) +{ + static volatile bool busy; + if (busy) + return; + busy = true; + + int secs = s_getTicker(); + int s = secs % 60; + int m = secs / 60; + int h = secs / 3600; + int us = us_getTicker() % 1000000; + + snprintf(tmpbuf, sizeof(tmpbuf)-1, "%02d:%02d:%02d.%.06d ", h, m, s, us); + ser->write(tmpbuf, (int) sizeof "00:00:34.3436868 " -1); + + va_list arg; + va_start(arg, format); + int len = vsnprintf(tmpbuf, sizeof(tmpbuf)-3, format, arg); + tmpbuf[len] = '\r'; + tmpbuf[len+1] = '\n'; + tmpbuf[len+2] = 0; + ser->write(tmpbuf, len+3); + va_end(arg); + busy = false; +} + +void +rprintf(const char *format, ...) +{ + va_list arg; + va_start(arg, format); + int len = vsnprintf(tmpbuf, sizeof(tmpbuf)-3, format, arg); + tmpbuf[len] = 0; + ser->write(tmpbuf, len+1); + va_end(arg); +} + +void +dump(const char *title, const void *data, int len) +{ + dprintf("dump(\"%s\", 0x%x, %d bytes)", title, data, len); + + int i, j, cnt; + unsigned char *u; + const int width = 16; + const int seppos = 7; + + cnt = 0; + u = (unsigned char *)data; + while (len > 0) { + rprintf("%08x: ", (unsigned int)data + cnt); + cnt += width; + j = len < width ? len : width; + for (i = 0; i < j; i++) { + rprintf("%2.2x ", *(u + i)); + if (i == seppos) + ser->write(' '); + } + ser->write(' '); + if (j < width) { + i = width - j; + if (i > seppos + 1) + ser->write(' '); + while (i--) { + ser->print(" "); + } + } + for (i = 0; i < j; i++) { + int c = *(u + i); + if (c >= ' ' && c <= '~') + ser->write(c); + else + ser->write('.'); + if (i == seppos) + ser->write(' '); + } + len -= width; + u += width; + ser->print("\r\n"); + } + ser->print("--\r\n"); + +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/Arduino-mbed-APIs/arduino-util.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,18 @@ +/* + * The file is Licensed under the Apache License, Version 2.0 + * (c) 2017 Helmut Tschemernjak + * 30826 Garbsen (Hannover) Germany + */ + +#ifdef ARDUINO +#ifndef __ARDUINO_UTIL_H__ +#define __ARDUINO_UTIL_H__ + + +extern void dprintf(const char *format, ...); + +extern void dump(const char *title, const void *data, int len); + +#endif // __ARDUINO_UTIL_H__ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/Arduino-mbed-APIs/examples/TimerTest/TimerTest.ino Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,73 @@ + +#include "arduino-mbed.h" + +void TestTimeoutFunc(void); +void TestTimeoutFunc55(void); +void TestTimeoutFunc10(void); +void TestTimeoutFunc1m(void); +void SwitchInput(void); + +#define SW0 3 // switch needs pullup +#define LED LED_BUILTIN +#define MYSERIAL Serial + +DigitalOut led(LED); +InterruptIn intr(SW0); +Timeout tp; +Timeout tp2; +Timeout tp3; +Timeout tp4; + +void setup() { + MYSERIAL.begin(230400); + InitSerial(&MYSERIAL); + + ser->println("TimerTest"); + + tp.attach(callback(&TestTimeoutFunc), 1000); + tp2.attach(callback(&TestTimeoutFunc55), 5500); + tp3.attach(callback(&TestTimeoutFunc10), 10000); + // tp4.attach(callback(&TestTimeoutFunc1m), 1); + + intr.mode(PullUp); + intr.fall(callback(&SwitchInput)); +} + +void loop() { + led = !led; + + sleep(); // or deepsleep() +} + + + +void TestTimeoutFunc(void) { + tp.attach(callback(&TestTimeoutFunc), 1000); + led = !led; + ser->print(ms_getTicker(), DEC); + ser->println(" TestTimeoutFunc 1 sec"); +} + +void TestTimeoutFunc55(void) { + tp2.attach(callback(&TestTimeoutFunc55), 5500); + ser->print(ms_getTicker(), DEC); + ser->println(" TestTimeoutFunc 5.5 sec"); +} + + +void TestTimeoutFunc10(void) { + tp3.attach(callback(&TestTimeoutFunc10), 10000); + ser->print(ms_getTicker(), DEC); + ser->println(" TestTimeoutFunc 10 sec"); +} + + +void TestTimeoutFunc1m(void) { + tp4.attach(callback(&TestTimeoutFunc1m), 1); +} + +void SwitchInput(void) { + ser->print(ms_getTicker(), DEC); + ser->println(" SwitchInput"); + led = !led; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/Arduino-mbed-APIs/library.properties Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,9 @@ +name=Arduino-mbed-APIs +version=1.0.0 +author=Helmut Tschemernjak <helmut2009@me.com> +maintainer=Helmut Tschemernjak <helmut2009@me.com> +sentence=Arduino-mbed-APIs to support mbed like APIs including Timer, Timeout, SPI, InterruptIn, DigialIn, DigitalOut, DigitalInOut, sleep, deepsleep, it also includes a SAMD TCC based Timer implementation for correct timestamps and timeouts. +paragraph= +category=Other +url=http://www.radioshuttle.de +architectures=samd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/LICENSE.txt Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,25 @@ +--- Revised BSD License --- +Copyright (c) 2013, SEMTECH S.A. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the Semtech corporation nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL SEMTECH S.A. 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. \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/LoRa_TODO.txt Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,50 @@ + +Move finished tasks to Done section: + +TODOs: +- add support for Linux - add sx1276-Linux-hal.h/cpp +- Add support to provide the send/receive packet buffer, + no need to allocate packet data in the sx1276 driver. Can be provided + Rx/Tx parameters, this avoids double memory usage +- Add support for larger Lora packets (can be up to 2048 bytes) + this feature is not so important, however the current implementation + is very basic. +- It is a little bit strange that RX/TX/Cad Timeout Timer calling the + some handler OnTimeoutIrq. Maybe we just need a single timer, or + it is a good idea to split the OnTimeoutIrq function into separate + callbacks for RX/TX/Cad timeouts +- Test if the SX1276 timeouts. Does rx/tx/sync really uses three different + timers or just one at a time. +- Add API to set the LNA gain + + + +Done: +- Started a Generic SX1276 driver to support all SX1276 modules (May-2017 Helmut) +- Migrated typedefs code into sx1276.h (7-May-2017 Helmut) +- Migrated enum code into sx1276.h/radio.h (7-May-2017 Helmut) +- Verify the Murata ANT Switch code +- MURATA PA_BOOST case,is _antSwitchTXBoost right? (Same as STM sample code) +- Check of the MURATA TCXO config is correct (implemented, check JP9 on STM L0 board) +- Make the timers more generic and move the OS code into the HAL layer. (May 2017 Helmut) +- Removed pull down on dio=-dio5 for L151 &LPC11U6X which make no sense to me. May 2017 Helmut +- Added radio API support to receive the MaxMTUSize (May 2017 Helmut) +- Added Send optional Send() parameter to include a header, + this saves additional buffers. (May 2017 Helmut) +- Added proper void * type from sending data, uint8_t * is not appropriate (May 2017 Helmut) +- Use also void pointer for FiFo Write/Read and regular SPI Read/Write +- Added return value to Init, we check for a radio availability (May 2017 Helmut) +- Added a RxSignalPending which verifies if we have a signal pending in receive state. (May 2017 Helmut) +- Added LoRa bandwidth mapping table, now the SetRx/Tx frequency is in Hz. (May 2017 Helmut) +- Enabled MURATA_SX1276 for the MURATA_SX1276 chip (May 2017 Helmut) +- Made SetRfTxPower public to allow easily power TX changes (May 2017 Helmut) +- Added userData and userThisPtr into the radio events, this allows to call C++ + functions and in can include a context via the userData +- Add support for Cad detection before sending a packet, already done in higher + level protocols +- Added initial Arduino support, needs more testing/completion. +- Support for Arduino completed, initial version works. +- Added GetFrequency support + The Murata’s Frequency shift using an TCXO us about 58 Hz + The RFM95 against Murata is about 3300 Hz + RFM95 against RFM95 testing will follow.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/README.md Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,53 @@ +# SX1276Generic Driver +/* +* (c) 2017 Helmut Tschemernjak (Helmut64 on mbed). +* 30826 Garbsen (Hannover) Germany +*/ + +This library represents a common SX1276 module driver supporting SX1276 +based modules. The approach is to support multiple OS versions including +mbed, Arduino and Linux using the same driver code and little +adjustments for the different OS version. The SX1276 driver is based on +the Semtech 1276 code which can be seen in the revisions of this library +repository. + +## Supported LoRa Modules + +The following Lora modules are supported: +- HopeRF RFM95 +- Murata MURATA_SX1276 (CMWX1ZZABZ-078, used the STM B_L072Z_LRWAN1 board) +- SX1276MB1MAS (433, 868 MHz version) +- SX1276MB1LAS (433, 915 MHz version) + +## Getting Started for Developers +Import the mbed sample project: +http://developer.mbed.org/users/Helmut64/code/STM32L0_LoRa +- It includes a PingPong sample code +- It includes a PinMap.h which allows to define the LoRa SPI, +DIO interrupt, reset and antenna pins. +The STM32L0_LoRa is a turnkey sample application for the STM B_L072Z_LRWAN1, +however it will work with all other mbed based boards by adjusting the PinMap.h + +## Developers help needed +A list of tasks is documented in the file: LoRa_TODO.txt +I (Helmut Tschemernjak) spend a very significant time to complete the +initial version of the SX1276Generic packet driver. Enhancements, +further module support and tuning is more than welcome. Please send me +your patches via mbed. Also questions can be submitted in the mbed +“Questions” area or a personal message via mbed. + +## Future developments +I work in a advanced private protocol using basic LoRa modules to +communicate between simple nodes (battery powered) and stations +(permanent power). The station should support thousands of nodes running +on an Linux based OS using this 1276Generic driver or the LoRa +concentrator module. The station should also work on mbed or Arduino +assuming sufficient memory is provided. I believe there is an +opportunity to do a better protocol compared to the official LoRa +protocol which requires an Concentrator, a LoRa server and an +application server. The idea is to over only efficient, reliable and +secure communication between the nodes and the stations. Further +forwarding to MQTT and other network services can be handled separately +on the station. + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/radio/radio.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,20 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C) 2014 Semtech + +Description: Interface for the radios, contains the main functions that a radio needs, and 5 callback functions + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainers: Miguel Luis, Gregory Cristian and Nicolas Huguenin +*/ +#include "radio.h" + +Radio::Radio( RadioEvents_t *events ) +{ + this->RadioEvents = events; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/radio/radio.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,516 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C) 2014 Semtech + +Description: Interface for the radios, contains the main functions that a radio needs, and 5 callback functions + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainers: Miguel Luis, Gregory Cristian and Nicolas Huguenin +*/ +#ifndef __RADIO_H__ +#define __RADIO_H__ + +#ifdef __MBED__ +#include "mbed.h" +#endif +#ifdef ARDUINO +#include <arduino.h> +#endif + +/*! + * Radio driver internal state machine states definition + */ +typedef enum RadioState +{ + RF_IDLE = 0, + RF_RX_RUNNING, + RF_TX_RUNNING, + RF_CAD, +} RadioState_t; + + +/*! + * Type of the modem. [LORA / FSK] + */ +typedef enum ModemType +{ + MODEM_FSK = 0, + MODEM_LORA +}RadioModems_t; + + +/*! + * Radio LoRa modem parameters + */ +typedef struct +{ + int8_t Power; + uint32_t Bandwidth; + uint32_t Datarate; + bool LowDatarateOptimize; + uint8_t Coderate; + uint16_t PreambleLen; + bool FixLen; + uint8_t PayloadLen; + bool CrcOn; + bool FreqHopOn; + uint8_t HopPeriod; + bool IqInverted; + bool RxContinuous; + uint32_t TxTimeout; + bool PublicNetwork; +}RadioLoRaSettings_t; + +/*! + * Radio FSK modem parameters + */ +typedef struct +{ + int8_t Power; + uint32_t Fdev; + uint32_t Bandwidth; + uint32_t BandwidthAfc; + uint32_t Datarate; + uint16_t PreambleLen; + bool FixLen; + uint8_t PayloadLen; + bool CrcOn; + bool IqInverted; + bool RxContinuous; + uint32_t TxTimeout; + uint32_t RxSingleTimeout; +}RadioFskSettings_t; + +/*! + * Radio FSK packet handler state + */ +typedef struct +{ + uint8_t PreambleDetected; + uint8_t SyncWordDetected; + int8_t RssiValue; + int32_t AfcValue; + uint8_t RxGain; + uint16_t Size; + uint16_t NbBytes; + uint8_t FifoThresh; + uint8_t ChunkSize; +}RadioFskPacketHandler_t; + +/*! + * Radio LoRa packet handler state + */ +typedef struct +{ + int8_t SnrValue; + int8_t RssiValue; + uint8_t Size; +}RadioLoRaPacketHandler_t; + +/*! + * Radio Settings + */ +typedef struct +{ + RadioState State; + ModemType Modem; + uint32_t Channel; + RadioFskSettings_t Fsk; + RadioFskPacketHandler_t FskPacketHandler; + RadioLoRaSettings_t LoRa; + RadioLoRaPacketHandler_t LoRaPacketHandler; +}RadioSettings_t; + +/*! + * @brief Radio driver callback functions + */ +typedef struct +{ + /*! + * @brief Tx Done callback prototype. + */ + void ( *TxDone )(void *radio, void *userThisPtr, void *userData); + /*! + * @brief Tx Timeout callback prototype. + */ + void ( *TxTimeout )(void *radio, void *userThisPtr, void *userData); + /*! + * @brief Rx Done callback prototype. + * + * @param [IN] payload Received buffer pointer + * @param [IN] size Received buffer size + * @param [IN] rssi RSSI value computed while receiving the frame [dBm] + * @param [IN] snr Raw SNR value given by the radio hardware + * FSK : N/A ( set to 0 ) + * LoRa: SNR value in dB + */ + void ( *RxDone )(void *radio, void *userThisPtr, void *userData, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); + /*! + * @brief Rx Timeout callback prototype. + */ + void ( *RxTimeout )(void *radio, void *userThisPtr, void *userData); + /*! + * @brief Rx Error callback prototype. + */ + void ( *RxError )(void *radio, void *userThisPtr, void *userData); + /*! + * \brief FHSS Change Channel callback prototype. + * + * \param [IN] currentChannel Index number of the current channel + */ + void ( *FhssChangeChannel )(void *radio, void *userThisPtr, void *userData, uint8_t currentChannel ); + + /*! + * @brief CAD Done callback prototype. + * + * @param [IN] channelDetected Channel Activity detected during the CAD + */ + void ( *CadDone ) (void *radio, void *userThisPtr, void *userData, bool channelActivityDetected ); + + /* + * Optional data which is being provided in callbacks + * can be used e.g. for the C++ this pointer. + */ + void *userThisPtr; + + /* + * Optional data which allows to bring in data structures pointer + * for a given radio. As callbacks are being called in interrupt level + * the userData is perfect to know the context without iterating this + * data structures. + */ + void *userData; + +}RadioEvents_t; + +/*! + * Interface for the radios, contains the main functions that a radio needs, and 5 callback functions + */ +class Radio +{ +protected: + RadioEvents_t* RadioEvents; + +public: + //------------------------------------------------------------------------- + // Constructor + //------------------------------------------------------------------------- + /*! + * @brief Constructor of the radio object, the parameters are the callback functions described in the header. + * + * @param [IN] events Structure containing the driver callback functions + */ + Radio( RadioEvents_t *events ); + virtual ~Radio( ) {}; + + //------------------------------------------------------------------------- + // Pure virtual functions + //------------------------------------------------------------------------- + + /*! + * @brief Initializes the radio + * + * @param [IN] events Structure containing the driver callback functions + */ + virtual bool Init( RadioEvents_t *events ) = 0; + + /*! + * @brief Return current radio status, returns true if a radios has been found. + * + * @param status Radio status. [RF_IDLE, RX_RUNNING, TX_RUNNING, CAD_RUNNING] + */ + virtual RadioState GetStatus( void ) = 0; + + /*! + * @brief Configures the radio with the given modem + * + * @param [IN] modem Modem to be used [0: FSK, 1: LoRa] + */ + virtual void SetModem( RadioModems_t modem ) = 0; + + /*! + * @brief Sets the channel frequency + * + * @param [IN] freq Channel RF frequency + */ + virtual void SetChannel( uint32_t freq ) = 0; + + /*! + * @brief Sets the channels configuration + * + * @param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * @param [IN] freq Channel RF frequency + * @param [IN] rssiThresh RSSI threshold + * + * @retval isFree [true: Channel is free, false: Channel is not free] + */ + virtual bool IsChannelFree( RadioModems_t modem, uint32_t freq, int16_t rssiThresh ) = 0; + + /*! + * @brief Generates a 32 bits random value based on the RSSI readings + * + * \remark This function sets the radio in LoRa modem mode and disables + * all interrupts. + * After calling this function either Radio.SetRxConfig or + * Radio.SetTxConfig functions must be called. + * + * @retval randomValue 32 bits random value + */ + virtual uint32_t Random( void )= 0; + + /*! + * @brief Sets the reception parameters + * + * @param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * @param [IN] bandwidth Sets the bandwidth + * FSK : >= 2600 and <= 250000 Hz + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * @param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * @param [IN] coderate Sets the coding rate ( LoRa only ) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * @param [IN] bandwidthAfc Sets the AFC Bandwidth ( FSK only ) + * FSK : >= 2600 and <= 250000 Hz + * LoRa: N/A ( set to 0 ) + * @param [IN] preambleLen Sets the Preamble length ( LoRa only ) + * FSK : N/A ( set to 0 ) + * LoRa: Length in symbols ( the hardware adds 4 more symbols ) + * @param [IN] symbTimeout Sets the RxSingle timeout value + * FSK : timeout number of bytes + * LoRa: timeout in symbols + * @param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * @param [IN] payloadLen Sets payload length when fixed lenght is used + * @param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * @param [IN] freqHopOn Enables disables the intra-packet frequency hopping [0: OFF, 1: ON] (LoRa only) + * @param [IN] hopPeriod Number of symbols bewteen each hop (LoRa only) + * @param [IN] iqInverted Inverts IQ signals ( LoRa only ) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * @param [IN] rxContinuous Sets the reception in continuous mode + * [false: single mode, true: continuous mode] + */ + virtual void SetRxConfig ( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool freqHopOn, uint8_t hopPeriod, + bool iqInverted, bool rxContinuous ) = 0; + + /*! + * @brief Sets the transmission parameters + * + * @param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * @param [IN] power Sets the output power [dBm] + * @param [IN] fdev Sets the frequency deviation ( FSK only ) + * FSK : [Hz] + * LoRa: 0 + * @param [IN] bandwidth Sets the bandwidth ( LoRa only ) + * FSK : 0 + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * @param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * @param [IN] coderate Sets the coding rate ( LoRa only ) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * @param [IN] preambleLen Sets the preamble length + * @param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * @param [IN] crcOn Enables disables the CRC [0: OFF, 1: ON] + * @param [IN] freqHopOn Enables disables the intra-packet frequency hopping [0: OFF, 1: ON] (LoRa only) + * @param [IN] hopPeriod Number of symbols bewteen each hop (LoRa only) + * @param [IN] iqInverted Inverts IQ signals ( LoRa only ) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * @param [IN] timeout Transmission timeout [us] + */ + virtual void SetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool freqHopOn, + uint8_t hopPeriod, bool iqInverted, uint32_t timeout ) = 0; + + /*! + * @brief Checks if the given RF frequency is supported by the hardware + * + * @param [IN] frequency RF frequency to be checked + * @retval isSupported [true: supported, false: unsupported] + */ + virtual bool CheckRfFrequency( uint32_t frequency ) = 0; + + /*! + * @brief Computes the packet time on air for the given payload + * + * \Remark Can only be called once SetRxConfig or SetTxConfig have been called + * + * @param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * @param [IN] pktLen Packet payload length + * + * @retval airTime Computed airTime for the given packet payload length + */ + virtual uint32_t TimeOnAir ( RadioModems_t modem, int16_t pktLen ) = 0; + + /*! + * @brief Sends the buffer of size. Prepares the packet to be sent and sets + * the radio in transmission + * + * @param [IN]: buffer Buffer pointer + * @param [IN]: size Buffer size + * @param [IN]: buffer Header pointer + * @param [IN]: size Header size + */ + virtual void Send( void *buffer, int16_t size, void *header = NULL, int16_t hsize = 0) = 0; + + /*! + * @brief Sets the radio in sleep mode + */ + virtual void Sleep( void ) = 0; + + /*! + * @brief Sets the radio in standby mode + */ + virtual void Standby( void ) = 0; + + /*! + * @brief Sets the radio in CAD mode + */ + virtual void StartCad( void ) = 0; + + /*! + * @brief Sets the radio in reception mode for the given time + * @param [IN] timeout Reception timeout [us] + * [0: continuous, others timeout] + */ + virtual void Rx( uint32_t timeout ) = 0; + + /*! + * @brief Check is radio receives a signal + */ + virtual bool RxSignalPending() = 0; + + + /*! + * @brief Sets the radio in transmission mode for the given time + * @param [IN] timeout Transmission timeout [us] + * [0: continuous, others timeout] + */ + virtual void Tx( uint32_t timeout ) = 0; + + /*! + * @brief Sets the radio in continuous wave transmission mode + * + * @param [IN]: freq Channel RF frequency + * @param [IN]: power Sets the output power [dBm] + * @param [IN]: time Transmission mode timeout [s] + */ + virtual void SetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time ) = 0; + + /*! + * @brief Returns the maximal transfer unit for a given modem + * + * @retval MTU size in bytes + */ + virtual int16_t MaxMTUSize( RadioModems_t modem ) = 0; + + /*! + * @brief Reads the current RSSI value + * + * @retval rssiValue Current RSSI value in [dBm] + */ + virtual int16_t GetRssi ( RadioModems_t modem ) = 0; + + /*! + * @brief Reads the current frequency error + * + * @retval frequency error value in [Hz] + */ + virtual int32_t GetFrequencyError( RadioModems_t modem ) = 0; + + /*! + * @brief Writes the radio register at the specified address + * + * @param [IN]: addr Register address + * @param [IN]: data New register value + */ + virtual void Write ( uint8_t addr, uint8_t data ) = 0; + + /*! + * @brief Reads the radio register at the specified address + * + * @param [IN]: addr Register address + * @retval data Register value + */ + virtual uint8_t Read ( uint8_t addr ) = 0; + + /*! + * @brief Writes multiple radio registers starting at address + * + * @param [IN] addr First Radio register address + * @param [IN] buffer Buffer containing the new register's values + * @param [IN] size Number of registers to be written + */ + virtual void Write( uint8_t addr, void *buffer, uint8_t size ) = 0; + + /*! + * @brief Reads multiple radio registers starting at address + * + * @param [IN] addr First Radio register address + * @param [OUT] buffer Buffer where to copy the registers data + * @param [IN] size Number of registers to be read + */ + virtual void Read ( uint8_t addr, void *buffer, uint8_t size ) = 0; + + /*! + * @brief Writes the buffer contents to the Radio FIFO + * + * @param [IN] buffer Buffer containing data to be put on the FIFO. + * @param [IN] size Number of bytes to be written to the FIFO + */ + virtual void WriteFifo( void *buffer, uint8_t size ) = 0; + + /*! + * @brief Reads the contents of the Radio FIFO + * + * @param [OUT] buffer Buffer where to copy the FIFO read data. + * @param [IN] size Number of bytes to be read from the FIFO + */ + virtual void ReadFifo( void *buffer, uint8_t size ) = 0; + + /*! + * @brief Sets the maximum payload length. + * + * @param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * @param [IN] max Maximum payload length in bytes + */ + virtual void SetMaxPayloadLength( RadioModems_t modem, uint8_t max ) = 0; + + /*! + * @brief Sets the network to public or private. Updates the sync byte. + * + * @remark Applies to LoRa modem only + * + * @param [IN] enable if true, it enables a public network + */ + virtual void SetPublicNetwork( bool enable ) = 0; + + /*! + * \brief Sets the radio output power. + * + * @param [IN] power Sets the RF output power + */ + virtual void SetRfTxPower( int8_t power ) = 0; + +}; + +#endif // __RADIO_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/registers/sx1276Regs-Fsk.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,1134 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C) 2014 Semtech + +Description: SX1276 FSK modem registers and bits definitions + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __SX1276_REGS_FSK_H__ +#define __SX1276_REGS_FSK_H__ + +/*! + * ============================================================================ + * SX1276 Internal registers Address + * ============================================================================ + */ +#define REG_FIFO 0x00 +// Common settings +#define REG_OPMODE 0x01 +#define REG_BITRATEMSB 0x02 +#define REG_BITRATELSB 0x03 +#define REG_FDEVMSB 0x04 +#define REG_FDEVLSB 0x05 +#define REG_FRFMSB 0x06 +#define REG_FRFMID 0x07 +#define REG_FRFLSB 0x08 +// Tx settings +#define REG_PACONFIG 0x09 +#define REG_PARAMP 0x0A +#define REG_OCP 0x0B +// Rx settings +#define REG_LNA 0x0C +#define REG_RXCONFIG 0x0D +#define REG_RSSICONFIG 0x0E +#define REG_RSSICOLLISION 0x0F +#define REG_RSSITHRESH 0x10 +#define REG_RSSIVALUE 0x11 +#define REG_RXBW 0x12 +#define REG_AFCBW 0x13 +#define REG_OOKPEAK 0x14 +#define REG_OOKFIX 0x15 +#define REG_OOKAVG 0x16 +#define REG_RES17 0x17 +#define REG_RES18 0x18 +#define REG_RES19 0x19 +#define REG_AFCFEI 0x1A +#define REG_AFCMSB 0x1B +#define REG_AFCLSB 0x1C +#define REG_FEIMSB 0x1D +#define REG_FEILSB 0x1E +#define REG_PREAMBLEDETECT 0x1F +#define REG_RXTIMEOUT1 0x20 +#define REG_RXTIMEOUT2 0x21 +#define REG_RXTIMEOUT3 0x22 +#define REG_RXDELAY 0x23 +// Oscillator settings +#define REG_OSC 0x24 +// Packet handler settings +#define REG_PREAMBLEMSB 0x25 +#define REG_PREAMBLELSB 0x26 +#define REG_SYNCCONFIG 0x27 +#define REG_SYNCVALUE1 0x28 +#define REG_SYNCVALUE2 0x29 +#define REG_SYNCVALUE3 0x2A +#define REG_SYNCVALUE4 0x2B +#define REG_SYNCVALUE5 0x2C +#define REG_SYNCVALUE6 0x2D +#define REG_SYNCVALUE7 0x2E +#define REG_SYNCVALUE8 0x2F +#define REG_PACKETCONFIG1 0x30 +#define REG_PACKETCONFIG2 0x31 +#define REG_PAYLOADLENGTH 0x32 +#define REG_NODEADRS 0x33 +#define REG_BROADCASTADRS 0x34 +#define REG_FIFOTHRESH 0x35 +// SM settings +#define REG_SEQCONFIG1 0x36 +#define REG_SEQCONFIG2 0x37 +#define REG_TIMERRESOL 0x38 +#define REG_TIMER1COEF 0x39 +#define REG_TIMER2COEF 0x3A +// Service settings +#define REG_IMAGECAL 0x3B +#define REG_TEMP 0x3C +#define REG_LOWBAT 0x3D +// Status +#define REG_IRQFLAGS1 0x3E +#define REG_IRQFLAGS2 0x3F +// I/O settings +#define REG_DIOMAPPING1 0x40 +#define REG_DIOMAPPING2 0x41 +// Version +#define REG_VERSION 0x42 +// Additional settings +#define REG_PLLHOP 0x44 +#define REG_TCXO 0x4B +#define REG_PADAC 0x4D +#define REG_FORMERTEMP 0x5B +#define REG_BITRATEFRAC 0x5D +#define REG_AGCREF 0x61 +#define REG_AGCTHRESH1 0x62 +#define REG_AGCTHRESH2 0x63 +#define REG_AGCTHRESH3 0x64 +#define REG_PLL 0x70 + +/*! + * ============================================================================ + * SX1276 FSK bits control definition + * ============================================================================ + */ + +/*! + * RegFifo + */ + +/*! + * RegOpMode + */ +#define RF_OPMODE_LONGRANGEMODE_MASK 0x7F +#define RF_OPMODE_LONGRANGEMODE_OFF 0x00 +#define RF_OPMODE_LONGRANGEMODE_ON 0x80 + +#define RF_OPMODE_MODULATIONTYPE_MASK 0x9F +#define RF_OPMODE_MODULATIONTYPE_FSK 0x00 // Default +#define RF_OPMODE_MODULATIONTYPE_OOK 0x20 + +#define RF_OPMODE_MODULATIONSHAPING_MASK 0xE7 +#define RF_OPMODE_MODULATIONSHAPING_00 0x00 // Default +#define RF_OPMODE_MODULATIONSHAPING_01 0x08 +#define RF_OPMODE_MODULATIONSHAPING_10 0x10 +#define RF_OPMODE_MODULATIONSHAPING_11 0x18 + +#define RF_OPMODE_MASK 0xF8 +#define RF_OPMODE_SLEEP 0x00 +#define RF_OPMODE_STANDBY 0x01 // Default +#define RF_OPMODE_SYNTHESIZER_TX 0x02 +#define RF_OPMODE_TRANSMITTER 0x03 +#define RF_OPMODE_SYNTHESIZER_RX 0x04 +#define RF_OPMODE_RECEIVER 0x05 + +/*! + * RegBitRate (bits/sec) + */ +#define RF_BITRATEMSB_1200_BPS 0x68 +#define RF_BITRATELSB_1200_BPS 0x2B +#define RF_BITRATEMSB_2400_BPS 0x34 +#define RF_BITRATELSB_2400_BPS 0x15 +#define RF_BITRATEMSB_4800_BPS 0x1A // Default +#define RF_BITRATELSB_4800_BPS 0x0B // Default +#define RF_BITRATEMSB_9600_BPS 0x0D +#define RF_BITRATELSB_9600_BPS 0x05 +#define RF_BITRATEMSB_15000_BPS 0x08 +#define RF_BITRATELSB_15000_BPS 0x55 +#define RF_BITRATEMSB_19200_BPS 0x06 +#define RF_BITRATELSB_19200_BPS 0x83 +#define RF_BITRATEMSB_38400_BPS 0x03 +#define RF_BITRATELSB_38400_BPS 0x41 +#define RF_BITRATEMSB_76800_BPS 0x01 +#define RF_BITRATELSB_76800_BPS 0xA1 +#define RF_BITRATEMSB_153600_BPS 0x00 +#define RF_BITRATELSB_153600_BPS 0xD0 +#define RF_BITRATEMSB_57600_BPS 0x02 +#define RF_BITRATELSB_57600_BPS 0x2C +#define RF_BITRATEMSB_115200_BPS 0x01 +#define RF_BITRATELSB_115200_BPS 0x16 +#define RF_BITRATEMSB_12500_BPS 0x0A +#define RF_BITRATELSB_12500_BPS 0x00 +#define RF_BITRATEMSB_25000_BPS 0x05 +#define RF_BITRATELSB_25000_BPS 0x00 +#define RF_BITRATEMSB_50000_BPS 0x02 +#define RF_BITRATELSB_50000_BPS 0x80 +#define RF_BITRATEMSB_100000_BPS 0x01 +#define RF_BITRATELSB_100000_BPS 0x40 +#define RF_BITRATEMSB_150000_BPS 0x00 +#define RF_BITRATELSB_150000_BPS 0xD5 +#define RF_BITRATEMSB_200000_BPS 0x00 +#define RF_BITRATELSB_200000_BPS 0xA0 +#define RF_BITRATEMSB_250000_BPS 0x00 +#define RF_BITRATELSB_250000_BPS 0x80 +#define RF_BITRATEMSB_32768_BPS 0x03 +#define RF_BITRATELSB_32768_BPS 0xD1 + +/*! + * RegFdev (Hz) + */ +#define RF_FDEVMSB_2000_HZ 0x00 +#define RF_FDEVLSB_2000_HZ 0x21 +#define RF_FDEVMSB_5000_HZ 0x00 // Default +#define RF_FDEVLSB_5000_HZ 0x52 // Default +#define RF_FDEVMSB_10000_HZ 0x00 +#define RF_FDEVLSB_10000_HZ 0xA4 +#define RF_FDEVMSB_15000_HZ 0x00 +#define RF_FDEVLSB_15000_HZ 0xF6 +#define RF_FDEVMSB_20000_HZ 0x01 +#define RF_FDEVLSB_20000_HZ 0x48 +#define RF_FDEVMSB_25000_HZ 0x01 +#define RF_FDEVLSB_25000_HZ 0x9A +#define RF_FDEVMSB_30000_HZ 0x01 +#define RF_FDEVLSB_30000_HZ 0xEC +#define RF_FDEVMSB_35000_HZ 0x02 +#define RF_FDEVLSB_35000_HZ 0x3D +#define RF_FDEVMSB_40000_HZ 0x02 +#define RF_FDEVLSB_40000_HZ 0x8F +#define RF_FDEVMSB_45000_HZ 0x02 +#define RF_FDEVLSB_45000_HZ 0xE1 +#define RF_FDEVMSB_50000_HZ 0x03 +#define RF_FDEVLSB_50000_HZ 0x33 +#define RF_FDEVMSB_55000_HZ 0x03 +#define RF_FDEVLSB_55000_HZ 0x85 +#define RF_FDEVMSB_60000_HZ 0x03 +#define RF_FDEVLSB_60000_HZ 0xD7 +#define RF_FDEVMSB_65000_HZ 0x04 +#define RF_FDEVLSB_65000_HZ 0x29 +#define RF_FDEVMSB_70000_HZ 0x04 +#define RF_FDEVLSB_70000_HZ 0x7B +#define RF_FDEVMSB_75000_HZ 0x04 +#define RF_FDEVLSB_75000_HZ 0xCD +#define RF_FDEVMSB_80000_HZ 0x05 +#define RF_FDEVLSB_80000_HZ 0x1F +#define RF_FDEVMSB_85000_HZ 0x05 +#define RF_FDEVLSB_85000_HZ 0x71 +#define RF_FDEVMSB_90000_HZ 0x05 +#define RF_FDEVLSB_90000_HZ 0xC3 +#define RF_FDEVMSB_95000_HZ 0x06 +#define RF_FDEVLSB_95000_HZ 0x14 +#define RF_FDEVMSB_100000_HZ 0x06 +#define RF_FDEVLSB_100000_HZ 0x66 +#define RF_FDEVMSB_110000_HZ 0x07 +#define RF_FDEVLSB_110000_HZ 0x0A +#define RF_FDEVMSB_120000_HZ 0x07 +#define RF_FDEVLSB_120000_HZ 0xAE +#define RF_FDEVMSB_130000_HZ 0x08 +#define RF_FDEVLSB_130000_HZ 0x52 +#define RF_FDEVMSB_140000_HZ 0x08 +#define RF_FDEVLSB_140000_HZ 0xF6 +#define RF_FDEVMSB_150000_HZ 0x09 +#define RF_FDEVLSB_150000_HZ 0x9A +#define RF_FDEVMSB_160000_HZ 0x0A +#define RF_FDEVLSB_160000_HZ 0x3D +#define RF_FDEVMSB_170000_HZ 0x0A +#define RF_FDEVLSB_170000_HZ 0xE1 +#define RF_FDEVMSB_180000_HZ 0x0B +#define RF_FDEVLSB_180000_HZ 0x85 +#define RF_FDEVMSB_190000_HZ 0x0C +#define RF_FDEVLSB_190000_HZ 0x29 +#define RF_FDEVMSB_200000_HZ 0x0C +#define RF_FDEVLSB_200000_HZ 0xCD + +/*! + * RegFrf (MHz) + */ +#define RF_FRFMSB_863_MHZ 0xD7 +#define RF_FRFMID_863_MHZ 0xC0 +#define RF_FRFLSB_863_MHZ 0x00 +#define RF_FRFMSB_864_MHZ 0xD8 +#define RF_FRFMID_864_MHZ 0x00 +#define RF_FRFLSB_864_MHZ 0x00 +#define RF_FRFMSB_865_MHZ 0xD8 +#define RF_FRFMID_865_MHZ 0x40 +#define RF_FRFLSB_865_MHZ 0x00 +#define RF_FRFMSB_866_MHZ 0xD8 +#define RF_FRFMID_866_MHZ 0x80 +#define RF_FRFLSB_866_MHZ 0x00 +#define RF_FRFMSB_867_MHZ 0xD8 +#define RF_FRFMID_867_MHZ 0xC0 +#define RF_FRFLSB_867_MHZ 0x00 +#define RF_FRFMSB_868_MHZ 0xD9 +#define RF_FRFMID_868_MHZ 0x00 +#define RF_FRFLSB_868_MHZ 0x00 +#define RF_FRFMSB_869_MHZ 0xD9 +#define RF_FRFMID_869_MHZ 0x40 +#define RF_FRFLSB_869_MHZ 0x00 +#define RF_FRFMSB_870_MHZ 0xD9 +#define RF_FRFMID_870_MHZ 0x80 +#define RF_FRFLSB_870_MHZ 0x00 + +#define RF_FRFMSB_902_MHZ 0xE1 +#define RF_FRFMID_902_MHZ 0x80 +#define RF_FRFLSB_902_MHZ 0x00 +#define RF_FRFMSB_903_MHZ 0xE1 +#define RF_FRFMID_903_MHZ 0xC0 +#define RF_FRFLSB_903_MHZ 0x00 +#define RF_FRFMSB_904_MHZ 0xE2 +#define RF_FRFMID_904_MHZ 0x00 +#define RF_FRFLSB_904_MHZ 0x00 +#define RF_FRFMSB_905_MHZ 0xE2 +#define RF_FRFMID_905_MHZ 0x40 +#define RF_FRFLSB_905_MHZ 0x00 +#define RF_FRFMSB_906_MHZ 0xE2 +#define RF_FRFMID_906_MHZ 0x80 +#define RF_FRFLSB_906_MHZ 0x00 +#define RF_FRFMSB_907_MHZ 0xE2 +#define RF_FRFMID_907_MHZ 0xC0 +#define RF_FRFLSB_907_MHZ 0x00 +#define RF_FRFMSB_908_MHZ 0xE3 +#define RF_FRFMID_908_MHZ 0x00 +#define RF_FRFLSB_908_MHZ 0x00 +#define RF_FRFMSB_909_MHZ 0xE3 +#define RF_FRFMID_909_MHZ 0x40 +#define RF_FRFLSB_909_MHZ 0x00 +#define RF_FRFMSB_910_MHZ 0xE3 +#define RF_FRFMID_910_MHZ 0x80 +#define RF_FRFLSB_910_MHZ 0x00 +#define RF_FRFMSB_911_MHZ 0xE3 +#define RF_FRFMID_911_MHZ 0xC0 +#define RF_FRFLSB_911_MHZ 0x00 +#define RF_FRFMSB_912_MHZ 0xE4 +#define RF_FRFMID_912_MHZ 0x00 +#define RF_FRFLSB_912_MHZ 0x00 +#define RF_FRFMSB_913_MHZ 0xE4 +#define RF_FRFMID_913_MHZ 0x40 +#define RF_FRFLSB_913_MHZ 0x00 +#define RF_FRFMSB_914_MHZ 0xE4 +#define RF_FRFMID_914_MHZ 0x80 +#define RF_FRFLSB_914_MHZ 0x00 +#define RF_FRFMSB_915_MHZ 0xE4 // Default +#define RF_FRFMID_915_MHZ 0xC0 // Default +#define RF_FRFLSB_915_MHZ 0x00 // Default +#define RF_FRFMSB_916_MHZ 0xE5 +#define RF_FRFMID_916_MHZ 0x00 +#define RF_FRFLSB_916_MHZ 0x00 +#define RF_FRFMSB_917_MHZ 0xE5 +#define RF_FRFMID_917_MHZ 0x40 +#define RF_FRFLSB_917_MHZ 0x00 +#define RF_FRFMSB_918_MHZ 0xE5 +#define RF_FRFMID_918_MHZ 0x80 +#define RF_FRFLSB_918_MHZ 0x00 +#define RF_FRFMSB_919_MHZ 0xE5 +#define RF_FRFMID_919_MHZ 0xC0 +#define RF_FRFLSB_919_MHZ 0x00 +#define RF_FRFMSB_920_MHZ 0xE6 +#define RF_FRFMID_920_MHZ 0x00 +#define RF_FRFLSB_920_MHZ 0x00 +#define RF_FRFMSB_921_MHZ 0xE6 +#define RF_FRFMID_921_MHZ 0x40 +#define RF_FRFLSB_921_MHZ 0x00 +#define RF_FRFMSB_922_MHZ 0xE6 +#define RF_FRFMID_922_MHZ 0x80 +#define RF_FRFLSB_922_MHZ 0x00 +#define RF_FRFMSB_923_MHZ 0xE6 +#define RF_FRFMID_923_MHZ 0xC0 +#define RF_FRFLSB_923_MHZ 0x00 +#define RF_FRFMSB_924_MHZ 0xE7 +#define RF_FRFMID_924_MHZ 0x00 +#define RF_FRFLSB_924_MHZ 0x00 +#define RF_FRFMSB_925_MHZ 0xE7 +#define RF_FRFMID_925_MHZ 0x40 +#define RF_FRFLSB_925_MHZ 0x00 +#define RF_FRFMSB_926_MHZ 0xE7 +#define RF_FRFMID_926_MHZ 0x80 +#define RF_FRFLSB_926_MHZ 0x00 +#define RF_FRFMSB_927_MHZ 0xE7 +#define RF_FRFMID_927_MHZ 0xC0 +#define RF_FRFLSB_927_MHZ 0x00 +#define RF_FRFMSB_928_MHZ 0xE8 +#define RF_FRFMID_928_MHZ 0x00 +#define RF_FRFLSB_928_MHZ 0x00 + +/*! + * RegPaConfig + */ +#define RF_PACONFIG_PASELECT_MASK 0x7F +#define RF_PACONFIG_PASELECT_PABOOST 0x80 +#define RF_PACONFIG_PASELECT_RFO 0x00 // Default + +#define RF_PACONFIG_MAX_POWER_MASK 0x8F + +#define RF_PACONFIG_OUTPUTPOWER_MASK 0xF0 + +/*! + * RegPaRamp + */ +#define RF_PARAMP_MODULATIONSHAPING_MASK 0x9F +#define RF_PARAMP_MODULATIONSHAPING_00 0x00 // Default +#define RF_PARAMP_MODULATIONSHAPING_01 0x20 +#define RF_PARAMP_MODULATIONSHAPING_10 0x40 +#define RF_PARAMP_MODULATIONSHAPING_11 0x60 + +#define RF_PARAMP_LOWPNTXPLL_MASK 0xEF +#define RF_PARAMP_LOWPNTXPLL_OFF 0x10 +#define RF_PARAMP_LOWPNTXPLL_ON 0x00 // Default + +#define RF_PARAMP_MASK 0xF0 +#define RF_PARAMP_3400_US 0x00 +#define RF_PARAMP_2000_US 0x01 +#define RF_PARAMP_1000_US 0x02 +#define RF_PARAMP_0500_US 0x03 +#define RF_PARAMP_0250_US 0x04 +#define RF_PARAMP_0125_US 0x05 +#define RF_PARAMP_0100_US 0x06 +#define RF_PARAMP_0062_US 0x07 +#define RF_PARAMP_0050_US 0x08 +#define RF_PARAMP_0040_US 0x09 // Default +#define RF_PARAMP_0031_US 0x0A +#define RF_PARAMP_0025_US 0x0B +#define RF_PARAMP_0020_US 0x0C +#define RF_PARAMP_0015_US 0x0D +#define RF_PARAMP_0012_US 0x0E +#define RF_PARAMP_0010_US 0x0F + +/*! + * RegOcp + */ +#define RF_OCP_MASK 0xDF +#define RF_OCP_ON 0x20 // Default +#define RF_OCP_OFF 0x00 + +#define RF_OCP_TRIM_MASK 0xE0 +#define RF_OCP_TRIM_045_MA 0x00 +#define RF_OCP_TRIM_050_MA 0x01 +#define RF_OCP_TRIM_055_MA 0x02 +#define RF_OCP_TRIM_060_MA 0x03 +#define RF_OCP_TRIM_065_MA 0x04 +#define RF_OCP_TRIM_070_MA 0x05 +#define RF_OCP_TRIM_075_MA 0x06 +#define RF_OCP_TRIM_080_MA 0x07 +#define RF_OCP_TRIM_085_MA 0x08 +#define RF_OCP_TRIM_090_MA 0x09 +#define RF_OCP_TRIM_095_MA 0x0A +#define RF_OCP_TRIM_100_MA 0x0B // Default +#define RF_OCP_TRIM_105_MA 0x0C +#define RF_OCP_TRIM_110_MA 0x0D +#define RF_OCP_TRIM_115_MA 0x0E +#define RF_OCP_TRIM_120_MA 0x0F +#define RF_OCP_TRIM_130_MA 0x10 +#define RF_OCP_TRIM_140_MA 0x11 +#define RF_OCP_TRIM_150_MA 0x12 +#define RF_OCP_TRIM_160_MA 0x13 +#define RF_OCP_TRIM_170_MA 0x14 +#define RF_OCP_TRIM_180_MA 0x15 +#define RF_OCP_TRIM_190_MA 0x16 +#define RF_OCP_TRIM_200_MA 0x17 +#define RF_OCP_TRIM_210_MA 0x18 +#define RF_OCP_TRIM_220_MA 0x19 +#define RF_OCP_TRIM_230_MA 0x1A +#define RF_OCP_TRIM_240_MA 0x1B + +/*! + * RegLna + */ +#define RF_LNA_GAIN_MASK 0x1F +#define RF_LNA_GAIN_G1 0x20 // Default +#define RF_LNA_GAIN_G2 0x40 +#define RF_LNA_GAIN_G3 0x60 +#define RF_LNA_GAIN_G4 0x80 +#define RF_LNA_GAIN_G5 0xA0 +#define RF_LNA_GAIN_G6 0xC0 + +#define RF_LNA_BOOST_MASK 0xFC +#define RF_LNA_BOOST_OFF 0x00 // Default +#define RF_LNA_BOOST_ON 0x03 + +/*! + * RegRxConfig + */ +#define RF_RXCONFIG_RESTARTRXONCOLLISION_MASK 0x7F +#define RF_RXCONFIG_RESTARTRXONCOLLISION_ON 0x80 +#define RF_RXCONFIG_RESTARTRXONCOLLISION_OFF 0x00 // Default + +#define RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK 0x40 // Write only + +#define RF_RXCONFIG_RESTARTRXWITHPLLLOCK 0x20 // Write only + +#define RF_RXCONFIG_AFCAUTO_MASK 0xEF +#define RF_RXCONFIG_AFCAUTO_ON 0x10 +#define RF_RXCONFIG_AFCAUTO_OFF 0x00 // Default + +#define RF_RXCONFIG_AGCAUTO_MASK 0xF7 +#define RF_RXCONFIG_AGCAUTO_ON 0x08 // Default +#define RF_RXCONFIG_AGCAUTO_OFF 0x00 + +#define RF_RXCONFIG_RXTRIGER_MASK 0xF8 +#define RF_RXCONFIG_RXTRIGER_OFF 0x00 +#define RF_RXCONFIG_RXTRIGER_RSSI 0x01 +#define RF_RXCONFIG_RXTRIGER_PREAMBLEDETECT 0x06 // Default +#define RF_RXCONFIG_RXTRIGER_RSSI_PREAMBLEDETECT 0x07 + +/*! + * RegRssiConfig + */ +#define RF_RSSICONFIG_OFFSET_MASK 0x07 +#define RF_RSSICONFIG_OFFSET_P_00_DB 0x00 // Default +#define RF_RSSICONFIG_OFFSET_P_01_DB 0x08 +#define RF_RSSICONFIG_OFFSET_P_02_DB 0x10 +#define RF_RSSICONFIG_OFFSET_P_03_DB 0x18 +#define RF_RSSICONFIG_OFFSET_P_04_DB 0x20 +#define RF_RSSICONFIG_OFFSET_P_05_DB 0x28 +#define RF_RSSICONFIG_OFFSET_P_06_DB 0x30 +#define RF_RSSICONFIG_OFFSET_P_07_DB 0x38 +#define RF_RSSICONFIG_OFFSET_P_08_DB 0x40 +#define RF_RSSICONFIG_OFFSET_P_09_DB 0x48 +#define RF_RSSICONFIG_OFFSET_P_10_DB 0x50 +#define RF_RSSICONFIG_OFFSET_P_11_DB 0x58 +#define RF_RSSICONFIG_OFFSET_P_12_DB 0x60 +#define RF_RSSICONFIG_OFFSET_P_13_DB 0x68 +#define RF_RSSICONFIG_OFFSET_P_14_DB 0x70 +#define RF_RSSICONFIG_OFFSET_P_15_DB 0x78 +#define RF_RSSICONFIG_OFFSET_M_16_DB 0x80 +#define RF_RSSICONFIG_OFFSET_M_15_DB 0x88 +#define RF_RSSICONFIG_OFFSET_M_14_DB 0x90 +#define RF_RSSICONFIG_OFFSET_M_13_DB 0x98 +#define RF_RSSICONFIG_OFFSET_M_12_DB 0xA0 +#define RF_RSSICONFIG_OFFSET_M_11_DB 0xA8 +#define RF_RSSICONFIG_OFFSET_M_10_DB 0xB0 +#define RF_RSSICONFIG_OFFSET_M_09_DB 0xB8 +#define RF_RSSICONFIG_OFFSET_M_08_DB 0xC0 +#define RF_RSSICONFIG_OFFSET_M_07_DB 0xC8 +#define RF_RSSICONFIG_OFFSET_M_06_DB 0xD0 +#define RF_RSSICONFIG_OFFSET_M_05_DB 0xD8 +#define RF_RSSICONFIG_OFFSET_M_04_DB 0xE0 +#define RF_RSSICONFIG_OFFSET_M_03_DB 0xE8 +#define RF_RSSICONFIG_OFFSET_M_02_DB 0xF0 +#define RF_RSSICONFIG_OFFSET_M_01_DB 0xF8 + +#define RF_RSSICONFIG_SMOOTHING_MASK 0xF8 +#define RF_RSSICONFIG_SMOOTHING_2 0x00 +#define RF_RSSICONFIG_SMOOTHING_4 0x01 +#define RF_RSSICONFIG_SMOOTHING_8 0x02 // Default +#define RF_RSSICONFIG_SMOOTHING_16 0x03 +#define RF_RSSICONFIG_SMOOTHING_32 0x04 +#define RF_RSSICONFIG_SMOOTHING_64 0x05 +#define RF_RSSICONFIG_SMOOTHING_128 0x06 +#define RF_RSSICONFIG_SMOOTHING_256 0x07 + +/*! + * RegRssiCollision + */ +#define RF_RSSICOLISION_THRESHOLD 0x0A // Default + +/*! + * RegRssiThresh + */ +#define RF_RSSITHRESH_THRESHOLD 0xFF // Default + +/*! + * RegRssiValue (Read Only) + */ + +/*! + * RegRxBw + */ +#define RF_RXBW_MANT_MASK 0xE7 +#define RF_RXBW_MANT_16 0x00 +#define RF_RXBW_MANT_20 0x08 +#define RF_RXBW_MANT_24 0x10 // Default + +#define RF_RXBW_EXP_MASK 0xF8 +#define RF_RXBW_EXP_0 0x00 +#define RF_RXBW_EXP_1 0x01 +#define RF_RXBW_EXP_2 0x02 +#define RF_RXBW_EXP_3 0x03 +#define RF_RXBW_EXP_4 0x04 +#define RF_RXBW_EXP_5 0x05 // Default +#define RF_RXBW_EXP_6 0x06 +#define RF_RXBW_EXP_7 0x07 + +/*! + * RegAfcBw + */ +#define RF_AFCBW_MANTAFC_MASK 0xE7 +#define RF_AFCBW_MANTAFC_16 0x00 +#define RF_AFCBW_MANTAFC_20 0x08 // Default +#define RF_AFCBW_MANTAFC_24 0x10 + +#define RF_AFCBW_EXPAFC_MASK 0xF8 +#define RF_AFCBW_EXPAFC_0 0x00 +#define RF_AFCBW_EXPAFC_1 0x01 +#define RF_AFCBW_EXPAFC_2 0x02 +#define RF_AFCBW_EXPAFC_3 0x03 // Default +#define RF_AFCBW_EXPAFC_4 0x04 +#define RF_AFCBW_EXPAFC_5 0x05 +#define RF_AFCBW_EXPAFC_6 0x06 +#define RF_AFCBW_EXPAFC_7 0x07 + +/*! + * RegOokPeak + */ +#define RF_OOKPEAK_BITSYNC_MASK 0xDF // Default +#define RF_OOKPEAK_BITSYNC_ON 0x20 // Default +#define RF_OOKPEAK_BITSYNC_OFF 0x00 + +#define RF_OOKPEAK_OOKTHRESHTYPE_MASK 0xE7 +#define RF_OOKPEAK_OOKTHRESHTYPE_FIXED 0x00 +#define RF_OOKPEAK_OOKTHRESHTYPE_PEAK 0x08 // Default +#define RF_OOKPEAK_OOKTHRESHTYPE_AVERAGE 0x10 + +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_MASK 0xF8 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_0_5_DB 0x00 // Default +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_1_0_DB 0x01 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_1_5_DB 0x02 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_2_0_DB 0x03 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_3_0_DB 0x04 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_4_0_DB 0x05 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_5_0_DB 0x06 +#define RF_OOKPEAK_OOKPEAKTHRESHSTEP_6_0_DB 0x07 + +/*! + * RegOokFix + */ +#define RF_OOKFIX_OOKFIXEDTHRESHOLD 0x0C // Default + +/*! + * RegOokAvg + */ +#define RF_OOKAVG_OOKPEAKTHRESHDEC_MASK 0x1F +#define RF_OOKAVG_OOKPEAKTHRESHDEC_000 0x00 // Default +#define RF_OOKAVG_OOKPEAKTHRESHDEC_001 0x20 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_010 0x40 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_011 0x60 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_100 0x80 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_101 0xA0 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_110 0xC0 +#define RF_OOKAVG_OOKPEAKTHRESHDEC_111 0xE0 + +#define RF_OOKAVG_AVERAGEOFFSET_MASK 0xF3 +#define RF_OOKAVG_AVERAGEOFFSET_0_DB 0x00 // Default +#define RF_OOKAVG_AVERAGEOFFSET_2_DB 0x04 +#define RF_OOKAVG_AVERAGEOFFSET_4_DB 0x08 +#define RF_OOKAVG_AVERAGEOFFSET_6_DB 0x0C + +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_MASK 0xFC +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_00 0x00 +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_01 0x01 +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_10 0x02 // Default +#define RF_OOKAVG_OOKAVERAGETHRESHFILT_11 0x03 + +/*! + * RegAfcFei + */ +#define RF_AFCFEI_AGCSTART 0x10 + +#define RF_AFCFEI_AFCCLEAR 0x02 + +#define RF_AFCFEI_AFCAUTOCLEAR_MASK 0xFE +#define RF_AFCFEI_AFCAUTOCLEAR_ON 0x01 +#define RF_AFCFEI_AFCAUTOCLEAR_OFF 0x00 // Default + +/*! + * RegAfcMsb (Read Only) + */ + +/*! + * RegAfcLsb (Read Only) + */ + +/*! + * RegFeiMsb (Read Only) + */ + +/*! + * RegFeiLsb (Read Only) + */ + +/*! + * RegPreambleDetect + */ +#define RF_PREAMBLEDETECT_DETECTOR_MASK 0x7F +#define RF_PREAMBLEDETECT_DETECTOR_ON 0x80 // Default +#define RF_PREAMBLEDETECT_DETECTOR_OFF 0x00 + +#define RF_PREAMBLEDETECT_DETECTORSIZE_MASK 0x9F +#define RF_PREAMBLEDETECT_DETECTORSIZE_1 0x00 +#define RF_PREAMBLEDETECT_DETECTORSIZE_2 0x20 // Default +#define RF_PREAMBLEDETECT_DETECTORSIZE_3 0x40 +#define RF_PREAMBLEDETECT_DETECTORSIZE_4 0x60 + +#define RF_PREAMBLEDETECT_DETECTORTOL_MASK 0xE0 +#define RF_PREAMBLEDETECT_DETECTORTOL_0 0x00 +#define RF_PREAMBLEDETECT_DETECTORTOL_1 0x01 +#define RF_PREAMBLEDETECT_DETECTORTOL_2 0x02 +#define RF_PREAMBLEDETECT_DETECTORTOL_3 0x03 +#define RF_PREAMBLEDETECT_DETECTORTOL_4 0x04 +#define RF_PREAMBLEDETECT_DETECTORTOL_5 0x05 +#define RF_PREAMBLEDETECT_DETECTORTOL_6 0x06 +#define RF_PREAMBLEDETECT_DETECTORTOL_7 0x07 +#define RF_PREAMBLEDETECT_DETECTORTOL_8 0x08 +#define RF_PREAMBLEDETECT_DETECTORTOL_9 0x09 +#define RF_PREAMBLEDETECT_DETECTORTOL_10 0x0A // Default +#define RF_PREAMBLEDETECT_DETECTORTOL_11 0x0B +#define RF_PREAMBLEDETECT_DETECTORTOL_12 0x0C +#define RF_PREAMBLEDETECT_DETECTORTOL_13 0x0D +#define RF_PREAMBLEDETECT_DETECTORTOL_14 0x0E +#define RF_PREAMBLEDETECT_DETECTORTOL_15 0x0F +#define RF_PREAMBLEDETECT_DETECTORTOL_16 0x10 +#define RF_PREAMBLEDETECT_DETECTORTOL_17 0x11 +#define RF_PREAMBLEDETECT_DETECTORTOL_18 0x12 +#define RF_PREAMBLEDETECT_DETECTORTOL_19 0x13 +#define RF_PREAMBLEDETECT_DETECTORTOL_20 0x14 +#define RF_PREAMBLEDETECT_DETECTORTOL_21 0x15 +#define RF_PREAMBLEDETECT_DETECTORTOL_22 0x16 +#define RF_PREAMBLEDETECT_DETECTORTOL_23 0x17 +#define RF_PREAMBLEDETECT_DETECTORTOL_24 0x18 +#define RF_PREAMBLEDETECT_DETECTORTOL_25 0x19 +#define RF_PREAMBLEDETECT_DETECTORTOL_26 0x1A +#define RF_PREAMBLEDETECT_DETECTORTOL_27 0x1B +#define RF_PREAMBLEDETECT_DETECTORTOL_28 0x1C +#define RF_PREAMBLEDETECT_DETECTORTOL_29 0x1D +#define RF_PREAMBLEDETECT_DETECTORTOL_30 0x1E +#define RF_PREAMBLEDETECT_DETECTORTOL_31 0x1F + +/*! + * RegRxTimeout1 + */ +#define RF_RXTIMEOUT1_TIMEOUTRXRSSI 0x00 // Default + +/*! + * RegRxTimeout2 + */ +#define RF_RXTIMEOUT2_TIMEOUTRXPREAMBLE 0x00 // Default + +/*! + * RegRxTimeout3 + */ +#define RF_RXTIMEOUT3_TIMEOUTSIGNALSYNC 0x00 // Default + +/*! + * RegRxDelay + */ +#define RF_RXDELAY_INTERPACKETRXDELAY 0x00 // Default + +/*! + * RegOsc + */ +#define RF_OSC_RCCALSTART 0x08 + +#define RF_OSC_CLKOUT_MASK 0xF8 +#define RF_OSC_CLKOUT_32_MHZ 0x00 +#define RF_OSC_CLKOUT_16_MHZ 0x01 +#define RF_OSC_CLKOUT_8_MHZ 0x02 +#define RF_OSC_CLKOUT_4_MHZ 0x03 +#define RF_OSC_CLKOUT_2_MHZ 0x04 +#define RF_OSC_CLKOUT_1_MHZ 0x05 // Default +#define RF_OSC_CLKOUT_RC 0x06 +#define RF_OSC_CLKOUT_OFF 0x07 + +/*! + * RegPreambleMsb/RegPreambleLsb + */ +#define RF_PREAMBLEMSB_SIZE 0x00 // Default +#define RF_PREAMBLELSB_SIZE 0x03 // Default + +/*! + * RegSyncConfig + */ +#define RF_SYNCCONFIG_AUTORESTARTRXMODE_MASK 0x3F +#define RF_SYNCCONFIG_AUTORESTARTRXMODE_WAITPLL_ON 0x80 // Default +#define RF_SYNCCONFIG_AUTORESTARTRXMODE_WAITPLL_OFF 0x40 +#define RF_SYNCCONFIG_AUTORESTARTRXMODE_OFF 0x00 + + +#define RF_SYNCCONFIG_PREAMBLEPOLARITY_MASK 0xDF +#define RF_SYNCCONFIG_PREAMBLEPOLARITY_55 0x20 +#define RF_SYNCCONFIG_PREAMBLEPOLARITY_AA 0x00 // Default + +#define RF_SYNCCONFIG_SYNC_MASK 0xEF +#define RF_SYNCCONFIG_SYNC_ON 0x10 // Default +#define RF_SYNCCONFIG_SYNC_OFF 0x00 + + +#define RF_SYNCCONFIG_SYNCSIZE_MASK 0xF8 +#define RF_SYNCCONFIG_SYNCSIZE_1 0x00 +#define RF_SYNCCONFIG_SYNCSIZE_2 0x01 +#define RF_SYNCCONFIG_SYNCSIZE_3 0x02 +#define RF_SYNCCONFIG_SYNCSIZE_4 0x03 // Default +#define RF_SYNCCONFIG_SYNCSIZE_5 0x04 +#define RF_SYNCCONFIG_SYNCSIZE_6 0x05 +#define RF_SYNCCONFIG_SYNCSIZE_7 0x06 +#define RF_SYNCCONFIG_SYNCSIZE_8 0x07 + +/*! + * RegSyncValue1-8 + */ +#define RF_SYNCVALUE1_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE2_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE3_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE4_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE5_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE6_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE7_SYNCVALUE 0x01 // Default +#define RF_SYNCVALUE8_SYNCVALUE 0x01 // Default + +/*! + * RegPacketConfig1 + */ +#define RF_PACKETCONFIG1_PACKETFORMAT_MASK 0x7F +#define RF_PACKETCONFIG1_PACKETFORMAT_FIXED 0x00 +#define RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE 0x80 // Default + +#define RF_PACKETCONFIG1_DCFREE_MASK 0x9F +#define RF_PACKETCONFIG1_DCFREE_OFF 0x00 // Default +#define RF_PACKETCONFIG1_DCFREE_MANCHESTER 0x20 +#define RF_PACKETCONFIG1_DCFREE_WHITENING 0x40 + +#define RF_PACKETCONFIG1_CRC_MASK 0xEF +#define RF_PACKETCONFIG1_CRC_ON 0x10 // Default +#define RF_PACKETCONFIG1_CRC_OFF 0x00 + +#define RF_PACKETCONFIG1_CRCAUTOCLEAR_MASK 0xF7 +#define RF_PACKETCONFIG1_CRCAUTOCLEAR_ON 0x00 // Default +#define RF_PACKETCONFIG1_CRCAUTOCLEAR_OFF 0x08 + +#define RF_PACKETCONFIG1_ADDRSFILTERING_MASK 0xF9 +#define RF_PACKETCONFIG1_ADDRSFILTERING_OFF 0x00 // Default +#define RF_PACKETCONFIG1_ADDRSFILTERING_NODE 0x02 +#define RF_PACKETCONFIG1_ADDRSFILTERING_NODEBROADCAST 0x04 + +#define RF_PACKETCONFIG1_CRCWHITENINGTYPE_MASK 0xFE +#define RF_PACKETCONFIG1_CRCWHITENINGTYPE_CCITT 0x00 // Default +#define RF_PACKETCONFIG1_CRCWHITENINGTYPE_IBM 0x01 + +/*! + * RegPacketConfig2 + */ + +#define RF_PACKETCONFIG2_WMBUS_CRC_ENABLE_MASK 0x7F +#define RF_PACKETCONFIG2_WMBUS_CRC_ENABLE 0x80 +#define RF_PACKETCONFIG2_WMBUS_CRC_DISABLE 0x00 // Default + +#define RF_PACKETCONFIG2_DATAMODE_MASK 0xBF +#define RF_PACKETCONFIG2_DATAMODE_CONTINUOUS 0x00 +#define RF_PACKETCONFIG2_DATAMODE_PACKET 0x40 // Default + +#define RF_PACKETCONFIG2_IOHOME_MASK 0xDF +#define RF_PACKETCONFIG2_IOHOME_ON 0x20 +#define RF_PACKETCONFIG2_IOHOME_OFF 0x00 // Default + +#define RF_PACKETCONFIG2_BEACON_MASK 0xF7 +#define RF_PACKETCONFIG2_BEACON_ON 0x08 +#define RF_PACKETCONFIG2_BEACON_OFF 0x00 // Default + +#define RF_PACKETCONFIG2_PAYLOADLENGTH_MSB_MASK 0xF8 + +/*! + * RegPayloadLength + */ +#define RF_PAYLOADLENGTH_LENGTH 0x40 // Default + +/*! + * RegNodeAdrs + */ +#define RF_NODEADDRESS_ADDRESS 0x00 + +/*! + * RegBroadcastAdrs + */ +#define RF_BROADCASTADDRESS_ADDRESS 0x00 + +/*! + * RegFifoThresh + */ +#define RF_FIFOTHRESH_TXSTARTCONDITION_MASK 0x7F +#define RF_FIFOTHRESH_TXSTARTCONDITION_FIFOTHRESH 0x00 // Default +#define RF_FIFOTHRESH_TXSTARTCONDITION_FIFONOTEMPTY 0x80 + +#define RF_FIFOTHRESH_FIFOTHRESHOLD_MASK 0xC0 +#define RF_FIFOTHRESH_FIFOTHRESHOLD_THRESHOLD 0x0F // Default + +/*! + * RegSeqConfig1 + */ +#define RF_SEQCONFIG1_SEQUENCER_START 0x80 + +#define RF_SEQCONFIG1_SEQUENCER_STOP 0x40 + +#define RF_SEQCONFIG1_IDLEMODE_MASK 0xDF +#define RF_SEQCONFIG1_IDLEMODE_SLEEP 0x20 +#define RF_SEQCONFIG1_IDLEMODE_STANDBY 0x00 // Default + +#define RF_SEQCONFIG1_FROMSTART_MASK 0xE7 +#define RF_SEQCONFIG1_FROMSTART_TOLPS 0x00 // Default +#define RF_SEQCONFIG1_FROMSTART_TORX 0x08 +#define RF_SEQCONFIG1_FROMSTART_TOTX 0x10 +#define RF_SEQCONFIG1_FROMSTART_TOTX_ONFIFOLEVEL 0x18 + +#define RF_SEQCONFIG1_LPS_MASK 0xFB +#define RF_SEQCONFIG1_LPS_SEQUENCER_OFF 0x00 // Default +#define RF_SEQCONFIG1_LPS_IDLE 0x04 + +#define RF_SEQCONFIG1_FROMIDLE_MASK 0xFD +#define RF_SEQCONFIG1_FROMIDLE_TOTX 0x00 // Default +#define RF_SEQCONFIG1_FROMIDLE_TORX 0x02 + +#define RF_SEQCONFIG1_FROMTX_MASK 0xFE +#define RF_SEQCONFIG1_FROMTX_TOLPS 0x00 // Default +#define RF_SEQCONFIG1_FROMTX_TORX 0x01 + +/*! + * RegSeqConfig2 + */ +#define RF_SEQCONFIG2_FROMRX_MASK 0x1F +#define RF_SEQCONFIG2_FROMRX_TOUNUSED_000 0x00 // Default +#define RF_SEQCONFIG2_FROMRX_TORXPKT_ONPLDRDY 0x20 +#define RF_SEQCONFIG2_FROMRX_TOLPS_ONPLDRDY 0x40 +#define RF_SEQCONFIG2_FROMRX_TORXPKT_ONCRCOK 0x60 +#define RF_SEQCONFIG2_FROMRX_TOSEQUENCEROFF_ONRSSI 0x80 +#define RF_SEQCONFIG2_FROMRX_TOSEQUENCEROFF_ONSYNC 0xA0 +#define RF_SEQCONFIG2_FROMRX_TOSEQUENCEROFF_ONPREAMBLE 0xC0 +#define RF_SEQCONFIG2_FROMRX_TOUNUSED_111 0xE0 + +#define RF_SEQCONFIG2_FROMRXTIMEOUT_MASK 0xE7 +#define RF_SEQCONFIG2_FROMRXTIMEOUT_TORXRESTART 0x00 // Default +#define RF_SEQCONFIG2_FROMRXTIMEOUT_TOTX 0x08 +#define RF_SEQCONFIG2_FROMRXTIMEOUT_TOLPS 0x10 +#define RF_SEQCONFIG2_FROMRXTIMEOUT_TOSEQUENCEROFF 0x18 + +#define RF_SEQCONFIG2_FROMRXPKT_MASK 0xF8 +#define RF_SEQCONFIG2_FROMRXPKT_TOSEQUENCEROFF 0x00 // Default +#define RF_SEQCONFIG2_FROMRXPKT_TOTX_ONFIFOEMPTY 0x01 +#define RF_SEQCONFIG2_FROMRXPKT_TOLPS 0x02 +#define RF_SEQCONFIG2_FROMRXPKT_TOSYNTHESIZERRX 0x03 +#define RF_SEQCONFIG2_FROMRXPKT_TORX 0x04 + +/*! + * RegTimerResol + */ +#define RF_TIMERRESOL_TIMER1RESOL_MASK 0xF3 +#define RF_TIMERRESOL_TIMER1RESOL_OFF 0x00 // Default +#define RF_TIMERRESOL_TIMER1RESOL_000064_US 0x04 +#define RF_TIMERRESOL_TIMER1RESOL_004100_US 0x08 +#define RF_TIMERRESOL_TIMER1RESOL_262000_US 0x0C + +#define RF_TIMERRESOL_TIMER2RESOL_MASK 0xFC +#define RF_TIMERRESOL_TIMER2RESOL_OFF 0x00 // Default +#define RF_TIMERRESOL_TIMER2RESOL_000064_US 0x01 +#define RF_TIMERRESOL_TIMER2RESOL_004100_US 0x02 +#define RF_TIMERRESOL_TIMER2RESOL_262000_US 0x03 + +/*! + * RegTimer1Coef + */ +#define RF_TIMER1COEF_TIMER1COEFFICIENT 0xF5 // Default + +/*! + * RegTimer2Coef + */ +#define RF_TIMER2COEF_TIMER2COEFFICIENT 0x20 // Default + +/*! + * RegImageCal + */ +#define RF_IMAGECAL_AUTOIMAGECAL_MASK 0x7F +#define RF_IMAGECAL_AUTOIMAGECAL_ON 0x80 +#define RF_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default + +#define RF_IMAGECAL_IMAGECAL_MASK 0xBF +#define RF_IMAGECAL_IMAGECAL_START 0x40 + +#define RF_IMAGECAL_IMAGECAL_RUNNING 0x20 +#define RF_IMAGECAL_IMAGECAL_DONE 0x00 // Default + +#define RF_IMAGECAL_TEMPCHANGE_HIGHER 0x08 +#define RF_IMAGECAL_TEMPCHANGE_LOWER 0x00 + +#define RF_IMAGECAL_TEMPTHRESHOLD_MASK 0xF9 +#define RF_IMAGECAL_TEMPTHRESHOLD_05 0x00 +#define RF_IMAGECAL_TEMPTHRESHOLD_10 0x02 // Default +#define RF_IMAGECAL_TEMPTHRESHOLD_15 0x04 +#define RF_IMAGECAL_TEMPTHRESHOLD_20 0x06 + +#define RF_IMAGECAL_TEMPMONITOR_MASK 0xFE +#define RF_IMAGECAL_TEMPMONITOR_ON 0x00 // Default +#define RF_IMAGECAL_TEMPMONITOR_OFF 0x01 + +/*! + * RegTemp (Read Only) + */ + +/*! + * RegLowBat + */ +#define RF_LOWBAT_MASK 0xF7 +#define RF_LOWBAT_ON 0x08 +#define RF_LOWBAT_OFF 0x00 // Default + +#define RF_LOWBAT_TRIM_MASK 0xF8 +#define RF_LOWBAT_TRIM_1695 0x00 +#define RF_LOWBAT_TRIM_1764 0x01 +#define RF_LOWBAT_TRIM_1835 0x02 // Default +#define RF_LOWBAT_TRIM_1905 0x03 +#define RF_LOWBAT_TRIM_1976 0x04 +#define RF_LOWBAT_TRIM_2045 0x05 +#define RF_LOWBAT_TRIM_2116 0x06 +#define RF_LOWBAT_TRIM_2185 0x07 + +/*! + * RegIrqFlags1 + */ +#define RF_IRQFLAGS1_MODEREADY 0x80 + +#define RF_IRQFLAGS1_RXREADY 0x40 + +#define RF_IRQFLAGS1_TXREADY 0x20 + +#define RF_IRQFLAGS1_PLLLOCK 0x10 + +#define RF_IRQFLAGS1_RSSI 0x08 + +#define RF_IRQFLAGS1_TIMEOUT 0x04 + +#define RF_IRQFLAGS1_PREAMBLEDETECT 0x02 + +#define RF_IRQFLAGS1_SYNCADDRESSMATCH 0x01 + +/*! + * RegIrqFlags2 + */ +#define RF_IRQFLAGS2_FIFOFULL 0x80 + +#define RF_IRQFLAGS2_FIFOEMPTY 0x40 + +#define RF_IRQFLAGS2_FIFOLEVEL 0x20 + +#define RF_IRQFLAGS2_FIFOOVERRUN 0x10 + +#define RF_IRQFLAGS2_PACKETSENT 0x08 + +#define RF_IRQFLAGS2_PAYLOADREADY 0x04 + +#define RF_IRQFLAGS2_CRCOK 0x02 + +#define RF_IRQFLAGS2_LOWBAT 0x01 + +/*! + * RegDioMapping1 + */ +#define RF_DIOMAPPING1_DIO0_MASK 0x3F +#define RF_DIOMAPPING1_DIO0_00 0x00 // Default +#define RF_DIOMAPPING1_DIO0_01 0x40 +#define RF_DIOMAPPING1_DIO0_10 0x80 +#define RF_DIOMAPPING1_DIO0_11 0xC0 + +#define RF_DIOMAPPING1_DIO1_MASK 0xCF +#define RF_DIOMAPPING1_DIO1_00 0x00 // Default +#define RF_DIOMAPPING1_DIO1_01 0x10 +#define RF_DIOMAPPING1_DIO1_10 0x20 +#define RF_DIOMAPPING1_DIO1_11 0x30 + +#define RF_DIOMAPPING1_DIO2_MASK 0xF3 +#define RF_DIOMAPPING1_DIO2_00 0x00 // Default +#define RF_DIOMAPPING1_DIO2_01 0x04 +#define RF_DIOMAPPING1_DIO2_10 0x08 +#define RF_DIOMAPPING1_DIO2_11 0x0C + +#define RF_DIOMAPPING1_DIO3_MASK 0xFC +#define RF_DIOMAPPING1_DIO3_00 0x00 // Default +#define RF_DIOMAPPING1_DIO3_01 0x01 +#define RF_DIOMAPPING1_DIO3_10 0x02 +#define RF_DIOMAPPING1_DIO3_11 0x03 + +/*! + * RegDioMapping2 + */ +#define RF_DIOMAPPING2_DIO4_MASK 0x3F +#define RF_DIOMAPPING2_DIO4_00 0x00 // Default +#define RF_DIOMAPPING2_DIO4_01 0x40 +#define RF_DIOMAPPING2_DIO4_10 0x80 +#define RF_DIOMAPPING2_DIO4_11 0xC0 + +#define RF_DIOMAPPING2_DIO5_MASK 0xCF +#define RF_DIOMAPPING2_DIO5_00 0x00 // Default +#define RF_DIOMAPPING2_DIO5_01 0x10 +#define RF_DIOMAPPING2_DIO5_10 0x20 +#define RF_DIOMAPPING2_DIO5_11 0x30 + +#define RF_DIOMAPPING2_MAP_MASK 0xFE +#define RF_DIOMAPPING2_MAP_PREAMBLEDETECT 0x01 +#define RF_DIOMAPPING2_MAP_RSSI 0x00 // Default + +/*! + * RegVersion (Read Only) + */ + +/*! + * RegPllHop + */ +#define RF_PLLHOP_FASTHOP_MASK 0x7F +#define RF_PLLHOP_FASTHOP_ON 0x80 +#define RF_PLLHOP_FASTHOP_OFF 0x00 // Default + +/*! + * RegTcxo + */ +#define RF_TCXO_TCXOINPUT_MASK 0xEF +#define RF_TCXO_TCXOINPUT_ON 0x10 +#define RF_TCXO_TCXOINPUT_OFF 0x00 // Default + +/*! + * RegPaDac + */ +#define RF_PADAC_20DBM_MASK 0xF8 +#define RF_PADAC_20DBM_ON 0x07 +#define RF_PADAC_20DBM_OFF 0x04 // Default + +/*! + * RegFormerTemp + */ + +/*! + * RegBitrateFrac + */ +#define RF_BITRATEFRAC_MASK 0xF0 + +/*! + * RegAgcRef + */ + +/*! + * RegAgcThresh1 + */ + +/*! + * RegAgcThresh2 + */ + +/*! + * RegAgcThresh3 + */ + +/*! + * RegPll + */ +#define RF_PLL_BANDWIDTH_MASK 0x3F +#define RF_PLL_BANDWIDTH_75 0x00 +#define RF_PLL_BANDWIDTH_150 0x40 +#define RF_PLL_BANDWIDTH_225 0x80 +#define RF_PLL_BANDWIDTH_300 0xC0 // Default + +#endif // __SX1276_REGS_FSK_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/registers/sx1276Regs-LoRa.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,571 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C) 2014 Semtech + +Description: SX1276 LoRa modem registers and bits definitions + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainer: Miguel Luis and Gregory Cristian +*/ +#ifndef __SX1276_REGS_LORA_H__ +#define __SX1276_REGS_LORA_H__ + +/*! + * ============================================================================ + * SX1276 Internal registers Address + * ============================================================================ + */ +#define REG_LR_FIFO 0x00 +// Common settings +#define REG_LR_OPMODE 0x01 +#define REG_LR_FRFMSB 0x06 +#define REG_LR_FRFMID 0x07 +#define REG_LR_FRFLSB 0x08 +// Tx settings +#define REG_LR_PACONFIG 0x09 +#define REG_LR_PARAMP 0x0A +#define REG_LR_OCP 0x0B +// Rx settings +#define REG_LR_LNA 0x0C +// LoRa registers +#define REG_LR_FIFOADDRPTR 0x0D +#define REG_LR_FIFOTXBASEADDR 0x0E +#define REG_LR_FIFORXBASEADDR 0x0F +#define REG_LR_FIFORXCURRENTADDR 0x10 +#define REG_LR_IRQFLAGSMASK 0x11 +#define REG_LR_IRQFLAGS 0x12 +#define REG_LR_RXNBBYTES 0x13 +#define REG_LR_RXHEADERCNTVALUEMSB 0x14 +#define REG_LR_RXHEADERCNTVALUELSB 0x15 +#define REG_LR_RXPACKETCNTVALUEMSB 0x16 +#define REG_LR_RXPACKETCNTVALUELSB 0x17 +#define REG_LR_MODEMSTAT 0x18 +#define REG_LR_PKTSNRVALUE 0x19 +#define REG_LR_PKTRSSIVALUE 0x1A +#define REG_LR_RSSIVALUE 0x1B +#define REG_LR_HOPCHANNEL 0x1C +#define REG_LR_MODEMCONFIG1 0x1D +#define REG_LR_MODEMCONFIG2 0x1E +#define REG_LR_SYMBTIMEOUTLSB 0x1F +#define REG_LR_PREAMBLEMSB 0x20 +#define REG_LR_PREAMBLELSB 0x21 +#define REG_LR_PAYLOADLENGTH 0x22 +#define REG_LR_PAYLOADMAXLENGTH 0x23 +#define REG_LR_HOPPERIOD 0x24 +#define REG_LR_FIFORXBYTEADDR 0x25 +#define REG_LR_MODEMCONFIG3 0x26 +#define REG_LR_FEIMSB 0x28 +#define REG_LR_FEIMID 0x29 +#define REG_LR_FEILSB 0x2A +#define REG_LR_RSSIWIDEBAND 0x2C +#define REG_LR_TEST2F 0x2F +#define REG_LR_TEST30 0x30 +#define REG_LR_DETECTOPTIMIZE 0x31 +#define REG_LR_INVERTIQ 0x33 +#define REG_LR_TEST36 0x36 +#define REG_LR_DETECTIONTHRESHOLD 0x37 +#define REG_LR_SYNCWORD 0x39 +#define REG_LR_TEST3A 0x3A +#define REG_LR_INVERTIQ2 0x3B + +// end of documented register in datasheet +// I/O settings +#define REG_LR_DIOMAPPING1 0x40 +#define REG_LR_DIOMAPPING2 0x41 +// Version +#define REG_LR_VERSION 0x42 +// Additional settings +#define REG_LR_PLLHOP 0x44 +#define REG_LR_TCXO 0x4B +#define REG_LR_PADAC 0x4D +#define REG_LR_FORMERTEMP 0x5B +#define REG_LR_BITRATEFRAC 0x5D +#define REG_LR_AGCREF 0x61 +#define REG_LR_AGCTHRESH1 0x62 +#define REG_LR_AGCTHRESH2 0x63 +#define REG_LR_AGCTHRESH3 0x64 +#define REG_LR_PLL 0x70 + +/*! + * ============================================================================ + * SX1276 LoRa bits control definition + * ============================================================================ + */ + +/*! + * RegFifo + */ + +/*! + * RegOpMode + */ +#define RFLR_OPMODE_LONGRANGEMODE_MASK 0x7F +#define RFLR_OPMODE_LONGRANGEMODE_OFF 0x00 // Default +#define RFLR_OPMODE_LONGRANGEMODE_ON 0x80 + +#define RFLR_OPMODE_ACCESSSHAREDREG_MASK 0xBF +#define RFLR_OPMODE_ACCESSSHAREDREG_ENABLE 0x40 +#define RFLR_OPMODE_ACCESSSHAREDREG_DISABLE 0x00 // Default + +#define RFLR_OPMODE_FREQMODE_ACCESS_MASK 0xF7 +#define RFLR_OPMODE_FREQMODE_ACCESS_LF 0x08 // Default +#define RFLR_OPMODE_FREQMODE_ACCESS_HF 0x00 + +#define RFLR_OPMODE_MASK 0xF8 +#define RFLR_OPMODE_SLEEP 0x00 +#define RFLR_OPMODE_STANDBY 0x01 // Default +#define RFLR_OPMODE_SYNTHESIZER_TX 0x02 +#define RFLR_OPMODE_TRANSMITTER 0x03 +#define RFLR_OPMODE_SYNTHESIZER_RX 0x04 +#define RFLR_OPMODE_RECEIVER 0x05 +// LoRa specific modes +#define RFLR_OPMODE_RECEIVER_SINGLE 0x06 +#define RFLR_OPMODE_CAD 0x07 + +/*! + * RegFrf (MHz) + */ +#define RFLR_FRFMSB_434_MHZ 0x6C // Default +#define RFLR_FRFMID_434_MHZ 0x80 // Default +#define RFLR_FRFLSB_434_MHZ 0x00 // Default + +/*! + * RegPaConfig + */ +#define RFLR_PACONFIG_PASELECT_MASK 0x7F +#define RFLR_PACONFIG_PASELECT_PABOOST 0x80 +#define RFLR_PACONFIG_PASELECT_RFO 0x00 // Default + +#define RFLR_PACONFIG_MAX_POWER_MASK 0x8F + +#define RFLR_PACONFIG_OUTPUTPOWER_MASK 0xF0 + +/*! + * RegPaRamp + */ +#define RFLR_PARAMP_TXBANDFORCE_MASK 0xEF +#define RFLR_PARAMP_TXBANDFORCE_BAND_SEL 0x10 +#define RFLR_PARAMP_TXBANDFORCE_AUTO 0x00 // Default + +#define RFLR_PARAMP_MASK 0xF0 +#define RFLR_PARAMP_3400_US 0x00 +#define RFLR_PARAMP_2000_US 0x01 +#define RFLR_PARAMP_1000_US 0x02 +#define RFLR_PARAMP_0500_US 0x03 +#define RFLR_PARAMP_0250_US 0x04 +#define RFLR_PARAMP_0125_US 0x05 +#define RFLR_PARAMP_0100_US 0x06 +#define RFLR_PARAMP_0062_US 0x07 +#define RFLR_PARAMP_0050_US 0x08 +#define RFLR_PARAMP_0040_US 0x09 // Default +#define RFLR_PARAMP_0031_US 0x0A +#define RFLR_PARAMP_0025_US 0x0B +#define RFLR_PARAMP_0020_US 0x0C +#define RFLR_PARAMP_0015_US 0x0D +#define RFLR_PARAMP_0012_US 0x0E +#define RFLR_PARAMP_0010_US 0x0F + +/*! + * RegOcp + */ +#define RFLR_OCP_MASK 0xDF +#define RFLR_OCP_ON 0x20 // Default +#define RFLR_OCP_OFF 0x00 + +#define RFLR_OCP_TRIM_MASK 0xE0 +#define RFLR_OCP_TRIM_045_MA 0x00 +#define RFLR_OCP_TRIM_050_MA 0x01 +#define RFLR_OCP_TRIM_055_MA 0x02 +#define RFLR_OCP_TRIM_060_MA 0x03 +#define RFLR_OCP_TRIM_065_MA 0x04 +#define RFLR_OCP_TRIM_070_MA 0x05 +#define RFLR_OCP_TRIM_075_MA 0x06 +#define RFLR_OCP_TRIM_080_MA 0x07 +#define RFLR_OCP_TRIM_085_MA 0x08 +#define RFLR_OCP_TRIM_090_MA 0x09 +#define RFLR_OCP_TRIM_095_MA 0x0A +#define RFLR_OCP_TRIM_100_MA 0x0B // Default +#define RFLR_OCP_TRIM_105_MA 0x0C +#define RFLR_OCP_TRIM_110_MA 0x0D +#define RFLR_OCP_TRIM_115_MA 0x0E +#define RFLR_OCP_TRIM_120_MA 0x0F +#define RFLR_OCP_TRIM_130_MA 0x10 +#define RFLR_OCP_TRIM_140_MA 0x11 +#define RFLR_OCP_TRIM_150_MA 0x12 +#define RFLR_OCP_TRIM_160_MA 0x13 +#define RFLR_OCP_TRIM_170_MA 0x14 +#define RFLR_OCP_TRIM_180_MA 0x15 +#define RFLR_OCP_TRIM_190_MA 0x16 +#define RFLR_OCP_TRIM_200_MA 0x17 +#define RFLR_OCP_TRIM_210_MA 0x18 +#define RFLR_OCP_TRIM_220_MA 0x19 +#define RFLR_OCP_TRIM_230_MA 0x1A +#define RFLR_OCP_TRIM_240_MA 0x1B + +/*! + * RegLna + */ +#define RFLR_LNA_GAIN_MASK 0x1F +#define RFLR_LNA_GAIN_G1 0x20 // Default +#define RFLR_LNA_GAIN_G2 0x40 +#define RFLR_LNA_GAIN_G3 0x60 +#define RFLR_LNA_GAIN_G4 0x80 +#define RFLR_LNA_GAIN_G5 0xA0 +#define RFLR_LNA_GAIN_G6 0xC0 + +#define RFLR_LNA_BOOST_LF_MASK 0xE7 +#define RFLR_LNA_BOOST_LF_DEFAULT 0x00 // Default + +#define RFLR_LNA_BOOST_HF_MASK 0xFC +#define RFLR_LNA_BOOST_HF_OFF 0x00 // Default +#define RFLR_LNA_BOOST_HF_ON 0x03 + +/*! + * RegFifoAddrPtr + */ +#define RFLR_FIFOADDRPTR 0x00 // Default + +/*! + * RegFifoTxBaseAddr + */ +#define RFLR_FIFOTXBASEADDR 0x80 // Default + +/*! + * RegFifoTxBaseAddr + */ +#define RFLR_FIFORXBASEADDR 0x00 // Default + +/*! + * RegFifoRxCurrentAddr (Read Only) + */ + +/*! + * RegIrqFlagsMask + */ +#define RFLR_IRQFLAGS_RXTIMEOUT_MASK 0x80 +#define RFLR_IRQFLAGS_RXDONE_MASK 0x40 +#define RFLR_IRQFLAGS_PAYLOADCRCERROR_MASK 0x20 +#define RFLR_IRQFLAGS_VALIDHEADER_MASK 0x10 +#define RFLR_IRQFLAGS_TXDONE_MASK 0x08 +#define RFLR_IRQFLAGS_CADDONE_MASK 0x04 +#define RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL_MASK 0x02 +#define RFLR_IRQFLAGS_CADDETECTED_MASK 0x01 + +/*! + * RegIrqFlags + */ +#define RFLR_IRQFLAGS_RXTIMEOUT 0x80 +#define RFLR_IRQFLAGS_RXDONE 0x40 +#define RFLR_IRQFLAGS_PAYLOADCRCERROR 0x20 +#define RFLR_IRQFLAGS_VALIDHEADER 0x10 +#define RFLR_IRQFLAGS_TXDONE 0x08 +#define RFLR_IRQFLAGS_CADDONE 0x04 +#define RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL 0x02 +#define RFLR_IRQFLAGS_CADDETECTED 0x01 + +/*! + * RegFifoRxNbBytes (Read Only) + */ + +/*! + * RegRxHeaderCntValueMsb (Read Only) + */ + +/*! + * RegRxHeaderCntValueLsb (Read Only) + */ + +/*! + * RegRxPacketCntValueMsb (Read Only) + */ + +/*! + * RegRxPacketCntValueLsb (Read Only) + */ + +/*! + * RegModemStat (Read Only) + */ +#define RFLR_MODEMSTAT_RX_CR_MASK 0x1F +#define RFLR_MODEMSTAT_MODEM_STATUS_MASK 0xE0 + +#define RFLR_MODEMSTAT_MODEM_CLEAR 0x10 +#define RFLR_MODEMSTAT_HEADERINFO_VALID 0x08 +#define RFLR_MODEMSTAT_RX_ONGOING 0x04 +#define RFLR_MODEMSTAT_SIGNAL_SYNCRONIZED 0x02 +#define RFLR_MODEMSTAT_SIGNAL_DETECTED 0x01 + +/*! + * RegPktSnrValue (Read Only) + */ + +/*! + * RegPktRssiValue (Read Only) + */ + +/*! + * RegRssiValue (Read Only) + */ + +/*! + * RegHopChannel (Read Only) + */ +#define RFLR_HOPCHANNEL_PLL_LOCK_TIMEOUT_MASK 0x7F +#define RFLR_HOPCHANNEL_PLL_LOCK_FAIL 0x80 +#define RFLR_HOPCHANNEL_PLL_LOCK_SUCCEED 0x00 // Default + +#define RFLR_HOPCHANNEL_CRCONPAYLOAD_MASK 0xBF +#define RFLR_HOPCHANNEL_CRCONPAYLOAD_ON 0x40 +#define RFLR_HOPCHANNEL_CRCONPAYLOAD_OFF 0x00 // Default + +#define RFLR_HOPCHANNEL_CHANNEL_MASK 0x3F + +/*! + * RegModemConfig1 + */ +#define RFLR_MODEMCONFIG1_BW_MASK 0x0F +#define RFLR_MODEMCONFIG1_BW_7_81_KHZ 0x00 +#define RFLR_MODEMCONFIG1_BW_10_41_KHZ 0x10 +#define RFLR_MODEMCONFIG1_BW_15_62_KHZ 0x20 +#define RFLR_MODEMCONFIG1_BW_20_83_KHZ 0x30 +#define RFLR_MODEMCONFIG1_BW_31_25_KHZ 0x40 +#define RFLR_MODEMCONFIG1_BW_41_66_KHZ 0x50 +#define RFLR_MODEMCONFIG1_BW_62_50_KHZ 0x60 +#define RFLR_MODEMCONFIG1_BW_125_KHZ 0x70 // Default +#define RFLR_MODEMCONFIG1_BW_250_KHZ 0x80 +#define RFLR_MODEMCONFIG1_BW_500_KHZ 0x90 + +#define RFLR_MODEMCONFIG1_CODINGRATE_MASK 0xF1 +#define RFLR_MODEMCONFIG1_CODINGRATE_4_5 0x02 +#define RFLR_MODEMCONFIG1_CODINGRATE_4_6 0x04 // Default +#define RFLR_MODEMCONFIG1_CODINGRATE_4_7 0x06 +#define RFLR_MODEMCONFIG1_CODINGRATE_4_8 0x08 + +#define RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK 0xFE +#define RFLR_MODEMCONFIG1_IMPLICITHEADER_ON 0x01 +#define RFLR_MODEMCONFIG1_IMPLICITHEADER_OFF 0x00 // Default + +/*! + * RegModemConfig2 + */ +#define RFLR_MODEMCONFIG2_SF_MASK 0x0F +#define RFLR_MODEMCONFIG2_SF_6 0x60 +#define RFLR_MODEMCONFIG2_SF_7 0x70 // Default +#define RFLR_MODEMCONFIG2_SF_8 0x80 +#define RFLR_MODEMCONFIG2_SF_9 0x90 +#define RFLR_MODEMCONFIG2_SF_10 0xA0 +#define RFLR_MODEMCONFIG2_SF_11 0xB0 +#define RFLR_MODEMCONFIG2_SF_12 0xC0 + +#define RFLR_MODEMCONFIG2_TXCONTINUOUSMODE_MASK 0xF7 +#define RFLR_MODEMCONFIG2_TXCONTINUOUSMODE_ON 0x08 +#define RFLR_MODEMCONFIG2_TXCONTINUOUSMODE_OFF 0x00 + +#define RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK 0xFB +#define RFLR_MODEMCONFIG2_RXPAYLOADCRC_ON 0x04 +#define RFLR_MODEMCONFIG2_RXPAYLOADCRC_OFF 0x00 // Default + +#define RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK 0xFC +#define RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB 0x00 // Default + +/*! + * RegSymbTimeoutLsb + */ +#define RFLR_SYMBTIMEOUTLSB_SYMBTIMEOUT 0x64 // Default + +/*! + * RegPreambleLengthMsb + */ +#define RFLR_PREAMBLELENGTHMSB 0x00 // Default + +/*! + * RegPreambleLengthLsb + */ +#define RFLR_PREAMBLELENGTHLSB 0x08 // Default + +/*! + * RegPayloadLength + */ +#define RFLR_PAYLOADLENGTH 0x0E // Default + +/*! + * RegPayloadMaxLength + */ +#define RFLR_PAYLOADMAXLENGTH 0xFF // Default + +/*! + * RegHopPeriod + */ +#define RFLR_HOPPERIOD_FREQFOPPINGPERIOD 0x00 // Default + +/*! + * RegFifoRxByteAddr (Read Only) + */ + +/*! + * RegModemConfig3 + */ +#define RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK 0xF7 +#define RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_ON 0x08 +#define RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_OFF 0x00 // Default + +#define RFLR_MODEMCONFIG3_AGCAUTO_MASK 0xFB +#define RFLR_MODEMCONFIG3_AGCAUTO_ON 0x04 // Default +#define RFLR_MODEMCONFIG3_AGCAUTO_OFF 0x00 + +/*! + * RegFeiMsb (Read Only) + */ + +/*! + * RegFeiMid (Read Only) + */ + +/*! + * RegFeiLsb (Read Only) + */ + +/*! + * RegRssiWideband (Read Only) + */ + +/*! + * RegDetectOptimize + */ +#define RFLR_DETECTIONOPTIMIZE_MASK 0xF8 +#define RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12 0x03 // Default +#define RFLR_DETECTIONOPTIMIZE_SF6 0x05 + +/*! + * RegInvertIQ + */ +#define RFLR_INVERTIQ_RX_MASK 0xBF +#define RFLR_INVERTIQ_RX_OFF 0x00 +#define RFLR_INVERTIQ_RX_ON 0x40 +#define RFLR_INVERTIQ_TX_MASK 0xFE +#define RFLR_INVERTIQ_TX_OFF 0x01 +#define RFLR_INVERTIQ_TX_ON 0x00 + +/*! + * RegDetectionThreshold + */ +#define RFLR_DETECTIONTHRESH_SF7_TO_SF12 0x0A // Default +#define RFLR_DETECTIONTHRESH_SF6 0x0C + +/*! + * RegInvertIQ2 + */ +#define RFLR_INVERTIQ2_ON 0x19 +#define RFLR_INVERTIQ2_OFF 0x1D + +/*! + * RegDioMapping1 + */ +#define RFLR_DIOMAPPING1_DIO0_MASK 0x3F +#define RFLR_DIOMAPPING1_DIO0_00 0x00 // Default +#define RFLR_DIOMAPPING1_DIO0_01 0x40 +#define RFLR_DIOMAPPING1_DIO0_10 0x80 +#define RFLR_DIOMAPPING1_DIO0_11 0xC0 + +#define RFLR_DIOMAPPING1_DIO1_MASK 0xCF +#define RFLR_DIOMAPPING1_DIO1_00 0x00 // Default +#define RFLR_DIOMAPPING1_DIO1_01 0x10 +#define RFLR_DIOMAPPING1_DIO1_10 0x20 +#define RFLR_DIOMAPPING1_DIO1_11 0x30 + +#define RFLR_DIOMAPPING1_DIO2_MASK 0xF3 +#define RFLR_DIOMAPPING1_DIO2_00 0x00 // Default +#define RFLR_DIOMAPPING1_DIO2_01 0x04 +#define RFLR_DIOMAPPING1_DIO2_10 0x08 +#define RFLR_DIOMAPPING1_DIO2_11 0x0C + +#define RFLR_DIOMAPPING1_DIO3_MASK 0xFC +#define RFLR_DIOMAPPING1_DIO3_00 0x00 // Default +#define RFLR_DIOMAPPING1_DIO3_01 0x01 +#define RFLR_DIOMAPPING1_DIO3_10 0x02 +#define RFLR_DIOMAPPING1_DIO3_11 0x03 + +/*! + * RegDioMapping2 + */ +#define RFLR_DIOMAPPING2_DIO4_MASK 0x3F +#define RFLR_DIOMAPPING2_DIO4_00 0x00 // Default +#define RFLR_DIOMAPPING2_DIO4_01 0x40 +#define RFLR_DIOMAPPING2_DIO4_10 0x80 +#define RFLR_DIOMAPPING2_DIO4_11 0xC0 + +#define RFLR_DIOMAPPING2_DIO5_MASK 0xCF +#define RFLR_DIOMAPPING2_DIO5_00 0x00 // Default +#define RFLR_DIOMAPPING2_DIO5_01 0x10 +#define RFLR_DIOMAPPING2_DIO5_10 0x20 +#define RFLR_DIOMAPPING2_DIO5_11 0x30 + +#define RFLR_DIOMAPPING2_MAP_MASK 0xFE +#define RFLR_DIOMAPPING2_MAP_PREAMBLEDETECT 0x01 +#define RFLR_DIOMAPPING2_MAP_RSSI 0x00 // Default + +/*! + * RegVersion (Read Only) + */ + +/*! + * RegPllHop + */ +#define RFLR_PLLHOP_FASTHOP_MASK 0x7F +#define RFLR_PLLHOP_FASTHOP_ON 0x80 +#define RFLR_PLLHOP_FASTHOP_OFF 0x00 // Default + +/*! + * RegTcxo + */ +#define RFLR_TCXO_TCXOINPUT_MASK 0xEF +#define RFLR_TCXO_TCXOINPUT_ON 0x10 +#define RFLR_TCXO_TCXOINPUT_OFF 0x00 // Default + +/*! + * RegPaDac + */ +#define RFLR_PADAC_20DBM_MASK 0xF8 +#define RFLR_PADAC_20DBM_ON 0x07 +#define RFLR_PADAC_20DBM_OFF 0x04 // Default + +/*! + * RegFormerTemp + */ + +/*! + * RegBitrateFrac + */ +#define RF_BITRATEFRAC_MASK 0xF0 + +/*! + * RegAgcRef + */ + +/*! + * RegAgcThresh1 + */ + +/*! + * RegAgcThresh2 + */ + +/*! + * RegAgcThresh3 + */ + +/*! + * RegPll + */ +#define RF_PLL_BANDWIDTH_MASK 0x3F +#define RF_PLL_BANDWIDTH_75 0x00 +#define RF_PLL_BANDWIDTH_150 0x40 +#define RF_PLL_BANDWIDTH_225 0x80 +#define RF_PLL_BANDWIDTH_300 0xC0 // Default + +#endif // __SX1276_REGS_LORA_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/sx1276/library.properties Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,9 @@ +name=SX1276GenericLib +version=1.0.0 +author=Semtech Inc, Helmut Tschemernjak <helmut2009@me.com> +maintainer=Helmut Tschemernjak <helmut2009@me.com> +sentence=SX1276GenericLib to support sx1276 bassed LoRa modules, including HopeRF RFM95, Murata CMWX1ZZABZ and Semtech SX1276MB1MAS/SX1276MB1LAS modules +paragraph= +category=Communication +url=http://www.radioshuttle.de +architectures=samd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/sx1276/sx1276-linux-hal.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,18 @@ + +/* + * This file contains a copy of the master content sx1276-mbed-hal.h + * with adaption for the Linux environment (PI or similar + * (c) 2017 Helmut Tschemernjak + * 30826 Garbsen (Hannover) Germany + */ +#ifdef __linux__ + + + + + + + + + +#endif // __linux__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/sx1276/sx1276-mbed-hal.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,442 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C) 2014 Semtech + +Description: - + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainers: Miguel Luis, Gregory Cristian and Nicolas Huguenin +*/ + +/* + * additional development to make it more generic across multiple OS versions + * (c) 2017 Helmut Tschemernjak + * 30826 Garbsen (Hannover) Germany + */ + +#ifdef ARDUINO + #include "arduino-mbed.h" +#endif + +#include "sx1276-mbed-hal.h" + + + +SX1276Generic::SX1276Generic( RadioEvents_t *events, BoardType_t board, + PinName mosi, PinName miso, PinName sclk, PinName nss, PinName reset, + PinName dio0, PinName dio1, PinName dio2, PinName dio3, PinName dio4, PinName dio5, + PinName antSwitch, PinName antSwitchTX, PinName antSwitchTXBoost, PinName tcxo) + : SX1276( events) +{ + Sleep_ms( 10 ); + this->RadioEvents = events; + boardConnected = board; + + _antSwitch = NULL; + _antSwitchTX = NULL; + _antSwitchTXBoost = NULL; + + _tcxo = NULL; + if (tcxo != NC) + _tcxo = new DigitalOut(tcxo); + + switch(boardConnected) { + case SX1276MB1MAS: + case SX1276MB1LAS: + _antSwitch = new DigitalOut(antSwitch); + break; + case RFM95_SX1276: + break; + case MURATA_SX1276: + _antSwitch = new DigitalOut(antSwitch); + _antSwitchTX = new DigitalOut(antSwitchTX); + _antSwitchTXBoost = new DigitalOut(antSwitchTXBoost); + break; + default: + break; + } + _spi = new XSPI(mosi, miso, sclk ); + _nss = new DigitalOut(nss); + + _reset = new DigitalInOut(reset); + + _dio0 = NULL; + _dio1 = NULL; + _dio2 = NULL; + _dio3 = NULL; + _dio4 = NULL; + _dio5 = NULL; + if (dio0 != NC) + _dio0 = new InterruptIn(dio0); + if (dio1 != NC) + _dio1 = new InterruptIn(dio1); + if (dio2 != NC) + _dio2 = new InterruptIn(dio2); + if (dio3 != NC) + _dio3 = new InterruptIn(dio3); + if (dio4 != NC) + _dio4 = new InterruptIn(dio4); + if (dio5 != NC) + _dio5 = new DigitalIn(dio5); + + Reset( ); + + IoInit( ); + + RxChainCalibration( ); + + SetOpMode( RF_OPMODE_SLEEP ); + + IoIrqInit( dioIrq ); + + RadioRegistersInit( ); + + SetModem( MODEM_FSK ); +} + +SX1276Generic::~SX1276Generic() +{ + if (_antSwitch) + delete _antSwitch; + if (_antSwitchTX) + delete _antSwitchTX; + if (_antSwitchTXBoost) + delete _antSwitchTXBoost; + + if (_tcxo) { + *_tcxo = 0; + delete (_tcxo); + } + delete _reset; + delete _spi; + delete _nss; + + if (_dio0) + delete _dio0; + if (_dio1) + delete _dio1; + if (_dio2) + delete _dio2; + if (_dio3) + delete _dio3; + if (_dio4) + delete _dio4; + if (_dio5) + delete _dio5; +} + + +//------------------------------------------------------------------------- +// Board relative functions +//------------------------------------------------------------------------- +uint8_t SX1276Generic::DetectBoardType( void ) +{ + return boardConnected; +} + +void SX1276Generic::IoInit( void ) +{ + if (_tcxo) + *_tcxo = 1; + AntSwInit( ); + SpiInit( ); +} + + +void SX1276Generic::SpiInit( void ) +{ + *_nss = 1; + _spi->format( 8,0 ); + uint32_t frequencyToSet = 8000000; +#ifdef TARGET_KL25Z //busclock frequency is halved -> double the spi frequency to compensate + _spi->frequency( frequencyToSet * 2 ); +#else + _spi->frequency( frequencyToSet ); +#endif + wait_ms(100); +} + +void SX1276Generic::IoIrqInit( DioIrqHandler *irqHandlers ) +{ + if (_dio0) + _dio0->rise(callback(this, static_cast< Trigger > ( irqHandlers[0] ))); + if (_dio1) + _dio1->rise(callback(this, static_cast< Trigger > ( irqHandlers[1] ))); + if (_dio2) + _dio2->rise(callback(this, static_cast< Trigger > ( irqHandlers[2] ))); + if (_dio3) + _dio3->rise(callback(this, static_cast< Trigger > ( irqHandlers[3] ))); + if (_dio4) + _dio4->rise(callback(this, static_cast< Trigger > ( irqHandlers[4] ))); +} + +void SX1276Generic::IoDeInit( void ) +{ + //nothing +} + +void SX1276Generic::SetRfTxPower( int8_t power ) +{ + uint8_t paConfig = 0; + uint8_t paDac = 0; + + paConfig = Read( REG_PACONFIG ); + paDac = Read( REG_PADAC ); + + paConfig = ( paConfig & RF_PACONFIG_PASELECT_MASK ) | GetPaSelect( this->settings.Channel ); + paConfig = ( paConfig & RF_PACONFIG_MAX_POWER_MASK ) | 0x70; + + if( ( paConfig & RF_PACONFIG_PASELECT_PABOOST ) == RF_PACONFIG_PASELECT_PABOOST ) + { + if( power > 17 ) + { + paDac = ( paDac & RF_PADAC_20DBM_MASK ) | RF_PADAC_20DBM_ON; + } + else + { + paDac = ( paDac & RF_PADAC_20DBM_MASK ) | RF_PADAC_20DBM_OFF; + } + if( ( paDac & RF_PADAC_20DBM_ON ) == RF_PADAC_20DBM_ON ) + { + if( power < 5 ) + { + power = 5; + } + if( power > 20 ) + { + power = 20; + } + paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power - 5 ) & 0x0F ); + } + else + { + if( power < 2 ) + { + power = 2; + } + if( power > 17 ) + { + power = 17; + } + paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power - 2 ) & 0x0F ); + } + } + else + { + if( power < -1 ) + { + power = -1; + } + if( power > 14 ) + { + power = 14; + } + paConfig = ( paConfig & RF_PACONFIG_OUTPUTPOWER_MASK ) | ( uint8_t )( ( uint16_t )( power + 1 ) & 0x0F ); + } + Write( REG_PACONFIG, paConfig ); + Write( REG_PADAC, paDac ); +} + + +uint8_t SX1276Generic::GetPaSelect( uint32_t channel ) +{ + if( channel > RF_MID_BAND_THRESH ) + { + if (boardConnected == SX1276MB1LAS || boardConnected == RFM95_SX1276 || boardConnected == MURATA_SX1276) + { + return RF_PACONFIG_PASELECT_PABOOST; + } + else + { + return RF_PACONFIG_PASELECT_RFO; + } + } + else + { + return RF_PACONFIG_PASELECT_RFO; + } +} + +void SX1276Generic::SetAntSwLowPower( bool status ) +{ + if( isRadioActive != status ) + { + isRadioActive = status; + + if( status == false ) + { + AntSwInit( ); + } + else + { + AntSwDeInit( ); + } + } +} + +void SX1276Generic::AntSwInit( void ) +{ + if (_antSwitch) + *_antSwitch = 0; + if (boardConnected == MURATA_SX1276) { + *_antSwitchTX = 0; + *_antSwitchTXBoost = 0; + } +} + +void SX1276Generic::AntSwDeInit( void ) +{ + if (_antSwitch) + *_antSwitch = 0; + if (boardConnected == MURATA_SX1276) { + *_antSwitchTX = 0; + *_antSwitchTXBoost = 0; + } +} + + +void SX1276Generic::SetAntSw( uint8_t opMode ) +{ + switch( opMode ) + { + case RFLR_OPMODE_TRANSMITTER: + if (boardConnected == MURATA_SX1276) { + *_antSwitch = 0;// Murata-RX + if (Read( REG_PACONFIG) & RF_PACONFIG_PASELECT_PABOOST) + *_antSwitchTXBoost = 1; + else + *_antSwitchTX = 1; // alternate: antSwitchTXBoost = 1 + } else { + if (_antSwitch) + *_antSwitch = 1; + } + break; + case RFLR_OPMODE_RECEIVER: + case RFLR_OPMODE_RECEIVER_SINGLE: + case RFLR_OPMODE_CAD: + if (boardConnected == MURATA_SX1276) { + *_antSwitch = 1; // Murata-RX + *_antSwitchTX = 0; + *_antSwitchTXBoost = 0; + } else { + if (_antSwitch) + _antSwitch = 0; + } + break; + case RFLR_OPMODE_SLEEP: + case RFLR_OPMODE_STANDBY: + default: + if (boardConnected == MURATA_SX1276) { + *_antSwitch = 0; //Murata-RX + *_antSwitchTX = 0; + *_antSwitchTXBoost = 0; + } else { + if (_antSwitch) + *_antSwitch = 0; + } + break; + } +} + +void SX1276Generic::SetTimeout(TimeoutTimer_t timer, timeoutFuncPtr func, int timeout_ms) +{ + switch(timer) { + case RXTimeoutTimer: + if (func) + rxTimeoutTimer.attach_us(callback(this, func), timeout_ms); + else + rxTimeoutTimer.detach(); + break; + case TXTimeoutTimer: + if (func) + txTimeoutTimer.attach_us(callback(this, func), timeout_ms); + else + txTimeoutTimer.detach(); + break; + case RXTimeoutSyncWordTimer: + if (func) + rxTimeoutSyncWord.attach_us(callback(this, func), timeout_ms); + else + rxTimeoutSyncWord.detach(); + break; + } +} + +void +SX1276Generic::Sleep_ms(int ms) +{ + wait_ms(ms); +} + +bool SX1276Generic::CheckRfFrequency( uint32_t frequency ) +{ + if (frequency > 1200000) + return false; + // Implement check. Currently all frequencies are supported + return true; +} + +void SX1276Generic::Reset( void ) +{ + _reset->output(); + *_reset = 0; + wait_ms( 1 ); + *_reset = 1; + _reset->input(); // I don't know my input again, maybe to save power (Helmut T) + wait_ms( 6 ); +} + +void SX1276Generic::Write( uint8_t addr, uint8_t data ) +{ + Write( addr, &data, 1 ); +} + +uint8_t SX1276Generic::Read( uint8_t addr ) +{ + uint8_t data; + Read( addr, &data, 1 ); + return data; +} + +void SX1276Generic::Write( uint8_t addr, void *buffer, uint8_t size ) +{ + uint8_t i; + uint8_t *p = (uint8_t *)buffer; + + *_nss = 0; // what about SPI hold/release timing on fast MCUs? Helmut + _spi->write( addr | 0x80 ); + for( i = 0; i < size; i++ ) + { + _spi->write(*p++); + } + *_nss = 1; +} + +void SX1276Generic::Read( uint8_t addr, void *buffer, uint8_t size ) +{ + uint8_t i; + uint8_t *p = (uint8_t *)buffer; + + *_nss = 0; // what about SPI hold/release timing on fast MCUs? Helmut + _spi->write( addr & 0x7F ); + for( i = 0; i < size; i++ ) + { + *p++ = _spi->write( 0 ); + } + *_nss = 1; +} + +void SX1276Generic::WriteFifo( void *buffer, uint8_t size ) +{ + Write( 0, buffer, size ); +} + +void SX1276Generic::ReadFifo( void *buffer, uint8_t size ) +{ + Read( 0, buffer, size ); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/sx1276/sx1276-mbed-hal.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,251 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C) 2014 Semtech + +Description: - + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainers: Miguel Luis, Gregory Cristian and Nicolas Huguenin +*/ + +/* + * additional development to make it more generic across multiple OS versions + * (c) 2017 Helmut Tschemernjak + * 30826 Garbsen (Hannover) Germany + */ + +#ifndef __SX1276_MBED_HAL_H__ +#define __SX1276_MBED_HAL_H__ + + +#include "sx1276.h" + + +#ifdef __MBED__ +#define XSPI SPI +#endif + + +/*! + * Actual implementation of a SX1276 radio, includes some modifications to make it + * compatible with the MB1 LAS board + */ +class SX1276Generic : public SX1276 +{ +protected: + /*! + * Antenna switch GPIO pins objects + */ + DigitalOut *_antSwitch; + DigitalOut *_antSwitchTX; + DigitalOut *_antSwitchTXBoost; + + /*! + * SX1276 Reset pin + */ + DigitalInOut *_reset; + + /*! + * TCXO being used with the Murata Module + */ + DigitalOut *_tcxo; + + /*! + * SPI Interface + */ + XSPI *_spi; // mosi, miso, sclk + DigitalOut *_nss; + + /*! + * SX1276 DIO pins + */ + InterruptIn *_dio0; + InterruptIn *_dio1; + InterruptIn *_dio2; + InterruptIn *_dio3; + InterruptIn *_dio4; + DigitalIn *_dio5; + + /*! + * Tx and Rx timers + */ + Timeout txTimeoutTimer; + Timeout rxTimeoutTimer; + Timeout rxTimeoutSyncWord; + + +private: + /*! + * triggers definition + */ + typedef void (SX1276Generic::*Trigger)(void); + + +public: + SX1276Generic( RadioEvents_t *events, BoardType_t board, + PinName mosi, PinName miso, PinName sclk, PinName nss, PinName reset, + PinName dio0, PinName dio1, PinName dio2, PinName dio3, PinName dio4, PinName dio5, + PinName antSwitch = NC, PinName antSwitchTX= NC, PinName antSwitchTXBoost = NC, PinName tcxo = NC); + + + SX1276Generic( RadioEvents_t *events ); + + virtual ~SX1276Generic(); + +protected: + /*! + * @brief Initializes the radio I/Os pins interface + */ + virtual void IoInit( void ); + + /*! + * @brief Initializes the radio SPI + */ + virtual void SpiInit( void ); + + /*! + * @brief Initializes DIO IRQ handlers + * + * @param [IN] irqHandlers Array containing the IRQ callback functions + */ + virtual void IoIrqInit( DioIrqHandler *irqHandlers ); + + /*! + * @brief De-initializes the radio I/Os pins interface. + * + * \remark Useful when going in MCU lowpower modes + */ + virtual void IoDeInit( void ); + + /*! + * @brief Gets the board PA selection configuration + * + * @param [IN] channel Channel frequency in Hz + * @retval PaSelect RegPaConfig PaSelect value + */ + virtual uint8_t GetPaSelect( uint32_t channel ); + + /*! + * @brief Set the RF Switch I/Os pins in Low Power mode + * + * @param [IN] status enable or disable + */ + virtual void SetAntSwLowPower( bool status ); + + /*! + * @brief Initializes the RF Switch I/Os pins interface + */ + virtual void AntSwInit( void ); + + /*! + * @brief De-initializes the RF Switch I/Os pins interface + * + * @remark Needed to decrease the power consumption in MCU lowpower modes + */ + virtual void AntSwDeInit( void ); + + /*! + * @brief Controls the antena switch if necessary. + * + * @remark see errata note + * + * @param [IN] opMode Current radio operating mode + */ + virtual void SetAntSw( uint8_t opMode ); + + /* + * The the Timeout for a given Timer. + */ + virtual void SetTimeout(TimeoutTimer_t timer, timeoutFuncPtr, int timeout_ms = 0); + + /* + * A simple ms sleep + */ + virtual void Sleep_ms(int ms); + + +public: + + /*! + * @brief Detect the board connected by reading the value of the antenna switch pin + */ + virtual uint8_t DetectBoardType( void ); + + /*! + * @brief Checks if the given RF frequency is supported by the hardware + * + * @param [IN] frequency RF frequency to be checked + * @retval isSupported [true: supported, false: unsupported] + */ + virtual bool CheckRfFrequency( uint32_t frequency ); + + /*! + * @brief Writes the radio register at the specified address + * + * @param [IN]: addr Register address + * @param [IN]: data New register value + */ + virtual void Write ( uint8_t addr, uint8_t data ) ; + + /*! + * @brief Reads the radio register at the specified address + * + * @param [IN]: addr Register address + * @retval data Register value + */ + virtual uint8_t Read ( uint8_t addr ) ; + + /*! + * @brief Writes multiple radio registers starting at address + * + * @param [IN] addr First Radio register address + * @param [IN] buffer Buffer containing the new register's values + * @param [IN] size Number of registers to be written + */ + virtual void Write( uint8_t addr, void *buffer, uint8_t size ) ; + + /*! + * @brief Reads multiple radio registers starting at address + * + * @param [IN] addr First Radio register address + * @param [OUT] buffer Buffer where to copy the registers data + * @param [IN] size Number of registers to be read + */ + virtual void Read ( uint8_t addr, void *buffer, uint8_t size ) ; + + /*! + * @brief Writes the buffer contents to the SX1276 FIFO + * + * @param [IN] buffer Buffer containing data to be put on the FIFO. + * @param [IN] size Number of bytes to be written to the FIFO + */ + virtual void WriteFifo( void *buffer, uint8_t size ) ; + + /*! + * @brief Reads the contents of the SX1276 FIFO + * + * @param [OUT] buffer Buffer where to copy the FIFO read data. + * @param [IN] size Number of bytes to be read from the FIFO + */ + virtual void ReadFifo( void *buffer, uint8_t size ) ; + + /*! + * @brief Reset the SX1276 + */ + virtual void Reset( void ); + + /*! + * \brief Sets the radio output power. + * + * @param [IN] power Sets the RF output power + */ + virtual void SetRfTxPower( int8_t power ); + +}; + +#endif // __SX1276_MBED_HAL_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/sx1276/sx1276.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,1711 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C) 2014 Semtech + +Description: Actual implementation of a SX1276 radio, inherits Radio + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainers: Miguel Luis, Gregory Cristian and Nicolas Huguenin +*/ + +/* + * additional development to make it more generic across multiple OS versions + * (c) 2017 Helmut Tschemernjak + * 30826 Garbsen (Hannover) Germany + */ + +#include "sx1276.h" + + + +const SX1276::BandwidthMap SX1276::FskBandwidths[] = +{ + { 2600 , 0x17 }, + { 3100 , 0x0F }, + { 3900 , 0x07 }, + { 5200 , 0x16 }, + { 6300 , 0x0E }, + { 7800 , 0x06 }, + { 10400 , 0x15 }, + { 12500 , 0x0D }, + { 15600 , 0x05 }, + { 20800 , 0x14 }, + { 25000 , 0x0C }, + { 31300 , 0x04 }, + { 41700 , 0x13 }, + { 50000 , 0x0B }, + { 62500 , 0x03 }, + { 83333 , 0x12 }, + { 100000, 0x0A }, + { 125000, 0x02 }, + { 166700, 0x11 }, + { 200000, 0x09 }, + { 250000, 0x01 }, + { 300000, 0x00 }, // Invalid Bandwidth +}; + +const SX1276::BandwidthMap SX1276::LoRaBandwidths[] = +{ + { 7800, 0 }, // 7.8 kHz requires TCXO + { 10400, 1 }, // 10.4 kHz requires TCXO + { 15600, 2 }, // 15.6 kHz requires TCXO + { 20800, 3 }, // 20.8 kHz requires TCXO + { 31250, 4 }, // 31.25 kHz requires TCXO + { 41700, 5 }, // 41.7 kHz requires TCXO + { 62500, 6 }, // 62.5 kHz requires TCXO + { 125000, 7 }, // 125 kHz the LoRa protocol default + { 250000, 8 }, // 250 kHz + { 500000, 9 }, // 500 kHz + { 600000, 10 }, // Invalid Bandwidth, reserved + }; + + + +/*! + * @brief Radio hardware registers initialization definition + * + * @remark Can be automatically generated by the SX1276 GUI (not yet implemented) + */ + +const SX1276::RadioRegisters SX1276::RadioRegsInit[] = { + { MODEM_FSK , REG_LNA , 0x23 }, + { MODEM_FSK , REG_RXCONFIG , 0x1E }, + { MODEM_FSK , REG_RSSICONFIG , 0xD2 }, + { MODEM_FSK , REG_AFCFEI , 0x01 }, + { MODEM_FSK , REG_PREAMBLEDETECT , 0xAA }, + { MODEM_FSK , REG_OSC , 0x07 }, + { MODEM_FSK , REG_SYNCCONFIG , 0x12 }, + { MODEM_FSK , REG_SYNCVALUE1 , 0xC1 }, + { MODEM_FSK , REG_SYNCVALUE2 , 0x94 }, + { MODEM_FSK , REG_SYNCVALUE3 , 0xC1 }, + { MODEM_FSK , REG_PACKETCONFIG1 , 0xD8 }, + { MODEM_FSK , REG_FIFOTHRESH , 0x8F }, + { MODEM_FSK , REG_IMAGECAL , 0x02 }, + { MODEM_FSK , REG_DIOMAPPING1 , 0x00 }, + { MODEM_FSK , REG_DIOMAPPING2 , 0x30 }, + { MODEM_LORA, REG_LR_PAYLOADMAXLENGTH, 0x40 }, + +}; + + +SX1276::SX1276( RadioEvents_t *events) : Radio( events ), isRadioActive( false ) +{ + this->rxtxBuffer = new uint8_t[RX_BUFFER_SIZE]; + + this->RadioEvents = events; + + this->dioIrq = new DioIrqHandler[6]; + + this->dioIrq[0] = &SX1276::OnDio0Irq; + this->dioIrq[1] = &SX1276::OnDio1Irq; + this->dioIrq[2] = &SX1276::OnDio2Irq; + this->dioIrq[3] = &SX1276::OnDio3Irq; + this->dioIrq[4] = &SX1276::OnDio4Irq; + this->dioIrq[5] = NULL; + + this->settings.State = RF_IDLE; +} + +SX1276::~SX1276( ) +{ + delete this->rxtxBuffer; + delete this->dioIrq; +} + +bool SX1276::Init( RadioEvents_t *events ) +{ + if (Read(REG_VERSION) == 0x00) + return false; + + this->RadioEvents = events; + return true; +} + + +void SX1276::RadioRegistersInit( ) +{ + uint8_t i = 0; + for( i = 0; i < sizeof( RadioRegsInit ) / sizeof( RadioRegisters ); i++ ) + { + SetModem( RadioRegsInit[i].Modem ); + Write( RadioRegsInit[i].Addr, RadioRegsInit[i].Value ); + } +} + + +RadioState SX1276::GetStatus( void ) +{ + return this->settings.State; +} + +void SX1276::SetChannel( uint32_t freq ) +{ + this->settings.Channel = freq; + freq = ( uint32_t )( ( double )freq / ( double )FREQ_STEP ); + Write( REG_FRFMSB, ( uint8_t )( ( freq >> 16 ) & 0xFF ) ); + Write( REG_FRFMID, ( uint8_t )( ( freq >> 8 ) & 0xFF ) ); + Write( REG_FRFLSB, ( uint8_t )( freq & 0xFF ) ); +} + +bool SX1276::IsChannelFree( RadioModems_t modem, uint32_t freq, int16_t rssiThresh ) +{ + int16_t rssi = 0; + + SetModem( modem ); + + SetChannel( freq ); + + SetOpMode( RF_OPMODE_RECEIVER ); + + Sleep_ms( 1 ); + + rssi = GetRssi( modem ); + + Sleep( ); + + if( rssi > rssiThresh ) + { + return false; + } + return true; +} + +uint32_t SX1276::Random( void ) +{ + uint8_t i; + uint32_t rnd = 0; + + /* + * Radio setup for random number generation + */ + // Set LoRa modem ON + SetModem( MODEM_LORA ); + + // Disable LoRa modem interrupts + Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // Set radio in continuous reception + SetOpMode( RF_OPMODE_RECEIVER ); + + for( i = 0; i < 32; i++ ) + { + Sleep_ms( 1 ); + // Unfiltered RSSI value reading. Only takes the LSB value + rnd |= ( ( uint32_t )Read( REG_LR_RSSIWIDEBAND ) & 0x01 ) << i; + } + + Sleep( ); + + return rnd; +} + +/*! + * Performs the Rx chain calibration for LF and HF bands + * \remark Must be called just after the reset so all registers are at their + * default values + */ +void SX1276::RxChainCalibration( void ) +{ + uint8_t regPaConfigInitVal; + uint32_t initialFreq; + + // Save context + regPaConfigInitVal = this->Read( REG_PACONFIG ); + initialFreq = ( double )( ( ( uint32_t )this->Read( REG_FRFMSB ) << 16 ) | + ( ( uint32_t )this->Read( REG_FRFMID ) << 8 ) | + ( ( uint32_t )this->Read( REG_FRFLSB ) ) ) * ( double )FREQ_STEP; + + // Cut the PA just in case, RFO output, power = -1 dBm + this->Write( REG_PACONFIG, 0x00 ); + + // Launch Rx chain calibration for LF band + Write ( REG_IMAGECAL, ( Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_MASK ) | RF_IMAGECAL_IMAGECAL_START ); + while( ( Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_RUNNING ) == RF_IMAGECAL_IMAGECAL_RUNNING ) + { + } + + // Sets a Frequency in HF band + SetChannel( 868000000 ); + + // Launch Rx chain calibration for HF band + Write ( REG_IMAGECAL, ( Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_MASK ) | RF_IMAGECAL_IMAGECAL_START ); + while( ( Read( REG_IMAGECAL ) & RF_IMAGECAL_IMAGECAL_RUNNING ) == RF_IMAGECAL_IMAGECAL_RUNNING ) + { + } + + // Restore context + this->Write( REG_PACONFIG, regPaConfigInitVal ); + SetChannel( initialFreq ); +} + +/*! + * Returns the known FSK bandwidth registers value + * + * \param [IN] bandwidth Bandwidth value in Hz + * \retval regValue Bandwidth register value. + */ +uint8_t SX1276::GetFskBandwidthRegValue( uint32_t bandwidth ) +{ + uint8_t i; + + for( i = 0; i < ( sizeof( FskBandwidths ) / sizeof( BandwidthMap ) ) - 1; i++ ) + { + if( ( bandwidth >= FskBandwidths[i].bandwidth ) && ( bandwidth < FskBandwidths[i + 1].bandwidth ) ) + { + return FskBandwidths[i].RegValue; + } + } + // ERROR: Value not found + while( 1 ); +} + +/*! + * Returns the known LoRa bandwidth registers value + * + * \param [IN] bandwidth Bandwidth value in Hz + * \retval regValue Bandwidth register value. + */ +uint8_t SX1276::GetLoRaBandwidthRegValue( uint32_t bandwidth ) +{ + uint8_t i; + + for( i = 0; i < ( sizeof( LoRaBandwidths ) / sizeof( BandwidthMap ) ) - 1; i++ ) + { + if( ( bandwidth >= LoRaBandwidths[i].bandwidth ) && ( bandwidth < LoRaBandwidths[i + 1].bandwidth ) ) + { + return LoRaBandwidths[i].RegValue; + } + } + // ERROR: Value not found + while( 1 ); +} + +void SX1276::SetRxConfig( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool freqHopOn, uint8_t hopPeriod, + bool iqInverted, bool rxContinuous ) +{ + SetModem( modem ); + + switch( modem ) + { + case MODEM_FSK: + { + this->settings.Fsk.Bandwidth = bandwidth; + this->settings.Fsk.Datarate = datarate; + this->settings.Fsk.BandwidthAfc = bandwidthAfc; + this->settings.Fsk.FixLen = fixLen; + this->settings.Fsk.PayloadLen = payloadLen; + this->settings.Fsk.CrcOn = crcOn; + this->settings.Fsk.IqInverted = iqInverted; + this->settings.Fsk.RxContinuous = rxContinuous; + this->settings.Fsk.PreambleLen = preambleLen; + this->settings.Fsk.RxSingleTimeout = symbTimeout * ( ( 1.0 / ( double )datarate ) * 8.0 ) * 1e3; + + + datarate = ( uint16_t )( ( double )XTAL_FREQ / ( double )datarate ); + Write( REG_BITRATEMSB, ( uint8_t )( datarate >> 8 ) ); + Write( REG_BITRATELSB, ( uint8_t )( datarate & 0xFF ) ); + + Write( REG_RXBW, GetFskBandwidthRegValue( bandwidth ) ); + Write( REG_AFCBW, GetFskBandwidthRegValue( bandwidthAfc ) ); + + Write( REG_PREAMBLEMSB, ( uint8_t )( ( preambleLen >> 8 ) & 0xFF ) ); + Write( REG_PREAMBLELSB, ( uint8_t )( preambleLen & 0xFF ) ); + + if( fixLen == 1 ) + { + Write( REG_PAYLOADLENGTH, payloadLen ); + } + else + { + Write( REG_PAYLOADLENGTH, 0xFF ); // Set payload length to the maximum + } + + Write( REG_PACKETCONFIG1, + ( Read( REG_PACKETCONFIG1 ) & + RF_PACKETCONFIG1_CRC_MASK & + RF_PACKETCONFIG1_PACKETFORMAT_MASK ) | + ( ( fixLen == 1 ) ? RF_PACKETCONFIG1_PACKETFORMAT_FIXED : RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE ) | + ( crcOn << 4 ) ); + Write( REG_PACKETCONFIG2, ( Read( REG_PACKETCONFIG2 ) | RF_PACKETCONFIG2_DATAMODE_PACKET ) ); + } + break; + case MODEM_LORA: + { + if (bandwidth > 11) // specified in Hz, needs mapping + bandwidth = GetLoRaBandwidthRegValue(bandwidth); + if( bandwidth > LORA_BANKWIDTH_500kHz ) + { + // Fatal error: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + while( 1 ); + } + this->settings.LoRa.Bandwidth = bandwidth; + this->settings.LoRa.Datarate = datarate; + this->settings.LoRa.Coderate = coderate; + this->settings.LoRa.PreambleLen = preambleLen; + this->settings.LoRa.FixLen = fixLen; + this->settings.LoRa.PayloadLen = payloadLen; + this->settings.LoRa.CrcOn = crcOn; + this->settings.LoRa.FreqHopOn = freqHopOn; + this->settings.LoRa.HopPeriod = hopPeriod; + this->settings.LoRa.IqInverted = iqInverted; + this->settings.LoRa.RxContinuous = rxContinuous; + + if( datarate > LORA_SF12 ) + { + datarate = LORA_SF12; + } + else if( datarate < LORA_SF6 ) + { + datarate = LORA_SF6; + } + + if( ( ( bandwidth == LORA_BANKWIDTH_125kHz ) && ( ( datarate == LORA_SF11 ) || ( datarate == LORA_SF12 ) ) ) || + ( ( bandwidth == LORA_BANKWIDTH_250kHz ) && ( datarate == LORA_SF12 ) ) ) + { + this->settings.LoRa.LowDatarateOptimize = 0x01; + } + else + { + this->settings.LoRa.LowDatarateOptimize = 0x00; + } + + Write( REG_LR_MODEMCONFIG1, + ( Read( REG_LR_MODEMCONFIG1 ) & + RFLR_MODEMCONFIG1_BW_MASK & + RFLR_MODEMCONFIG1_CODINGRATE_MASK & + RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK ) | + ( bandwidth << 4 ) | ( coderate << 1 ) | + fixLen ); + + Write( REG_LR_MODEMCONFIG2, + ( Read( REG_LR_MODEMCONFIG2 ) & + RFLR_MODEMCONFIG2_SF_MASK & + RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK & + RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK ) | + ( datarate << 4 ) | ( crcOn << 2 ) | + ( ( symbTimeout >> 8 ) & ~RFLR_MODEMCONFIG2_SYMBTIMEOUTMSB_MASK ) ); + + Write( REG_LR_MODEMCONFIG3, + ( Read( REG_LR_MODEMCONFIG3 ) & + RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK ) | + ( this->settings.LoRa.LowDatarateOptimize << 3 ) ); + + Write( REG_LR_SYMBTIMEOUTLSB, ( uint8_t )( symbTimeout & 0xFF ) ); + + Write( REG_LR_PREAMBLEMSB, ( uint8_t )( ( preambleLen >> 8 ) & 0xFF ) ); + Write( REG_LR_PREAMBLELSB, ( uint8_t )( preambleLen & 0xFF ) ); + + if( fixLen == 1 ) + { + Write( REG_LR_PAYLOADLENGTH, payloadLen ); + } + + if( this->settings.LoRa.FreqHopOn == true ) + { + Write( REG_LR_PLLHOP, ( Read( REG_LR_PLLHOP ) & RFLR_PLLHOP_FASTHOP_MASK ) | RFLR_PLLHOP_FASTHOP_ON ); + Write( REG_LR_HOPPERIOD, this->settings.LoRa.HopPeriod ); + } + + if( ( bandwidth == LORA_BANKWIDTH_500kHz ) && ( this->settings.Channel > RF_MID_BAND_THRESH ) ) + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + Write( REG_LR_TEST36, 0x02 ); + Write( REG_LR_TEST3A, 0x64 ); + } + else if( bandwidth == LORA_BANKWIDTH_500kHz ) + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + Write( REG_LR_TEST36, 0x02 ); + Write( REG_LR_TEST3A, 0x7F ); + } + else + { + // ERRATA 2.1 - Sensitivity Optimization with a 500 kHz Bandwidth + Write( REG_LR_TEST36, 0x03 ); + } + + if( datarate == LORA_SF6 ) + { + Write( REG_LR_DETECTOPTIMIZE, + ( Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF6 ); + Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF6 ); + } + else + { + Write( REG_LR_DETECTOPTIMIZE, + ( Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12 ); + Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF7_TO_SF12 ); + } + } + break; + } +} + +void SX1276::SetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool freqHopOn, + uint8_t hopPeriod, bool iqInverted, uint32_t timeout ) +{ + SetModem( modem ); + SetRfTxPower( power ); + + switch( modem ) + { + case MODEM_FSK: + { + this->settings.Fsk.Power = power; + this->settings.Fsk.Fdev = fdev; + this->settings.Fsk.Bandwidth = bandwidth; + this->settings.Fsk.Datarate = datarate; + this->settings.Fsk.PreambleLen = preambleLen; + this->settings.Fsk.FixLen = fixLen; + this->settings.Fsk.CrcOn = crcOn; + this->settings.Fsk.IqInverted = iqInverted; + this->settings.Fsk.TxTimeout = timeout; + + fdev = ( uint16_t )( ( double )fdev / ( double )FREQ_STEP ); + Write( REG_FDEVMSB, ( uint8_t )( fdev >> 8 ) ); + Write( REG_FDEVLSB, ( uint8_t )( fdev & 0xFF ) ); + + datarate = ( uint16_t )( ( double )XTAL_FREQ / ( double )datarate ); + Write( REG_BITRATEMSB, ( uint8_t )( datarate >> 8 ) ); + Write( REG_BITRATELSB, ( uint8_t )( datarate & 0xFF ) ); + + Write( REG_PREAMBLEMSB, ( preambleLen >> 8 ) & 0x00FF ); + Write( REG_PREAMBLELSB, preambleLen & 0xFF ); + + Write( REG_PACKETCONFIG1, + ( Read( REG_PACKETCONFIG1 ) & + RF_PACKETCONFIG1_CRC_MASK & + RF_PACKETCONFIG1_PACKETFORMAT_MASK ) | + ( ( fixLen == 1 ) ? RF_PACKETCONFIG1_PACKETFORMAT_FIXED : RF_PACKETCONFIG1_PACKETFORMAT_VARIABLE ) | + ( crcOn << 4 ) ); + Write( REG_PACKETCONFIG2, ( Read( REG_PACKETCONFIG2 ) | RF_PACKETCONFIG2_DATAMODE_PACKET ) ); + } + break; + case MODEM_LORA: + { + this->settings.LoRa.Power = power; + if (bandwidth > 11) // specified in Hz, needs mapping + bandwidth = GetLoRaBandwidthRegValue(bandwidth); + if( bandwidth > LORA_BANKWIDTH_500kHz ) + { + // Fatal error: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + while( 1 ); + } + this->settings.LoRa.Bandwidth = bandwidth; + this->settings.LoRa.Datarate = datarate; + this->settings.LoRa.Coderate = coderate; + this->settings.LoRa.PreambleLen = preambleLen; + this->settings.LoRa.FixLen = fixLen; + this->settings.LoRa.FreqHopOn = freqHopOn; + this->settings.LoRa.HopPeriod = hopPeriod; + this->settings.LoRa.CrcOn = crcOn; + this->settings.LoRa.IqInverted = iqInverted; + this->settings.LoRa.TxTimeout = timeout; + + if( datarate > LORA_SF12 ) + { + datarate = LORA_SF12; + } + else if( datarate < LORA_SF6 ) + { + datarate = LORA_SF6; + } + if( ( ( bandwidth == LORA_BANKWIDTH_125kHz ) && ( ( datarate == LORA_SF11 ) || ( datarate == LORA_SF12 ) ) ) || + ( ( bandwidth == LORA_BANKWIDTH_250kHz ) && ( datarate == LORA_SF12 ) ) ) + { + this->settings.LoRa.LowDatarateOptimize = 0x01; + } + else + { + this->settings.LoRa.LowDatarateOptimize = 0x00; + } + + if( this->settings.LoRa.FreqHopOn == true ) + { + Write( REG_LR_PLLHOP, ( Read( REG_LR_PLLHOP ) & RFLR_PLLHOP_FASTHOP_MASK ) | RFLR_PLLHOP_FASTHOP_ON ); + Write( REG_LR_HOPPERIOD, this->settings.LoRa.HopPeriod ); + } + + Write( REG_LR_MODEMCONFIG1, + ( Read( REG_LR_MODEMCONFIG1 ) & + RFLR_MODEMCONFIG1_BW_MASK & + RFLR_MODEMCONFIG1_CODINGRATE_MASK & + RFLR_MODEMCONFIG1_IMPLICITHEADER_MASK ) | + ( bandwidth << 4 ) | ( coderate << 1 ) | + fixLen ); + + Write( REG_LR_MODEMCONFIG2, + ( Read( REG_LR_MODEMCONFIG2 ) & + RFLR_MODEMCONFIG2_SF_MASK & + RFLR_MODEMCONFIG2_RXPAYLOADCRC_MASK ) | + ( datarate << 4 ) | ( crcOn << 2 ) ); + + Write( REG_LR_MODEMCONFIG3, + ( Read( REG_LR_MODEMCONFIG3 ) & + RFLR_MODEMCONFIG3_LOWDATARATEOPTIMIZE_MASK ) | + ( this->settings.LoRa.LowDatarateOptimize << 3 ) ); + + Write( REG_LR_PREAMBLEMSB, ( preambleLen >> 8 ) & 0x00FF ); + Write( REG_LR_PREAMBLELSB, preambleLen & 0xFF ); + + if( datarate == LORA_SF6 ) + { + Write( REG_LR_DETECTOPTIMIZE, + ( Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF6 ); + Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF6 ); + } + else + { + Write( REG_LR_DETECTOPTIMIZE, + ( Read( REG_LR_DETECTOPTIMIZE ) & + RFLR_DETECTIONOPTIMIZE_MASK ) | + RFLR_DETECTIONOPTIMIZE_SF7_TO_SF12 ); + Write( REG_LR_DETECTIONTHRESHOLD, + RFLR_DETECTIONTHRESH_SF7_TO_SF12 ); + } + } + break; + } +} + +uint32_t SX1276::TimeOnAir( RadioModems_t modem, int16_t pktLen ) +{ + uint32_t airTime = 0; + + switch( modem ) + { + case MODEM_FSK: + { + airTime = rint( ( 8 * ( this->settings.Fsk.PreambleLen + + ( ( Read( REG_SYNCCONFIG ) & ~RF_SYNCCONFIG_SYNCSIZE_MASK ) + 1 ) + + ( ( this->settings.Fsk.FixLen == 0x01 ) ? 0.0 : 1.0 ) + + ( ( ( Read( REG_PACKETCONFIG1 ) & ~RF_PACKETCONFIG1_ADDRSFILTERING_MASK ) != 0x00 ) ? 1.0 : 0 ) + + pktLen + + ( ( this->settings.Fsk.CrcOn == 0x01 ) ? 2.0 : 0 ) ) / + this->settings.Fsk.Datarate ) * 1e3 ); + } + break; + case MODEM_LORA: + { + double bw = 0.0; + // REMARK: When using LoRa modem only bandwidths 125, 250 and 500 kHz are supported + switch( this->settings.LoRa.Bandwidth ) + { + case LORA_BANKWIDTH_7kHz: // 7.8 kHz + bw = 78e2; + break; + case LORA_BANKWIDTH_10kHz: // 10.4 kHz + bw = 104e2; + break; + case LORA_BANKWIDTH_15kHz: // 15.6 kHz + bw = 156e2; + break; + case LORA_BANKWIDTH_20kHz: // 20.8 kHz + bw = 208e2; + break; + case LORA_BANKWIDTH_31kHz: // 31.25 kHz + bw = 312e2; + break; + case LORA_BANKWIDTH_41kHz: // 41.7 kHz + bw = 414e2; + break; + case LORA_BANKWIDTH_62kHz: // 62.5 kHz + bw = 625e2; + break; + case LORA_BANKWIDTH_125kHz: // 125 kHz + bw = 125e3; + break; + case LORA_BANKWIDTH_250kHz: // 250 kHz + bw = 250e3; + break; + case LORA_BANKWIDTH_500kHz: // 500 kHz + bw = 500e3; + break; + } + + // Symbol rate : time for one symbol (secs) + double rs = bw / ( 1 << this->settings.LoRa.Datarate ); + double ts = 1 / rs; + // time of preamble + double tPreamble = ( this->settings.LoRa.PreambleLen + 4.25 ) * ts; + // Symbol length of payload and time + double tmp = ceil( ( 8 * pktLen - 4 * this->settings.LoRa.Datarate + + 28 + 16 * this->settings.LoRa.CrcOn - + ( this->settings.LoRa.FixLen ? 20 : 0 ) ) / + ( double )( 4 * ( this->settings.LoRa.Datarate - + ( ( this->settings.LoRa.LowDatarateOptimize > 0 ) ? 2 : 0 ) ) ) ) * + ( this->settings.LoRa.Coderate + 4 ); + double nPayload = 8 + ( ( tmp > 0 ) ? tmp : 0 ); + double tPayload = nPayload * ts; + // Time on air + double tOnAir = tPreamble + tPayload; + // return ms secs + airTime = floor( tOnAir * 1e3 + 0.999 ); + } + break; + } + return airTime; +} + +void SX1276::Send( void *buffer, int16_t size, void *header, int16_t hsize ) +{ + uint32_t txTimeout = 0; + + switch( this->settings.Modem ) + { + case MODEM_FSK: + { + this->settings.FskPacketHandler.NbBytes = 0; + this->settings.FskPacketHandler.Size = size + hsize; + + if( this->settings.Fsk.FixLen == false ) + { + uint8_t tmpsize = size + hsize; + WriteFifo( ( uint8_t* )&tmpsize, 1 ); + } + else + { + Write( REG_PAYLOADLENGTH, size + hsize); + } + + if( ( size + hsize > 0 ) && ( size + hsize <= 64 ) ) + { + this->settings.FskPacketHandler.ChunkSize = size + hsize; + } + else + { + if (header) { + WriteFifo( header, hsize ); + memcpy( rxtxBuffer, header, hsize ); + } + memcpy( rxtxBuffer+hsize, (uint8_t *)buffer+hsize, size ); + this->settings.FskPacketHandler.ChunkSize = 32; + } + + // Write payload buffer + if (header) + WriteFifo( header, hsize ); + WriteFifo( buffer, this->settings.FskPacketHandler.ChunkSize ); + this->settings.FskPacketHandler.NbBytes += this->settings.FskPacketHandler.ChunkSize; + txTimeout = this->settings.Fsk.TxTimeout; + } + break; + case MODEM_LORA: + { + if( this->settings.LoRa.IqInverted == true ) + { + Write( REG_LR_INVERTIQ, ( ( Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_ON ) ); + Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_ON ); + } + else + { + Write( REG_LR_INVERTIQ, ( ( Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF ) ); + Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_OFF ); + } + + this->settings.LoRaPacketHandler.Size = size + hsize; + + // Initializes the payload size + Write( REG_LR_PAYLOADLENGTH, size + hsize); + + // Full buffer used for Tx + Write( REG_LR_FIFOTXBASEADDR, 0 ); + Write( REG_LR_FIFOADDRPTR, 0 ); + + // FIFO operations can not take place in Sleep mode + if( ( Read( REG_OPMODE ) & ~RF_OPMODE_MASK ) == RF_OPMODE_SLEEP ) + { + Standby( ); + Sleep_ms( 1 ); + } + // Write payload buffer + if (header) + WriteFifo( header, hsize ); + WriteFifo( buffer, size ); + txTimeout = this->settings.LoRa.TxTimeout; + } + break; + } + + Tx( txTimeout ); +} + +void SX1276::Sleep( void ) +{ + SetTimeout(TXTimeoutTimer, NULL); + SetTimeout(RXTimeoutTimer, NULL); + + SetOpMode( RF_OPMODE_SLEEP ); + this->settings.State = RF_IDLE; +} + +void SX1276::Standby( void ) +{ + SetTimeout(TXTimeoutTimer, NULL); + SetTimeout(RXTimeoutTimer, NULL); + + SetOpMode( RF_OPMODE_STANDBY ); + this->settings.State = RF_IDLE; +} + +void SX1276::Rx( uint32_t timeout ) +{ + bool rxContinuous = false; + + switch( this->settings.Modem ) + { + case MODEM_FSK: + { + rxContinuous = this->settings.Fsk.RxContinuous; + + // DIO0=PayloadReady + // DIO1=FifoLevel + // DIO2=SyncAddr + // DIO3=FifoEmpty + // DIO4=Preamble + // DIO5=ModeReady + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RF_DIOMAPPING1_DIO0_MASK & + RF_DIOMAPPING1_DIO1_MASK & + RF_DIOMAPPING1_DIO2_MASK ) | + RF_DIOMAPPING1_DIO0_00 | + RF_DIOMAPPING1_DIO1_00 | + RF_DIOMAPPING1_DIO2_11 ); + + Write( REG_DIOMAPPING2, ( Read( REG_DIOMAPPING2 ) & RF_DIOMAPPING2_DIO4_MASK & + RF_DIOMAPPING2_MAP_MASK ) | + RF_DIOMAPPING2_DIO4_11 | + RF_DIOMAPPING2_MAP_PREAMBLEDETECT ); + + this->settings.FskPacketHandler.FifoThresh = Read( REG_FIFOTHRESH ) & 0x3F; + + Write( REG_RXCONFIG, RF_RXCONFIG_AFCAUTO_ON | RF_RXCONFIG_AGCAUTO_ON | RF_RXCONFIG_RXTRIGER_PREAMBLEDETECT ); + + this->settings.FskPacketHandler.PreambleDetected = false; + this->settings.FskPacketHandler.SyncWordDetected = false; + this->settings.FskPacketHandler.NbBytes = 0; + this->settings.FskPacketHandler.Size = 0; + } + break; + case MODEM_LORA: + { + if( this->settings.LoRa.IqInverted == true ) + { + Write( REG_LR_INVERTIQ, ( ( Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_ON | RFLR_INVERTIQ_TX_OFF ) ); + Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_ON ); + } + else + { + Write( REG_LR_INVERTIQ, ( ( Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF ) ); + Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_OFF ); + } + + // ERRATA 2.3 - Receiver Spurious Reception of a LoRa Signal + if( this->settings.LoRa.Bandwidth < LORA_BANKWIDTH_500kHz ) + { + Write( REG_LR_DETECTOPTIMIZE, Read( REG_LR_DETECTOPTIMIZE ) & 0x7F ); + Write( REG_LR_TEST30, 0x00 ); + switch( this->settings.LoRa.Bandwidth ) + { + case LORA_BANKWIDTH_7kHz: // 7.8 kHz + Write( REG_LR_TEST2F, 0x48 ); + SetChannel(this->settings.Channel + 7.81e3 ); + break; + case LORA_BANKWIDTH_10kHz: // 10.4 kHz + Write( REG_LR_TEST2F, 0x44 ); + SetChannel(this->settings.Channel + 10.42e3 ); + break; + case LORA_BANKWIDTH_15kHz: // 15.6 kHz + Write( REG_LR_TEST2F, 0x44 ); + SetChannel(this->settings.Channel + 15.62e3 ); + break; + case LORA_BANKWIDTH_20kHz: // 20.8 kHz + Write( REG_LR_TEST2F, 0x44 ); + SetChannel(this->settings.Channel + 20.83e3 ); + break; + case LORA_BANKWIDTH_31kHz: // 31.25 kHz + Write( REG_LR_TEST2F, 0x44 ); + SetChannel(this->settings.Channel + 31.25e3 ); + break; + case LORA_BANKWIDTH_41kHz: // 41.4 kHz + Write( REG_LR_TEST2F, 0x44 ); + SetChannel(this->settings.Channel + 41.67e3 ); + break; + case LORA_BANKWIDTH_62kHz: // 62.5 kHz + Write( REG_LR_TEST2F, 0x40 ); + break; + case LORA_BANKWIDTH_125kHz: // 125 kHz + Write( REG_LR_TEST2F, 0x40 ); + break; + case LORA_BANKWIDTH_250kHz: // 250 kHz + Write( REG_LR_TEST2F, 0x40 ); + break; + } + } + else + { + Write( REG_LR_DETECTOPTIMIZE, Read( REG_LR_DETECTOPTIMIZE ) | 0x80 ); + } + + rxContinuous = this->settings.LoRa.RxContinuous; + + if( this->settings.LoRa.FreqHopOn == true ) + { + Write( REG_LR_IRQFLAGSMASK, //RFLR_IRQFLAGS_RXTIMEOUT | + //RFLR_IRQFLAGS_RXDONE | + //RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + //RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=RxDone, DIO2=FhssChangeChannel + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK & RFLR_DIOMAPPING1_DIO2_MASK ) | RFLR_DIOMAPPING1_DIO0_00 | RFLR_DIOMAPPING1_DIO2_00 ); + } + else + { + Write( REG_LR_IRQFLAGSMASK, //RFLR_IRQFLAGS_RXTIMEOUT | + //RFLR_IRQFLAGS_RXDONE | + //RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=RxDone + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK ) | RFLR_DIOMAPPING1_DIO0_00 ); + } + Write( REG_LR_FIFORXBASEADDR, 0 ); + Write( REG_LR_FIFOADDRPTR, 0 ); + } + break; + } + + this->settings.State = RF_RX_RUNNING; + if( timeout != 0 ) + { + SetTimeout(RXTimeoutTimer, &SX1276::OnTimeoutIrq, timeout * 1e3); + } + + if( this->settings.Modem == MODEM_FSK ) + { + SetOpMode( RF_OPMODE_RECEIVER ); + + if( rxContinuous == false ) + { + SetTimeout(RXTimeoutSyncWordTimer, &SX1276::OnTimeoutIrq, this->settings.Fsk.RxSingleTimeout * 1e3); + } + } + else + { + if( rxContinuous == true ) + { + SetOpMode( RFLR_OPMODE_RECEIVER ); + } + else + { + SetOpMode( RFLR_OPMODE_RECEIVER_SINGLE ); + } + } +} + +bool SX1276::RxSignalPending() +{ + if (this->settings.State != RF_RX_RUNNING) + return false; + + switch( this->settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + if (Read(REG_LR_MODEMSTAT) & (RFLR_MODEMSTAT_SIGNAL_DETECTED|RFLR_MODEMSTAT_SIGNAL_SYNCRONIZED|RFLR_MODEMSTAT_HEADERINFO_VALID|RFLR_MODEMSTAT_MODEM_CLEAR)) + return true; + break; + } + return false; +} + +void SX1276::Tx( uint32_t timeout ) +{ + + switch( this->settings.Modem ) + { + case MODEM_FSK: + { + // DIO0=PacketSent + // DIO1=FifoEmpty + // DIO2=FifoFull + // DIO3=FifoEmpty + // DIO4=LowBat + // DIO5=ModeReady + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RF_DIOMAPPING1_DIO0_MASK & + RF_DIOMAPPING1_DIO1_MASK & + RF_DIOMAPPING1_DIO2_MASK ) | + RF_DIOMAPPING1_DIO1_01 ); + + Write( REG_DIOMAPPING2, ( Read( REG_DIOMAPPING2 ) & RF_DIOMAPPING2_DIO4_MASK & + RF_DIOMAPPING2_MAP_MASK ) ); + this->settings.FskPacketHandler.FifoThresh = Read( REG_FIFOTHRESH ) & 0x3F; + } + break; + case MODEM_LORA: + { + if( this->settings.LoRa.FreqHopOn == true ) + { + Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + //RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + //RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=TxDone, DIO2=FhssChangeChannel + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK & RFLR_DIOMAPPING1_DIO2_MASK ) | RFLR_DIOMAPPING1_DIO0_01 | RFLR_DIOMAPPING1_DIO2_00 ); + } + else + { + Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + //RFLR_IRQFLAGS_TXDONE | + RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL | + RFLR_IRQFLAGS_CADDETECTED ); + + // DIO0=TxDone + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO0_MASK ) | RFLR_DIOMAPPING1_DIO0_01 ); + } + } + break; + } + + this->settings.State = RF_TX_RUNNING; + SetTimeout(TXTimeoutTimer, &SX1276::OnTimeoutIrq, timeout * 1e3); + SetOpMode( RF_OPMODE_TRANSMITTER ); +} + +void SX1276::StartCad( void ) +{ + switch( this->settings.Modem ) + { + case MODEM_FSK: + { + + } + break; + case MODEM_LORA: + { + Write( REG_LR_IRQFLAGSMASK, RFLR_IRQFLAGS_RXTIMEOUT | + RFLR_IRQFLAGS_RXDONE | + RFLR_IRQFLAGS_PAYLOADCRCERROR | + RFLR_IRQFLAGS_VALIDHEADER | + RFLR_IRQFLAGS_TXDONE | + //RFLR_IRQFLAGS_CADDONE | + RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL // | + //RFLR_IRQFLAGS_CADDETECTED + ); + + // DIO3=CADDone + Write( REG_DIOMAPPING1, ( Read( REG_DIOMAPPING1 ) & RFLR_DIOMAPPING1_DIO3_MASK ) | RFLR_DIOMAPPING1_DIO3_00 ); + + this->settings.State = RF_CAD; + SetOpMode( RFLR_OPMODE_CAD ); + } + break; + default: + break; + } +} + +void SX1276::SetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time ) +{ + uint32_t timeout = ( uint32_t )( time * 1e6 ); + + SetChannel( freq ); + + SetTxConfig( MODEM_FSK, power, 0, 0, 4800, 0, 5, false, false, 0, 0, 0, timeout ); + + Write( REG_PACKETCONFIG2, ( Read( REG_PACKETCONFIG2 ) & RF_PACKETCONFIG2_DATAMODE_MASK ) ); + // Disable radio interrupts + Write( REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_11 | RF_DIOMAPPING1_DIO1_11 ); + Write( REG_DIOMAPPING2, RF_DIOMAPPING2_DIO4_10 | RF_DIOMAPPING2_DIO5_10 ); + + this->settings.State = RF_TX_RUNNING; + SetTimeout(TXTimeoutTimer, &SX1276::OnTimeoutIrq, timeout); + SetOpMode( RF_OPMODE_TRANSMITTER ); +} + +int16_t SX1276::MaxMTUSize( RadioModems_t modem ) +{ + int16_t mtuSize = 0; + + switch( modem ) + { + case MODEM_FSK: + mtuSize = RX_BUFFER_SIZE; + case MODEM_LORA: + mtuSize = RX_BUFFER_SIZE; + break; + default: + mtuSize = -1; + break; + } + return mtuSize; +} + +int16_t SX1276::GetRssi( RadioModems_t modem ) +{ + int16_t rssi = 0; + + switch( modem ) + { + case MODEM_FSK: + rssi = -( Read( REG_RSSIVALUE ) >> 1 ); + break; + case MODEM_LORA: + if( this->settings.Channel > RF_MID_BAND_THRESH ) + { + rssi = RSSI_OFFSET_HF + Read( REG_LR_RSSIVALUE ); + } + else + { + rssi = RSSI_OFFSET_LF + Read( REG_LR_RSSIVALUE ); + } + break; + default: + rssi = -1; + break; + } + return rssi; +} + +int32_t SX1276::GetFrequencyError(RadioModems_t modem ) +{ + int32_t val = 0; + + if (modem != MODEM_LORA) + return 0; + + val = (Read(REG_LR_FEIMSB) & 0b1111) << 16; // high word, 4 valid bits only + val |= (Read(REG_LR_FEIMID) << 8) | Read(REG_LR_FEILSB); // high byte, low byte + if (val & 0x8000) //sconvert ign bit + val |= 0xfff00000; + + int32_t bandwidth = 0; + for (int i = 0; i < (int)(sizeof(LoRaBandwidths) / sizeof(BandwidthMap)) -1; i++ ) { + if (LoRaBandwidths[i].RegValue == this->settings.LoRa.Bandwidth) { + bandwidth = LoRaBandwidths[i].bandwidth; + break; + } + } + if (!bandwidth) + return 0; + + float bandWidthkHz = (float)bandwidth/1000; + + int32_t hz = (((float)val * (float)(1<<24)) / ((float)XTAL_FREQ)) * (bandWidthkHz / 500.0); + + return hz; +} + + +void SX1276::SetOpMode( uint8_t opMode ) +{ + if( opMode == RF_OPMODE_SLEEP ) + { + SetAntSwLowPower( true ); + } + else + { + SetAntSwLowPower( false ); + SetAntSw( opMode ); + } + Write( REG_OPMODE, ( Read( REG_OPMODE ) & RF_OPMODE_MASK ) | opMode ); +} + +void SX1276::SetModem( RadioModems_t modem ) +{ + if( ( Read( REG_OPMODE ) & RFLR_OPMODE_LONGRANGEMODE_ON ) != 0 ) + { + this->settings.Modem = MODEM_LORA; + } + else + { + this->settings.Modem = MODEM_FSK; + } + + if( this->settings.Modem == modem ) + { + return; + } + + this->settings.Modem = modem; + switch( this->settings.Modem ) + { + default: + case MODEM_FSK: + Sleep( ); + Write( REG_OPMODE, ( Read( REG_OPMODE ) & RFLR_OPMODE_LONGRANGEMODE_MASK ) | RFLR_OPMODE_LONGRANGEMODE_OFF ); + + Write( REG_DIOMAPPING1, 0x00 ); + Write( REG_DIOMAPPING2, 0x30 ); // DIO5=ModeReady + break; + case MODEM_LORA: + Sleep( ); + Write( REG_OPMODE, ( Read( REG_OPMODE ) & RFLR_OPMODE_LONGRANGEMODE_MASK ) | RFLR_OPMODE_LONGRANGEMODE_ON ); + + Write( REG_DIOMAPPING1, 0x00 ); + Write( REG_DIOMAPPING2, 0x00 ); + break; + } +} + +void SX1276::SetMaxPayloadLength( RadioModems_t modem, uint8_t max ) +{ + this->SetModem( modem ); + + switch( modem ) + { + case MODEM_FSK: + if( this->settings.Fsk.FixLen == false ) + { + this->Write( REG_PAYLOADLENGTH, max ); + } + break; + case MODEM_LORA: + this->Write( REG_LR_PAYLOADMAXLENGTH, max ); + break; + } +} + +void SX1276::SetPublicNetwork( bool enable ) +{ + SetModem( MODEM_LORA ); + this->settings.LoRa.PublicNetwork = enable; + if( enable == true ) + { + // Change LoRa modem SyncWord + Write( REG_LR_SYNCWORD, LORA_MAC_PUBLIC_SYNCWORD ); + } + else + { + // Change LoRa modem SyncWord + Write( REG_LR_SYNCWORD, LORA_MAC_PRIVATE_SYNCWORD ); + } +} + + +void SX1276::OnTimeoutIrq( void ) +{ + switch( this->settings.State ) + { + case RF_RX_RUNNING: + if( this->settings.Modem == MODEM_FSK ) + { + this->settings.FskPacketHandler.PreambleDetected = false; + this->settings.FskPacketHandler.SyncWordDetected = false; + this->settings.FskPacketHandler.NbBytes = 0; + this->settings.FskPacketHandler.Size = 0; + + // Clear Irqs + Write( REG_IRQFLAGS1, RF_IRQFLAGS1_RSSI | + RF_IRQFLAGS1_PREAMBLEDETECT | + RF_IRQFLAGS1_SYNCADDRESSMATCH ); + Write( REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN ); + + if( this->settings.Fsk.RxContinuous == true ) + { + // Continuous mode restart Rx chain + Write( REG_RXCONFIG, Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK ); + SetTimeout(RXTimeoutSyncWordTimer, &SX1276::OnTimeoutIrq, this->settings.Fsk.RxSingleTimeout * 1e3); + } + else + { + this->settings.State = RF_IDLE; + SetTimeout(RXTimeoutSyncWordTimer, NULL); + } + } + if (this->RadioEvents && this->RadioEvents->RxTimeout) + { + this->RadioEvents->RxTimeout(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData); + } + break; + case RF_TX_RUNNING: + // Tx timeout shouldn't happen. + // But it has been observed that when it happens it is a result of a corrupted SPI transfer + // it depends on the platform design. + // + // The workaround is to put the radio in a known state. Thus, we re-initialize it. + // BEGIN WORKAROUND + + // Reset the radio + Reset( ); + + // Calibrate Rx chain + RxChainCalibration( ); + + // Initialize radio default values + SetOpMode( RF_OPMODE_SLEEP ); + + RadioRegistersInit( ); + + SetModem( MODEM_FSK ); + + // Restore previous network type setting. + SetPublicNetwork( this->settings.LoRa.PublicNetwork ); + // END WORKAROUND + + this->settings.State = RF_IDLE; + if (this->RadioEvents && this->RadioEvents->TxTimeout) + { + this->RadioEvents->TxTimeout(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData); + } + break; + default: + break; + } +} + +void SX1276::OnDio0Irq( void ) +{ + volatile uint8_t irqFlags = 0; + + switch( this->settings.State ) + { + case RF_RX_RUNNING: + //TimerStop( &RxTimeoutTimer ); + // RxDone interrupt + switch( this->settings.Modem ) + { + case MODEM_FSK: + if( this->settings.Fsk.CrcOn == true ) + { + irqFlags = Read( REG_IRQFLAGS2 ); + if( ( irqFlags & RF_IRQFLAGS2_CRCOK ) != RF_IRQFLAGS2_CRCOK ) + { + // Clear Irqs + Write( REG_IRQFLAGS1, RF_IRQFLAGS1_RSSI | + RF_IRQFLAGS1_PREAMBLEDETECT | + RF_IRQFLAGS1_SYNCADDRESSMATCH ); + Write( REG_IRQFLAGS2, RF_IRQFLAGS2_FIFOOVERRUN ); + + SetTimeout(RXTimeoutTimer, NULL); + + if( this->settings.Fsk.RxContinuous == false ) + { + SetTimeout(RXTimeoutSyncWordTimer, NULL); + this->settings.State = RF_IDLE; + } + else + { + // Continuous mode restart Rx chain + Write( REG_RXCONFIG, Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK ); + SetTimeout(RXTimeoutSyncWordTimer, &SX1276::OnTimeoutIrq, this->settings.Fsk.RxSingleTimeout * 1e3); + } + + if (this->RadioEvents && this->RadioEvents->RxError) + { + this->RadioEvents->RxError(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData); + } + this->settings.FskPacketHandler.PreambleDetected = false; + this->settings.FskPacketHandler.SyncWordDetected = false; + this->settings.FskPacketHandler.NbBytes = 0; + this->settings.FskPacketHandler.Size = 0; + break; + } + } + + // Read received packet size + if( ( this->settings.FskPacketHandler.Size == 0 ) && ( this->settings.FskPacketHandler.NbBytes == 0 ) ) + { + if( this->settings.Fsk.FixLen == false ) + { + ReadFifo( ( uint8_t* )&this->settings.FskPacketHandler.Size, 1 ); + } + else + { + this->settings.FskPacketHandler.Size = Read( REG_PAYLOADLENGTH ); + } + ReadFifo( rxtxBuffer + this->settings.FskPacketHandler.NbBytes, this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + this->settings.FskPacketHandler.NbBytes += ( this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + } + else + { + ReadFifo( rxtxBuffer + this->settings.FskPacketHandler.NbBytes, this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + this->settings.FskPacketHandler.NbBytes += ( this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + } + + SetTimeout(RXTimeoutTimer, NULL); + + if( this->settings.Fsk.RxContinuous == false ) + { + this->settings.State = RF_IDLE; + SetTimeout(RXTimeoutSyncWordTimer, NULL); + } + else + { + // Continuous mode restart Rx chain + Write( REG_RXCONFIG, Read( REG_RXCONFIG ) | RF_RXCONFIG_RESTARTRXWITHOUTPLLLOCK ); + SetTimeout(RXTimeoutSyncWordTimer, &SX1276::OnTimeoutIrq, this->settings.Fsk.RxSingleTimeout * 1e3); + } + + if (this->RadioEvents && this->RadioEvents->RxDone) + { + this->RadioEvents->RxDone(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData, rxtxBuffer, this->settings.FskPacketHandler.Size, this->settings.FskPacketHandler.RssiValue, 0 ); + } + this->settings.FskPacketHandler.PreambleDetected = false; + this->settings.FskPacketHandler.SyncWordDetected = false; + this->settings.FskPacketHandler.NbBytes = 0; + this->settings.FskPacketHandler.Size = 0; + break; + case MODEM_LORA: + { + int8_t snr = 0; + + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXDONE ); + + irqFlags = Read( REG_LR_IRQFLAGS ); + if( ( irqFlags & RFLR_IRQFLAGS_PAYLOADCRCERROR_MASK ) == RFLR_IRQFLAGS_PAYLOADCRCERROR ) + { + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_PAYLOADCRCERROR ); + + if( this->settings.LoRa.RxContinuous == false ) + { + this->settings.State = RF_IDLE; + } + SetTimeout(RXTimeoutTimer, NULL); + + if(this->RadioEvents && this->RadioEvents->RxError) + { + this->RadioEvents->RxError(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData); + } + break; + } + + this->settings.LoRaPacketHandler.SnrValue = Read( REG_LR_PKTSNRVALUE ); + if( this->settings.LoRaPacketHandler.SnrValue & 0x80 ) // The SNR sign bit is 1 + { + // Invert and divide by 4 + snr = ( ( ~this->settings.LoRaPacketHandler.SnrValue + 1 ) & 0xFF ) >> 2; + snr = -snr; + } + else + { + // Divide by 4 + snr = ( this->settings.LoRaPacketHandler.SnrValue & 0xFF ) >> 2; + } + + int16_t rssi = Read( REG_LR_PKTRSSIVALUE ); + if( snr < 0 ) + { + if( this->settings.Channel > RF_MID_BAND_THRESH ) + { + this->settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_HF + rssi + ( rssi >> 4 ) + + snr; + } + else + { + this->settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_LF + rssi + ( rssi >> 4 ) + + snr; + } + } + else + { + if( this->settings.Channel > RF_MID_BAND_THRESH ) + { + this->settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_HF + rssi + ( rssi >> 4 ); + } + else + { + this->settings.LoRaPacketHandler.RssiValue = RSSI_OFFSET_LF + rssi + ( rssi >> 4 ); + } + } + + this->settings.LoRaPacketHandler.Size = Read( REG_LR_RXNBBYTES ); + ReadFifo( rxtxBuffer, this->settings.LoRaPacketHandler.Size ); + + if( this->settings.LoRa.RxContinuous == false ) + { + this->settings.State = RF_IDLE; + } + SetTimeout(RXTimeoutTimer, NULL); + + if(this->RadioEvents && this->RadioEvents->RxDone) + { + this->RadioEvents->RxDone(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData, rxtxBuffer, this->settings.LoRaPacketHandler.Size, this->settings.LoRaPacketHandler.RssiValue, this->settings.LoRaPacketHandler.SnrValue ); + } + } + break; + default: + break; + } + break; + case RF_TX_RUNNING: + SetTimeout(TXTimeoutTimer, NULL); + // TxDone interrupt + switch( this->settings.Modem ) + { + case MODEM_LORA: + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_TXDONE ); + // Intentional fall through + case MODEM_FSK: + default: + this->settings.State = RF_IDLE; + if (this->RadioEvents && this->RadioEvents->TxDone) + { + this->RadioEvents->TxDone(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData); + } + break; + } + break; + default: + break; + } +} + +void SX1276::OnDio1Irq( void ) +{ + switch( this->settings.State ) + { + case RF_RX_RUNNING: + switch( this->settings.Modem ) + { + case MODEM_FSK: + // FifoLevel interrupt + // Read received packet size + if( ( this->settings.FskPacketHandler.Size == 0 ) && ( this->settings.FskPacketHandler.NbBytes == 0 ) ) + { + if( this->settings.Fsk.FixLen == false ) + { + ReadFifo( ( uint8_t* )&this->settings.FskPacketHandler.Size, 1 ); + } + else + { + this->settings.FskPacketHandler.Size = Read( REG_PAYLOADLENGTH ); + } + } + + if( ( this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ) > this->settings.FskPacketHandler.FifoThresh ) + { + ReadFifo( ( rxtxBuffer + this->settings.FskPacketHandler.NbBytes ), this->settings.FskPacketHandler.FifoThresh ); + this->settings.FskPacketHandler.NbBytes += this->settings.FskPacketHandler.FifoThresh; + } + else + { + ReadFifo( ( rxtxBuffer + this->settings.FskPacketHandler.NbBytes ), this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + this->settings.FskPacketHandler.NbBytes += ( this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + } + break; + case MODEM_LORA: + // Sync time out + SetTimeout(RXTimeoutTimer, NULL); + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_RXTIMEOUT ); + + this->settings.State = RF_IDLE; + if (this->RadioEvents && this->RadioEvents->RxTimeout) + { + this->RadioEvents->RxTimeout(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData); + } + break; + default: + break; + } + break; + case RF_TX_RUNNING: + switch( this->settings.Modem ) + { + case MODEM_FSK: + // FifoEmpty interrupt + if( ( this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ) > this->settings.FskPacketHandler.ChunkSize ) + { + WriteFifo( ( rxtxBuffer + this->settings.FskPacketHandler.NbBytes ), this->settings.FskPacketHandler.ChunkSize ); + this->settings.FskPacketHandler.NbBytes += this->settings.FskPacketHandler.ChunkSize; + } + else + { + // Write the last chunk of data + WriteFifo( rxtxBuffer + this->settings.FskPacketHandler.NbBytes, this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes ); + this->settings.FskPacketHandler.NbBytes += this->settings.FskPacketHandler.Size - this->settings.FskPacketHandler.NbBytes; + } + break; + case MODEM_LORA: + break; + default: + break; + } + break; + default: + break; + } +} + +void SX1276::OnDio2Irq( void ) +{ + switch( this->settings.State ) + { + case RF_RX_RUNNING: + switch( this->settings.Modem ) + { + case MODEM_FSK: + // Checks if DIO4 is connected. If it is not PreambleDtected is set to true. + if( this->dioIrq[4] == NULL ) + { + this->settings.FskPacketHandler.PreambleDetected = true; + } + + if( ( this->settings.FskPacketHandler.PreambleDetected == true ) && ( this->settings.FskPacketHandler.SyncWordDetected == false ) ) + { + SetTimeout(RXTimeoutSyncWordTimer, NULL); + + this->settings.FskPacketHandler.SyncWordDetected = true; + + this->settings.FskPacketHandler.RssiValue = -( Read( REG_RSSIVALUE ) >> 1 ); + + this->settings.FskPacketHandler.AfcValue = ( int32_t )( double )( ( ( uint16_t )Read( REG_AFCMSB ) << 8 ) | + ( uint16_t )Read( REG_AFCLSB ) ) * + ( double )FREQ_STEP; + this->settings.FskPacketHandler.RxGain = ( Read( REG_LNA ) >> 5 ) & 0x07; + } + break; + case MODEM_LORA: + if( this->settings.LoRa.FreqHopOn == true ) + { + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL ); + + if (this->RadioEvents && this->RadioEvents->FhssChangeChannel) + { + this->RadioEvents->FhssChangeChannel(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData, ( Read( REG_LR_HOPCHANNEL ) & RFLR_HOPCHANNEL_CHANNEL_MASK ) ); + } + } + break; + default: + break; + } + break; + case RF_TX_RUNNING: + switch( this->settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + if( this->settings.LoRa.FreqHopOn == true ) + { + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_FHSSCHANGEDCHANNEL ); + + if (this->RadioEvents && this->RadioEvents->FhssChangeChannel) + { + this->RadioEvents->FhssChangeChannel(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData, ( Read( REG_LR_HOPCHANNEL ) & RFLR_HOPCHANNEL_CHANNEL_MASK ) ); + } + } + break; + default: + break; + } + break; + default: + break; + } +} + +void SX1276::OnDio3Irq( void ) +{ + switch( this->settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + if( ( Read( REG_LR_IRQFLAGS ) & RFLR_IRQFLAGS_CADDETECTED ) == RFLR_IRQFLAGS_CADDETECTED ) + { + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_CADDETECTED | RFLR_IRQFLAGS_CADDONE ); + if (this->RadioEvents && this->RadioEvents->CadDone) + { + this->RadioEvents->CadDone(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData, true ); + } + } + else + { + // Clear Irq + Write( REG_LR_IRQFLAGS, RFLR_IRQFLAGS_CADDONE ); + if (this->RadioEvents && this->RadioEvents->CadDone) + { + this->RadioEvents->CadDone(this, this->RadioEvents->userThisPtr, this->RadioEvents->userData, false ); + } + } + break; + default: + break; + } +} + +void SX1276::OnDio4Irq( void ) +{ + switch( this->settings.Modem ) + { + case MODEM_FSK: + { + if( this->settings.FskPacketHandler.PreambleDetected == false ) + { + this->settings.FskPacketHandler.PreambleDetected = true; + } + } + break; + case MODEM_LORA: + break; + default: + break; + } +} + +void SX1276::OnDio5Irq( void ) +{ + switch( this->settings.Modem ) + { + case MODEM_FSK: + break; + case MODEM_LORA: + break; + default: + break; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SX1276GenericLib/sx1276/sx1276.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,642 @@ +/* + / _____) _ | | +( (____ _____ ____ _| |_ _____ ____| |__ + \____ \| ___ | (_ _) ___ |/ ___) _ \ + _____) ) ____| | | || |_| ____( (___| | | | +(______/|_____)_|_|_| \__)_____)\____)_| |_| + (C) 2014 Semtech + +Description: Actual implementation of a SX1276 radio, inherits Radio + +License: Revised BSD License, see LICENSE.TXT file include in the project + +Maintainers: Miguel Luis, Gregory Cristian and Nicolas Huguenin +*/ + +/* + * additional development to make it more generic across multiple OS versions + * (c) 2017 Helmut Tschemernjak + * 30826 Garbsen (Hannover) Germany + */ + +#ifndef __SX1276_H__ +#define __SX1276_H__ + +#include "radio.h" +#include "sx1276Regs-Fsk.h" +#include "sx1276Regs-LoRa.h" + + + +/*! + * Radio wake-up time from sleep + */ +#define RADIO_WAKEUP_TIME 1 // [ms] + +/*! + * Sync word for Private LoRa networks + */ +#define LORA_MAC_PRIVATE_SYNCWORD 0x12 + +/*! + * Sync word for Public LoRa networks + */ +#define LORA_MAC_PUBLIC_SYNCWORD 0x34 + + +/*! + * SX1276 definitions + */ +#define XTAL_FREQ 32000000 +#define FREQ_STEP 61.03515625 + +#define RX_BUFFER_SIZE 256 + +/*! + * Constant values need to compute the RSSI value + */ +#define RSSI_OFFSET_LF -164.0 +#define RSSI_OFFSET_HF -157.0 + +#define RF_MID_BAND_THRESH 525000000 + + + + +/*! + * Type of the supported board. [SX1276MB1MAS / SX1276MB1LAS] + */ +typedef enum BoardType +{ + SX1276MB1MAS = 0, + SX1276MB1LAS, + RFM95_SX1276, + MURATA_SX1276, + UNKNOWN +}BoardType_t; + + +typedef enum { + LORA_SF6 = 6, // 64 chips/symbol, SF6 requires an TCXO! + LORA_SF7 = 7, // 128 chips/symbol + LORA_SF8 = 8, // 256 chips/symbol + LORA_SF9 = 9, // 512 chips/symbol + LORA_SF10 = 10, // 1024 chips/symbol + LORA_SF11 = 11, // 2048 chips/symbol + LORA_SF12 = 12, // 4096 chips/symbol +} lora_spreading_factor_t; + + +typedef enum { // cyclic error coding to perform forward error detection and correction + LORA_ERROR_CODING_RATE_4_5 = 1, // 1.25x overhead + LORA_ERROR_CODING_RATE_4_6 = 2, // 1.50x overhead + LORA_ERROR_CODING_RATE_4_7 = 3, // 1.75x overhead + LORA_ERROR_CODING_RATE_4_8 = 4, // 2.00x overhead +} lora_coding_rate_t; + + +typedef enum { + RF_FREQUENCY_868_0 = 868000000, // Hz + RF_FREQUENCY_868_1 = 868100000, // Hz + RF_FREQUENCY_868_3 = 868300000, // Hz + RF_FREQUENCY_868_5 = 868500000, // Hz +} rf_frequency_t; + + + +/*! + * Actual implementation of a SX1276 radio, inherits Radio + */ +class SX1276 : public Radio +{ +protected: + + bool isRadioActive; + + BoardType_t boardConnected; //1 = SX1276MB1LAS; 0 = SX1276MB1MAS + + uint8_t *rxtxBuffer; + + /*! + * Hardware IO IRQ callback function definition + */ + typedef void ( SX1276::*DioIrqHandler )( void ); + + /*! + * Hardware DIO IRQ functions + */ + DioIrqHandler *dioIrq; + + + + RadioSettings_t settings; + + /*! + * FSK bandwidth definition + */ + struct BandwidthMap { + uint32_t bandwidth; + uint8_t RegValue; + }; + static const struct BandwidthMap FskBandwidths[]; + static const struct BandwidthMap LoRaBandwidths[]; + +protected: + + /*! + * Performs the Rx chain calibration for LF and HF bands + * \remark Must be called just after the reset so all registers are at their + * default values + */ + void RxChainCalibration( void ); + +public: + SX1276( RadioEvents_t *events); + virtual ~SX1276( ); + + + + + //------------------------------------------------------------------------- + // Redefined Radio functions + //------------------------------------------------------------------------- + /*! + * @brief Return current radio status, returns true if a radios has been found. + * + * @param [IN] events Structure containing the driver callback functions + */ + virtual bool Init( RadioEvents_t *events ); + + /*! + * @brief Initializes the radio registers + */ + virtual void RadioRegistersInit(void); + + /*! + * Return current radio status + * + * @param status Radio status. [RF_IDLE, RX_RUNNING, TX_RUNNING, CAD_RUNNING] + */ + virtual RadioState GetStatus( void ); + + /*! + * @brief Configures the SX1276 with the given modem + * + * @param [IN] modem Modem to be used [0: FSK, 1: LoRa] + */ + virtual void SetModem( RadioModems_t modem ); + + /*! + * @brief Sets the channel frequency + * + * @param [IN] freq Channel RF frequency + */ + virtual void SetChannel( uint32_t freq ); + + /*! + * @brief Sets the channels configuration + * + * @param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * @param [IN] freq Channel RF frequency + * @param [IN] rssiThresh RSSI threshold + * + * @retval isFree [true: Channel is free, false: Channel is not free] + */ + virtual bool IsChannelFree( RadioModems_t modem, uint32_t freq, int16_t rssiThresh ); + + /*! + * @brief Generates a 32 bits random value based on the RSSI readings + * + * \remark This function sets the radio in LoRa modem mode and disables + * all interrupts. + * After calling this function either Radio.SetRxConfig or + * Radio.SetTxConfig functions must be called. + * + * @retval randomValue 32 bits random value + */ + virtual uint32_t Random( void ); + + /*! + * @brief Sets the reception parameters + * + * @param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * @param [IN] bandwidth Sets the bandwidth + * FSK : >= 2600 and <= 250000 Hz + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * @param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * @param [IN] coderate Sets the coding rate ( LoRa only ) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * @param [IN] bandwidthAfc Sets the AFC Bandwidth ( FSK only ) + * FSK : >= 2600 and <= 250000 Hz + * LoRa: N/A ( set to 0 ) + * @param [IN] preambleLen Sets the Preamble length ( LoRa only ) + * FSK : N/A ( set to 0 ) + * LoRa: Length in symbols ( the hardware adds 4 more symbols ) + * @param [IN] symbTimeout Sets the RxSingle timeout value + * FSK : timeout number of bytes + * LoRa: timeout in symbols + * @param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * @param [IN] payloadLen Sets payload length when fixed lenght is used + * @param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON] + * @param [IN] freqHopOn Enables disables the intra-packet frequency hopping [0: OFF, 1: ON] (LoRa only) + * @param [IN] hopPeriod Number of symbols bewteen each hop (LoRa only) + * @param [IN] iqInverted Inverts IQ signals ( LoRa only ) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * @param [IN] rxContinuous Sets the reception in continuous mode + * [false: single mode, true: continuous mode] + */ + virtual void SetRxConfig ( RadioModems_t modem, uint32_t bandwidth, + uint32_t datarate, uint8_t coderate, + uint32_t bandwidthAfc, uint16_t preambleLen, + uint16_t symbTimeout, bool fixLen, + uint8_t payloadLen, + bool crcOn, bool freqHopOn, uint8_t hopPeriod, + bool iqInverted, bool rxContinuous ); + + /*! + * @brief Sets the transmission parameters + * + * @param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * @param [IN] power Sets the output power [dBm] + * @param [IN] fdev Sets the frequency deviation ( FSK only ) + * FSK : [Hz] + * LoRa: 0 + * @param [IN] bandwidth Sets the bandwidth ( LoRa only ) + * FSK : 0 + * LoRa: [0: 125 kHz, 1: 250 kHz, + * 2: 500 kHz, 3: Reserved] + * @param [IN] datarate Sets the Datarate + * FSK : 600..300000 bits/s + * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, + * 10: 1024, 11: 2048, 12: 4096 chips] + * @param [IN] coderate Sets the coding rate ( LoRa only ) + * FSK : N/A ( set to 0 ) + * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] + * @param [IN] preambleLen Sets the preamble length + * @param [IN] fixLen Fixed length packets [0: variable, 1: fixed] + * @param [IN] crcOn Enables disables the CRC [0: OFF, 1: ON] + * @param [IN] freqHopOn Enables disables the intra-packet frequency hopping [0: OFF, 1: ON] (LoRa only) + * @param [IN] hopPeriod Number of symbols bewteen each hop (LoRa only) + * @param [IN] iqInverted Inverts IQ signals ( LoRa only ) + * FSK : N/A ( set to 0 ) + * LoRa: [0: not inverted, 1: inverted] + * @param [IN] timeout Transmission timeout [ms] + */ + virtual void SetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev, + uint32_t bandwidth, uint32_t datarate, + uint8_t coderate, uint16_t preambleLen, + bool fixLen, bool crcOn, bool freqHopOn, + uint8_t hopPeriod, bool iqInverted, uint32_t timeout ); + + + /*! + * @brief Checks if the given RF frequency is supported by the hardware + * + * @param [IN] frequency RF frequency to be checked + * @retval isSupported [true: supported, false: unsupported] + */ + virtual bool CheckRfFrequency( uint32_t frequency ) = 0; + + /*! + * @brief Computes the packet time on air for the given payload + * + * \Remark Can only be called once SetRxConfig or SetTxConfig have been called + * + * @param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * @param [IN] pktLen Packet payload length + * + * @retval airTime Computed airTime for the given packet payload length + */ + virtual uint32_t TimeOnAir ( RadioModems_t modem, int16_t pktLen ); + + /*! + * @brief Sends the buffer of size. Prepares the packet to be sent and sets + * the radio in transmission + * + * @param [IN]: buffer Buffer pointer + * @param [IN]: size Buffer size + * @param [IN]: buffer Header pointer + * @param [IN]: size Header size + */ + virtual void Send(void *buffer, int16_t size, void *header = NULL, int16_t hsize = 0); + + /*! + * @brief Sets the radio in sleep mode + */ + virtual void Sleep( void ); + + /*! + * @brief Sets the radio in standby mode + */ + virtual void Standby( void ); + + /*! + * @brief Sets the radio in CAD mode + */ + virtual void StartCad( void ); + + /*! + * @brief Sets the radio in reception mode for the given time + * @param [IN] timeout Reception timeout [ms] + * [0: continuous, others timeout] + */ + virtual void Rx( uint32_t timeout ); + + /*! + * @brief Check is radio receives a signal + */ + virtual bool RxSignalPending(); + + + /*! + * @brief Sets the radio in transmission mode for the given time + * @param [IN] timeout Transmission timeout [ms] + * [0: continuous, others timeout] + */ + virtual void Tx( uint32_t timeout ); + + /*! + * @brief Sets the radio in continuous wave transmission mode + * + * @param [IN]: freq Channel RF frequency + * @param [IN]: power Sets the output power [dBm] + * @param [IN]: time Transmission mode timeout [s] + */ + + virtual void SetTxContinuousWave( uint32_t freq, int8_t power, uint16_t time ); + + /*! + * @brief Returns the maximal transfer unit for a given modem + * + * @retval MTU size in bytes + */ + virtual int16_t MaxMTUSize( RadioModems_t modem ); + + /*! + * @brief Reads the current RSSI value + * + * @retval rssiValue Current RSSI value in [dBm] + */ + virtual int16_t GetRssi ( RadioModems_t modem ); + + /*! + * @brief Reads the current frequency error + * + * @retval frequency error value in [Hz] + */ + virtual int32_t GetFrequencyError( RadioModems_t modem ); + + /*! + * @brief Writes the radio register at the specified address + * + * @param [IN]: addr Register address + * @param [IN]: data New register value + */ + virtual void Write ( uint8_t addr, uint8_t data ) = 0; + + /*! + * @brief Reads the radio register at the specified address + * + * @param [IN]: addr Register address + * @retval data Register value + */ + virtual uint8_t Read ( uint8_t addr ) = 0; + + /*! + * @brief Writes multiple radio registers starting at address + * + * @param [IN] addr First Radio register address + * @param [IN] buffer Buffer containing the new register's values + * @param [IN] size Number of registers to be written + */ + virtual void Write( uint8_t addr, void *buffer, uint8_t size ) = 0; + + /*! + * @brief Reads multiple radio registers starting at address + * + * @param [IN] addr First Radio register address + * @param [OUT] buffer Buffer where to copy the registers data + * @param [IN] size Number of registers to be read + */ + virtual void Read ( uint8_t addr, void *buffer, uint8_t size ) = 0; + + /*! + * @brief Writes the buffer contents to the SX1276 FIFO + * + * @param [IN] buffer Buffer containing data to be put on the FIFO. + * @param [IN] size Number of bytes to be written to the FIFO + */ + virtual void WriteFifo( void *buffer, uint8_t size ) = 0; + + /*! + * @brief Reads the contents of the SX1276 FIFO + * + * @param [OUT] buffer Buffer where to copy the FIFO read data. + * @param [IN] size Number of bytes to be read from the FIFO + */ + virtual void ReadFifo( void *buffer, uint8_t size ) = 0; + /*! + * @brief Resets the SX1276 + */ + virtual void Reset( void ) = 0; + + /*! + * @brief Sets the maximum payload length. + * + * @param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] + * @param [IN] max Maximum payload length in bytes + */ + virtual void SetMaxPayloadLength( RadioModems_t modem, uint8_t max ); + + /*! + * \brief Sets the network to public or private. Updates the sync byte. + * + * \remark Applies to LoRa modem only + * + * \param [IN] enable if true, it enables a public network + */ + virtual void SetPublicNetwork( bool enable ); + + /*! + * @brief Sets the radio output power. + * + * @param [IN] power Sets the RF output power + */ + virtual void SetRfTxPower( int8_t power ) = 0; + + //------------------------------------------------------------------------- + // Board relative functions + //------------------------------------------------------------------------- + /*! + * Radio registers definition + */ + struct RadioRegisters { + ModemType Modem; + uint8_t Addr; + uint8_t Value; + }; + + + static const struct RadioRegisters RadioRegsInit[]; + + typedef enum { + RXTimeoutTimer, + TXTimeoutTimer, + RXTimeoutSyncWordTimer + } TimeoutTimer_t; + + +protected: + /*! + * @brief Initializes the radio I/Os pins interface + */ + virtual void IoInit( void ) = 0; + + /*! + * @brief Initializes the radio SPI + */ + virtual void SpiInit( void ) = 0; + + /*! + * @brief Initializes DIO IRQ handlers + * + * @param [IN] irqHandlers Array containing the IRQ callback functions + */ + virtual void IoIrqInit( DioIrqHandler *irqHandlers ) = 0; + + /*! + * @brief De-initializes the radio I/Os pins interface. + * + * \remark Useful when going in MCU lowpower modes + */ + virtual void IoDeInit( void ) = 0; + + /*! + * @brief Gets the board PA selection configuration + * + * @param [IN] channel Channel frequency in Hz + * @retval PaSelect RegPaConfig PaSelect value + */ + virtual uint8_t GetPaSelect( uint32_t channel ) = 0; + + /*! + * @brief Set the RF Switch I/Os pins in Low Power mode + * + * @param [IN] status enable or disable + */ + virtual void SetAntSwLowPower( bool status ) = 0; + + /*! + * @brief Initializes the RF Switch I/Os pins interface + */ + virtual void AntSwInit( void ) = 0; + + /*! + * @brief De-initializes the RF Switch I/Os pins interface + * + * \remark Needed to decrease the power consumption in MCU lowpower modes + */ + virtual void AntSwDeInit( void ) = 0; + + /*! + * @brief Controls the antenna switch if necessary. + * + * \remark see errata note + * + * @param [IN] opMode Current radio operating mode + */ + virtual void SetAntSw( uint8_t opMode ) = 0; + + typedef void ( SX1276::*timeoutFuncPtr)( void ); + + + /* + * The the Timeout for a given Timer. + */ + virtual void SetTimeout(TimeoutTimer_t timer, timeoutFuncPtr, int timeout_ms = 0) = 0; + + /* + * A simple ms sleep + */ + virtual void Sleep_ms(int ms) = 0; + +protected: + + /*! + * @brief Sets the SX1276 operating mode + * + * @param [IN] opMode New operating mode + */ + virtual void SetOpMode( uint8_t opMode ); + + /* + * SX1276 DIO IRQ callback functions prototype + */ + + /*! + * @brief DIO 0 IRQ callback + */ + virtual void OnDio0Irq( void ); + + /*! + * @brief DIO 1 IRQ callback + */ + virtual void OnDio1Irq( void ); + + /*! + * @brief DIO 2 IRQ callback + */ + virtual void OnDio2Irq( void ); + + /*! + * @brief DIO 3 IRQ callback + */ + virtual void OnDio3Irq( void ); + + /*! + * @brief DIO 4 IRQ callback + */ + virtual void OnDio4Irq( void ); + + /*! + * @brief DIO 5 IRQ callback + */ + virtual void OnDio5Irq( void ); + + /*! + * @brief Tx & Rx timeout timer callback + */ + virtual void OnTimeoutIrq( void ); + + /*! + * Returns the known FSK bandwidth registers value + * + * \param [IN] bandwidth Bandwidth value in Hz + * \retval regValue Bandwidth register value. + */ + static uint8_t GetFskBandwidthRegValue( uint32_t bandwidth ); + + static uint8_t GetLoRaBandwidthRegValue( uint32_t bandwidth ); + + enum { + LORA_BANKWIDTH_7kHz = 0, // 7.8 kHz requires TCXO + LORA_BANKWIDTH_10kHz = 1, // 10.4 kHz requires TCXO + LORA_BANKWIDTH_15kHz = 2, // 15.6 kHz requires TCXO + LORA_BANKWIDTH_20kHz = 3, // 20.8 kHz requires TCXO + LORA_BANKWIDTH_31kHz = 4, // 31.2 kHz requires TCXO + LORA_BANKWIDTH_41kHz = 5, // 41.4 kHz requires TCXO + LORA_BANKWIDTH_62kHz = 6, // 62.5 kHz requires TCXO + LORA_BANKWIDTH_125kHz = 7, + LORA_BANKWIDTH_250kHz = 8, + LORA_BANKWIDTH_500kHz = 9, + LORA_BANKWIDTH_RESERVED = 10, + }; +}; + +#endif // __SX1276_H__
--- a/SX1276GenericPingPong/GenericPingPong.cpp Fri Aug 18 07:45:44 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,414 +0,0 @@ -/* - * This file contains a copy of the master content sx1276PingPong - * with adaption for the SX1276Generic environment - * (c) 2017 Helmut Tschemernjak - * 30826 Garbsen (Hannover) Germany - */ - -#include "mbed.h" -#include "PinMap.h" -#include "GenericPingPong.h" -#include "sx1276-mbed-hal.h" -#include "main.h" - -#ifdef FEATURE_LORA - -/* Set this flag to '1' to display debug messages on the console */ -#define DEBUG_MESSAGE 1 - -/* Set this flag to '1' to use the LoRa modulation or to '0' to use FSK modulation */ -#define USE_MODEM_LORA 1 -#define USE_MODEM_FSK !USE_MODEM_LORA -#define RF_FREQUENCY RF_FREQUENCY_868_1 // Hz -#define TX_OUTPUT_POWER 14 // 14 dBm - -#if USE_MODEM_LORA == 1 - -#define LORA_BANDWIDTH 125000 // LoRa default, details in SX1276::BandwidthMap -#define LORA_SPREADING_FACTOR LORA_SF7 -#define LORA_CODINGRATE LORA_ERROR_CODING_RATE_4_5 - -#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx -#define LORA_SYMBOL_TIMEOUT 5 // Symbols -#define LORA_FIX_LENGTH_PAYLOAD_ON false -#define LORA_FHSS_ENABLED false -#define LORA_NB_SYMB_HOP 4 -#define LORA_IQ_INVERSION_ON false -#define LORA_CRC_ENABLED true - -#elif USE_MODEM_FSK == 1 - -#define FSK_FDEV 25000 // Hz -#define FSK_DATARATE 19200 // bps -#define FSK_BANDWIDTH 50000 // Hz -#define FSK_AFC_BANDWIDTH 83333 // Hz -#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx -#define FSK_FIX_LENGTH_PAYLOAD_ON false -#define FSK_CRC_ENABLED true - -#else - #error "Please define a modem in the compiler options." -#endif - - -#define RX_TIMEOUT_VALUE 3500 // in ms - -//#define BUFFER_SIZE 32 // Define the payload size here -#define BUFFER_SIZE 64 // Define the payload size here - -/* - * Global variables declarations - */ -typedef enum -{ - LOWPOWER = 0, - IDLE, - - RX, - RX_TIMEOUT, - RX_ERROR, - - TX, - TX_TIMEOUT, - - CAD, - CAD_DONE -} AppStates_t; - -volatile AppStates_t State = LOWPOWER; - -/*! - * Radio events function pointer - */ -static RadioEvents_t RadioEvents; - -/* - * Global variables declarations - */ -SX1276Generic *Radio; - - -const uint8_t PingMsg[] = { 0xff, 0xff, 0x00, 0x00, 'P', 'I', 'N', 'G'};// "PING"; -const uint8_t PongMsg[] = { 0xff, 0xff, 0x00, 0x00, 'P', 'O', 'N', 'G'};// "PONG"; - -uint16_t BufferSize = BUFFER_SIZE; -uint8_t *Buffer; - -DigitalOut *led3; - - -int SX1276PingPong() -{ -#if( defined ( TARGET_KL25Z ) || defined ( TARGET_LPC11U6X ) ) - DigitalOut *led = new DigitalOut(LED2); -#elif defined(TARGET_NUCLEO_L073RZ) || defined(TARGET_DISCO_L072CZ_LRWAN1) - DigitalOut *led = new DigitalOut(LED4); // RX red - led3 = new DigitalOut(LED3); // TX blue -#else - DigitalOut *led = new DigitalOut(LED1); - led3 = led; -#endif - - Buffer = new uint8_t[BUFFER_SIZE]; - *led3 = 1; - -#ifdef B_L072Z_LRWAN1_LORA - Radio = new SX1276Generic(NULL, MURATA_SX1276, - LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, - LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5, - LORA_ANT_RX, LORA_ANT_TX, LORA_ANT_BOOST, LORA_TCXO); -#else // RFM95 - Radio = new SX1276Generic(NULL, RFM95_SX1276, - LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, - LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5); - -#endif - - uint8_t i; - bool isMaster = true; - - dprintf("SX1276 Ping Pong Demo Application" ); - dprintf("Freqency: %.1f", (double)RF_FREQUENCY/1000000.0); - dprintf("TXPower: %d dBm", TX_OUTPUT_POWER); -#if USE_MODEM_LORA == 1 - dprintf("Bandwidth: %d Hz", LORA_BANDWIDTH); - dprintf("Spreading factor: SF%d", LORA_SPREADING_FACTOR); -#elif USE_MODEM_FSK == 1 - dprintf("Bandwidth: %d kHz", FSK_BANDWIDTH); - dprintf("Baudrate: %d", FSK_DATARATE); -#endif - // Initialize Radio driver - RadioEvents.TxDone = OnTxDone; - RadioEvents.RxDone = OnRxDone; - RadioEvents.RxError = OnRxError; - RadioEvents.TxTimeout = OnTxTimeout; - RadioEvents.RxTimeout = OnRxTimeout; - if (Radio->Init( &RadioEvents ) == false) { - while(1) { - dprintf("Radio could not be detected!"); - wait( 1 ); - } - } - - - switch(Radio->DetectBoardType()) { - case SX1276MB1LAS: - if (DEBUG_MESSAGE) - dprintf(" > Board Type: SX1276MB1LAS <"); - break; - case SX1276MB1MAS: - if (DEBUG_MESSAGE) - dprintf(" > Board Type: SX1276MB1LAS <"); - case MURATA_SX1276: - if (DEBUG_MESSAGE) - dprintf(" > Board Type: MURATA_SX1276_STM32L0 <"); - break; - case RFM95_SX1276: - if (DEBUG_MESSAGE) - dprintf(" > HopeRF RFM95xx <"); - break; - default: - dprintf(" > Board Type: unknown <"); - } - - Radio->SetChannel(RF_FREQUENCY ); - -#if USE_MODEM_LORA == 1 - - if (LORA_FHSS_ENABLED) - dprintf(" > LORA FHSS Mode <"); - if (!LORA_FHSS_ENABLED) - dprintf(" > LORA Mode <"); - - Radio->SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, - LORA_SPREADING_FACTOR, LORA_CODINGRATE, - LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, - LORA_CRC_ENABLED, LORA_FHSS_ENABLED, LORA_NB_SYMB_HOP, - LORA_IQ_INVERSION_ON, 2000 ); - - Radio->SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR, - LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH, - LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, 0, - LORA_CRC_ENABLED, LORA_FHSS_ENABLED, LORA_NB_SYMB_HOP, - LORA_IQ_INVERSION_ON, true ); - -#elif USE_MODEM_FSK == 1 - - dprintf(" > FSK Mode <"); - Radio->SetTxConfig( MODEM_FSK, TX_OUTPUT_POWER, FSK_FDEV, 0, - FSK_DATARATE, 0, - FSK_PREAMBLE_LENGTH, FSK_FIX_LENGTH_PAYLOAD_ON, - FSK_CRC_ENABLED, 0, 0, 0, 2000 ); - - Radio->SetRxConfig( MODEM_FSK, FSK_BANDWIDTH, FSK_DATARATE, - 0, FSK_AFC_BANDWIDTH, FSK_PREAMBLE_LENGTH, - 0, FSK_FIX_LENGTH_PAYLOAD_ON, 0, FSK_CRC_ENABLED, - 0, 0, false, true ); - -#else - -#error "Please define a modem in the compiler options." - -#endif - - if (DEBUG_MESSAGE) - dprintf("Starting Ping-Pong loop"); - - - Radio->Rx( RX_TIMEOUT_VALUE ); - - while( 1 ) - { -#ifdef TARGET_STM32L4 - WatchDogUpdate(); -#endif - - switch( State ) - { - case RX: - *led3 = 0; - if( isMaster == true ) - { - if( BufferSize > 0 ) - { - if( memcmp(Buffer, PongMsg, sizeof(PongMsg)) == 0 ) - { - *led = !*led; - dprintf( "...Pong" ); - // Send the next PING frame - memcpy(Buffer, PingMsg, sizeof(PingMsg)); - // We fill the buffer with numbers for the payload - for( i = sizeof(PingMsg); i < BufferSize; i++ ) - { - Buffer[i] = i - sizeof(PingMsg); - } - wait_ms( 10 ); - Radio->Send( Buffer, BufferSize ); - } - else if( memcmp(Buffer, PingMsg, sizeof(PingMsg)) == 0 ) - { // A master already exists then become a slave - dprintf( "...Ping" ); - *led = !*led; - isMaster = false; - // Send the next PONG frame - memcpy(Buffer, PongMsg, sizeof(PongMsg)); - // We fill the buffer with numbers for the payload - for( i = sizeof(PongMsg); i < BufferSize; i++ ) - { - Buffer[i] = i - sizeof(PongMsg); - } - wait_ms( 10 ); - Radio->Send( Buffer, BufferSize ); - } - else // valid reception but neither a PING or a PONG message - { // Set device as master ans start again - isMaster = true; - Radio->Rx( RX_TIMEOUT_VALUE ); - } - } - } - else - { - if( BufferSize > 0 ) - { - if( memcmp(Buffer, PingMsg, sizeof(PingMsg)) == 0 ) - { - *led = !*led; - dprintf( "...Ping" ); - // Send the reply to the PING string - memcpy(Buffer, PongMsg, sizeof(PongMsg)); - // We fill the buffer with numbers for the payload - for( i = sizeof(PongMsg); i < BufferSize; i++ ) - { - Buffer[i] = i - sizeof(PongMsg); - } - wait_ms( 10 ); - Radio->Send( Buffer, BufferSize ); - } - else // valid reception but not a PING as expected - { // Set device as master and start again - isMaster = true; - Radio->Rx( RX_TIMEOUT_VALUE ); - } - } - } - State = LOWPOWER; - break; - case TX: - *led3 = 1; - if( isMaster == true ) - { - dprintf("Ping..." ); - } - else - { - dprintf("Pong..." ); - } - Radio->Rx( RX_TIMEOUT_VALUE ); - State = LOWPOWER; - break; - case RX_TIMEOUT: - if( isMaster == true ) - { - // Send the next PING frame - memcpy(Buffer, PingMsg, sizeof(PingMsg)); - for( i = sizeof(PingMsg); i < BufferSize; i++ ) - { - Buffer[i] = i - sizeof(PingMsg); - } - wait_ms( 10 ); - Radio->Send( Buffer, BufferSize ); - } - else - { - Radio->Rx( RX_TIMEOUT_VALUE ); - } - State = LOWPOWER; - break; - case RX_ERROR: - // We have received a Packet with a CRC error, send reply as if packet was correct - if( isMaster == true ) - { - // Send the next PING frame - memcpy(Buffer, PingMsg, sizeof(PingMsg)); - for( i = 4; i < BufferSize; i++ ) - { - Buffer[i] = i - 4; - } - wait_ms( 10 ); - Radio->Send( Buffer, BufferSize ); - } - else - { - // Send the next PONG frame - memcpy(Buffer, PongMsg, sizeof(PongMsg)); - for( i = sizeof(PongMsg); i < BufferSize; i++ ) - { - Buffer[i] = i - sizeof(PongMsg); - } - wait_ms( 10 ); - Radio->Send( Buffer, BufferSize ); - } - State = LOWPOWER; - break; - case TX_TIMEOUT: - Radio->Rx( RX_TIMEOUT_VALUE ); - State = LOWPOWER; - break; - case LOWPOWER: - sleep(); - break; - default: - State = LOWPOWER; - break; - } - } -} - -void OnTxDone(void *radio, void *userThisPtr, void *userData) -{ - Radio->Sleep( ); - State = TX; - if (DEBUG_MESSAGE) - dprintf("> OnTxDone"); -} - -void OnRxDone(void *radio, void *userThisPtr, void *userData, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr) -{ - Radio->Sleep( ); - BufferSize = size; - memcpy( Buffer, payload, BufferSize ); - State = RX; - if (DEBUG_MESSAGE) - dprintf("> OnRxDone: RssiValue=%d dBm, SnrValue=%d", rssi, snr); - dump("Data:", payload, size); -} - -void OnTxTimeout(void *radio, void *userThisPtr, void *userData) -{ - *led3 = 0; - Radio->Sleep( ); - State = TX_TIMEOUT; - if(DEBUG_MESSAGE) - dprintf("> OnTxTimeout"); -} - -void OnRxTimeout(void *radio, void *userThisPtr, void *userData) -{ - *led3 = 0; - Radio->Sleep( ); - Buffer[BufferSize-1] = 0; - State = RX_TIMEOUT; - if (DEBUG_MESSAGE) - dprintf("> OnRxTimeout"); -} - -void OnRxError(void *radio, void *userThisPtr, void *userData) -{ - Radio->Sleep( ); - State = RX_ERROR; - if (DEBUG_MESSAGE) - dprintf("> OnRxError"); -} - -#endif
--- a/SX1276GenericPingPong/GenericPingPong.h Fri Aug 18 07:45:44 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/* - / _____) _ | | -( (____ _____ ____ _| |_ _____ ____| |__ - \____ \| ___ | (_ _) ___ |/ ___) _ \ - _____) ) ____| | | || |_| ____( (___| | | | -(______/|_____)_|_|_| \__)_____)\____)_| |_| - ( C )2014 Semtech - -Description: Contains the callbacks for the IRQs and any application related details - -License: Revised BSD License, see LICENSE.TXT file include in the project - -Maintainer: Miguel Luis and Gregory Cristian -*/ - -/* - * This file contains a copy of the master content sx1276PingPong - * with adaption for the SX1276Generic environment - * (c) 2017 Helmut Tschemernjak - * 30826 Garbsen (Hannover) Germany - */ - -#ifndef __SX1276PINGPONG_H__ -#define __SX1276PINGPONG_H__ - -#ifdef FEATURE_LORA -int SX1276PingPong(void); -#else -#define SX1276PingPong(x) void() -#endif -/* - * Callback functions prototypes - */ -/*! - * @brief Function to be executed on Radio Tx Done event - */ -void OnTxDone(void *radio, void *userThisPtr, void *userData); - -/*! - * @brief Function to be executed on Radio Rx Done event - */ -void OnRxDone(void *radio, void *userThisPtr, void *userData, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); - -/*! - * @brief Function executed on Radio Tx Timeout event - */ -void OnTxTimeout(void *radio, void *userThisPtr, void *userData); - -/*! - * @brief Function executed on Radio Rx Timeout event - */ -void OnRxTimeout(void *radio, void *userThisPtr, void *userData); - -/*! - * @brief Function executed on Radio Rx Error event - */ -void OnRxError(void *radio, void *userThisPtr, void *userData); - -/*! - * @brief Function executed on Radio Fhss Change Channel event - */ -void OnFhssChangeChannel(void *radio, void *userThisPtr, void *userData, uint8_t channelIndex); - -/*! - * @brief Function executed on CAD Done event - */ -void OnCadDone(void *radio, void *userThisPtr, void *userData); - -#endif // __MAIN_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Transmitter/Transmitter.cpp Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,230 @@ +#include "mbed.h" +#include "PinMap.h" +#include "main.h" +#include "sx1276-mbed-hal.h" + + +#ifdef FEATURE_LORA + +/* Set this flag to '1' to display debug messages on the console */ +#define DEBUG_MESSAGE 1 + +/* Set this flag to '1' to use the LoRa modulation or to '0' to use FSK modulation */ +#define USE_MODEM_LORA 1 +#define USE_MODEM_FSK !USE_MODEM_LORA +#define RF_FREQUENCY RF_FREQUENCY_868_1 // Hz +#define TX_OUTPUT_POWER 14 // 14 dBm + +#if USE_MODEM_LORA == 1 + +#define LORA_BANDWIDTH 125000 // LoRa default, details in SX1276::BandwidthMap +#define LORA_SPREADING_FACTOR LORA_SF7 +#define LORA_CODINGRATE LORA_ERROR_CODING_RATE_4_5 + +#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx +#define LORA_SYMBOL_TIMEOUT 5 // Symbols +#define LORA_FIX_LENGTH_PAYLOAD_ON false +#define LORA_FHSS_ENABLED false +#define LORA_NB_SYMB_HOP 4 +#define LORA_IQ_INVERSION_ON false +#define LORA_CRC_ENABLED true + +#elif USE_MODEM_FSK == 1 + +#define FSK_FDEV 25000 // Hz +#define FSK_DATARATE 19200 // bps +#define FSK_BANDWIDTH 50000 // Hz +#define FSK_AFC_BANDWIDTH 83333 // Hz +#define FSK_PREAMBLE_LENGTH 5 // Same for Tx and Rx +#define FSK_FIX_LENGTH_PAYLOAD_ON false +#define FSK_CRC_ENABLED true + +#else +#error "Please define a modem in the compiler options." +#endif + + +#define BUFFER_SIZE 2048 + +/* + * Global variables declarations + */ +typedef enum { + LOWPOWER = 0, + IDLE, + + RX, + RX_TIMEOUT, + RX_ERROR, + + TX, + TX_TIMEOUT, + + CAD, + CAD_DONE +} AppStates_t; + +volatile AppStates_t State = LOWPOWER; + +/*! + * Radio events function pointer + */ +static RadioEvents_t RadioEvents; + +/* + * Global variables declarations + */ +SX1276Generic *Radio; + +uint16_t BufferSize = BUFFER_SIZE; +uint8_t Buffer[BUFFER_SIZE]; + +DigitalOut *led3; + +BufferedSerial *serial; +int Transmitter() +{ +#if( defined ( TARGET_KL25Z ) || defined ( TARGET_LPC11U6X ) ) + DigitalOut *led = new DigitalOut(LED2); +#elif defined(TARGET_NUCLEO_L073RZ) || defined(TARGET_DISCO_L072CZ_LRWAN1) + DigitalOut *led = new DigitalOut(LED4); // RX red + led3 = new DigitalOut(LED3); // TX blue +#else + DigitalOut *led = new DigitalOut(LED1); + led3 = led; +#endif + +//////////////////////////////////////////////////////////////// + *led3 = 1; + + serial = new BufferedSerial(USBTX,USBRX); + serial->baud(115200*2); + serial->format(8); + +//////////////////////////////////////////////////////////////// + Radio = new SX1276Generic(NULL, MURATA_SX1276, + LORA_SPI_MOSI, LORA_SPI_MISO, LORA_SPI_SCLK, LORA_CS, LORA_RESET, + LORA_DIO0, LORA_DIO1, LORA_DIO2, LORA_DIO3, LORA_DIO4, LORA_DIO5, + LORA_ANT_RX, LORA_ANT_TX, LORA_ANT_BOOST, LORA_TCXO); + + // Initialize Radio driver + RadioEvents.TxDone = OnTxDone; + RadioEvents.RxDone = OnRxDone; + RadioEvents.RxError = OnRxError; + RadioEvents.TxTimeout = OnTxTimeout; + RadioEvents.RxTimeout = OnRxTimeout; + if (Radio->Init( &RadioEvents ) == false) { + while(Radio->Init( &RadioEvents ) == false) { + serial->printf("Radio could not be detected!"); + wait( 1 ); + } + } + + + + + Radio->SetChannel(RF_FREQUENCY ); + + + Radio->SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH, + LORA_SPREADING_FACTOR, LORA_CODINGRATE, + LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON, + LORA_CRC_ENABLED, LORA_FHSS_ENABLED, LORA_NB_SYMB_HOP, + LORA_IQ_INVERSION_ON, 2000 ); + + while( 1 ) { +#ifdef TARGET_STM32L4 + WatchDogUpdate(); +#endif + + switch( State ) { + case TX: + State = LOWPOWER; + break; + case RX_TIMEOUT: + State = LOWPOWER; + break; + case RX_ERROR: + State = LOWPOWER; + break; + case TX_TIMEOUT: + State = LOWPOWER; + break; + case LOWPOWER: + // going to send" ); + int sendSize = 0; + serial->printf("reached lowpower\n"); + int i = 0; + while(i < BufferSize) { + char temp; + while(!serial->readable()) ; + temp = serial->getc(); + if(temp == '\n') break; + Buffer[i++] = temp; + sendSize++; + } + + Buffer[i++] = '\n'; + Buffer[i++] = 0; + + sendSize++; + sendSize++; + + serial->printf("Sending: %s", Buffer); + Radio->Send( Buffer, sendSize); + wait(0.5); + *led3 = !*led3 ; + break; + default: + State = LOWPOWER; + break; + } + } +} + +void OnTxDone(void *radio, void *userThisPtr, void *userData) +{ + Radio->Sleep( ); + State = TX; + if (DEBUG_MESSAGE) + serial->printf("> OnTxDone [7]"); +} + +void OnRxDone(void *radio, void *userThisPtr, void *userData, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr) +{ + Radio->Sleep( ); + BufferSize = size; + memcpy( Buffer, payload, BufferSize ); + State = LOWPOWER; + //if (DEBUG_MESSAGE) +// dprintf("> OnRxDone: RssiValue=%d dBm, SnrValue=%d", rssi, snr); + //dump("Data:", payload, size); +} + +void OnTxTimeout(void *radio, void *userThisPtr, void *userData) +{ + *led3 = 0; + Radio->Sleep( ); + State = LOWPOWER; + //if(DEBUG_MESSAGE) +// serial->printf("> OnTxTimeout [8] "); +} + +void OnRxTimeout(void *radio, void *userThisPtr, void *userData) +{ + *led3 = 0; + Radio->Sleep( ); + State = LOWPOWER; + if (DEBUG_MESSAGE) + dprintf("> OnRxTimeout [9] "); +} + +void OnRxError(void *radio, void *userThisPtr, void *userData) +{ + Radio->Sleep( ); + State = LOWPOWER; + if (DEBUG_MESSAGE) + dprintf("> OnRxError [100] "); +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Transmitter/Transmitter.h Sat May 19 11:41:10 2018 +0000 @@ -0,0 +1,44 @@ +#ifndef __SX1276PINGPONG_H__ +#define __SX1276PINGPONG_H__ + +int Transmitter(void); + +/* + * Callback functions prototypes + */ +/*! + * @brief Function to be executed on Radio Tx Done event + */ +void OnTxDone(void *radio, void *userThisPtr, void *userData); + +/*! + * @brief Function to be executed on Radio Rx Done event + */ +void OnRxDone(void *radio, void *userThisPtr, void *userData, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); + +/*! + * @brief Function executed on Radio Tx Timeout event + */ +void OnTxTimeout(void *radio, void *userThisPtr, void *userData); + +/*! + * @brief Function executed on Radio Rx Timeout event + */ +void OnRxTimeout(void *radio, void *userThisPtr, void *userData); + +/*! + * @brief Function executed on Radio Rx Error event + */ +void OnRxError(void *radio, void *userThisPtr, void *userData); + +/*! + * @brief Function executed on Radio Fhss Change Channel event + */ +void OnFhssChangeChannel(void *radio, void *userThisPtr, void *userData, uint8_t channelIndex); + +/*! + * @brief Function executed on CAD Done event + */ +void OnCadDone(void *radio, void *userThisPtr, void *userData); + +#endif // __MAIN_H__
--- a/main.cpp Fri Aug 18 07:45:44 2017 +0000 +++ b/main.cpp Sat May 19 11:41:10 2018 +0000 @@ -6,17 +6,16 @@ #include "main.h" DigitalOut myled(LED1); -BufferedSerial *ser; + BufferedSerial *ser; + + int main() { SystemClock_Config(); - ser = new BufferedSerial(USBTX, USBRX); + ser = new BufferedSerial(USBTX,USBRX); ser->baud(115200*2); - ser->format(8); - ser->printf("Hello World\n\r"); - myled = 1; - - SX1276PingPong(); + ser->format(8); + Transmitter(); }
--- a/main.h Fri Aug 18 07:45:44 2017 +0000 +++ b/main.h Sat May 19 11:41:10 2018 +0000 @@ -7,7 +7,7 @@ #include "mbed.h" #include "PinMap.h" #include "BufferedSerial.h" -#include "GenericPingPong.h" +#include "Transmitter.h" void SystemClock_Config(void);