Moved Stash and BufferFiller classes into their own files (#329)
This commit is contained in:
parent
3386c350b1
commit
4861d72642
|
@ -13,380 +13,6 @@
|
|||
#include <stdarg.h>
|
||||
#include <avr/eeprom.h>
|
||||
|
||||
#define WRITEBUF 0
|
||||
#define READBUF 1
|
||||
#define BUFCOUNT 2
|
||||
|
||||
//#define FLOATEMIT // uncomment line to enable $T in emit_P for float emitting
|
||||
|
||||
byte Stash::map[SCRATCH_MAP_SIZE];
|
||||
Stash::Block Stash::bufs[BUFCOUNT];
|
||||
|
||||
uint8_t Stash::allocBlock () {
|
||||
for (uint8_t i = 0; i < sizeof map; ++i)
|
||||
if (map[i] != 0)
|
||||
for (uint8_t j = 0; j < 8; ++j)
|
||||
if (bitRead(map[i], j)) {
|
||||
bitClear(map[i], j);
|
||||
return (i << 3) + j;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Stash::freeBlock (uint8_t block) {
|
||||
bitSet(map[block>>3], block & 7);
|
||||
}
|
||||
|
||||
uint8_t Stash::fetchByte (uint8_t blk, uint8_t off) {
|
||||
return blk == bufs[WRITEBUF].bnum ? bufs[WRITEBUF].bytes[off] :
|
||||
blk == bufs[READBUF].bnum ? bufs[READBUF].bytes[off] :
|
||||
ether.peekin(blk, off);
|
||||
}
|
||||
|
||||
|
||||
// block 0 is special since always occupied
|
||||
void Stash::initMap (uint8_t last /*=SCRATCH_PAGE_NUM*/) {
|
||||
last = SCRATCH_PAGE_NUM;
|
||||
while (--last > 0)
|
||||
freeBlock(last);
|
||||
}
|
||||
|
||||
// load a page/block either into the write or into the readbuffer
|
||||
void Stash::load (uint8_t idx, uint8_t blk) {
|
||||
if (blk != bufs[idx].bnum) {
|
||||
if (idx == WRITEBUF) {
|
||||
ether.copyout(bufs[idx].bnum, bufs[idx].bytes);
|
||||
if (blk == bufs[READBUF].bnum)
|
||||
bufs[READBUF].bnum = 255; // forget read page if same
|
||||
} else if (blk == bufs[WRITEBUF].bnum) {
|
||||
// special case: read page is same as write buffer
|
||||
memcpy(&bufs[READBUF], &bufs[WRITEBUF], sizeof bufs[0]);
|
||||
return;
|
||||
}
|
||||
bufs[idx].bnum = blk;
|
||||
ether.copyin(bufs[idx].bnum, bufs[idx].bytes);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Stash::freeCount () {
|
||||
uint8_t count = 0;
|
||||
for (uint8_t i = 0; i < sizeof map; ++i)
|
||||
for (uint8_t m = 0x80; m != 0; m >>= 1)
|
||||
if (map[i] & m)
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
// create a new stash; make it the active stash; return the first block as a handle
|
||||
uint8_t Stash::create () {
|
||||
uint8_t blk = allocBlock();
|
||||
load(WRITEBUF, blk);
|
||||
bufs[WRITEBUF].head.count = 0;
|
||||
bufs[WRITEBUF].head.first = bufs[0].head.last = blk;
|
||||
bufs[WRITEBUF].tail = sizeof (StashHeader);
|
||||
bufs[WRITEBUF].next = 0;
|
||||
return open(blk); // you are now the active stash
|
||||
}
|
||||
|
||||
// the stashheader part only contains reasonable data if we are the first block
|
||||
uint8_t Stash::open (uint8_t blk) {
|
||||
curr = blk;
|
||||
offs = sizeof (StashHeader); // goto first byte
|
||||
load(READBUF, curr);
|
||||
memcpy((StashHeader*) this, bufs[READBUF].bytes, sizeof (StashHeader));
|
||||
return curr;
|
||||
}
|
||||
|
||||
// save the metadata of current block into the first block
|
||||
void Stash::save () {
|
||||
load(WRITEBUF, first);
|
||||
memcpy(bufs[WRITEBUF].bytes, (StashHeader*) this, sizeof (StashHeader));
|
||||
if (bufs[READBUF].bnum == first)
|
||||
load(READBUF, 0); // invalidates original in case it was the same block
|
||||
}
|
||||
|
||||
// follow the linked list of blocks and free every block
|
||||
void Stash::release () {
|
||||
while (first > 0) {
|
||||
freeBlock(first);
|
||||
first = ether.peekin(first, 63);
|
||||
}
|
||||
}
|
||||
|
||||
void Stash::put (char c) {
|
||||
load(WRITEBUF, last);
|
||||
uint8_t t = bufs[WRITEBUF].tail;
|
||||
bufs[WRITEBUF].bytes[t++] = c;
|
||||
if (t <= 62)
|
||||
bufs[WRITEBUF].tail = t;
|
||||
else {
|
||||
bufs[WRITEBUF].next = allocBlock();
|
||||
last = bufs[WRITEBUF].next;
|
||||
load(WRITEBUF, last);
|
||||
bufs[WRITEBUF].tail = bufs[WRITEBUF].next = 0;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
char Stash::get () {
|
||||
load(READBUF, curr);
|
||||
if (curr == last && offs >= bufs[READBUF].tail)
|
||||
return 0;
|
||||
uint8_t b = bufs[READBUF].bytes[offs];
|
||||
if (++offs >= 63 && curr != last) {
|
||||
curr = bufs[READBUF].next;
|
||||
offs = 0;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// fetchbyte(last, 62) is tail, i.e., number of characters in last block
|
||||
uint16_t Stash::size () {
|
||||
return 63 * count + fetchByte(last, 62) - sizeof (StashHeader);
|
||||
}
|
||||
|
||||
static char* wtoa (uint16_t value, char* ptr) {
|
||||
if (value > 9)
|
||||
ptr = wtoa(value / 10, ptr);
|
||||
*ptr = '0' + value % 10;
|
||||
*++ptr = 0;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// write information about the fmt string and the arguments into special page/block 0
|
||||
// block 0 is initially marked as allocated and never returned by allocateBlock
|
||||
void Stash::prepare (const char* fmt PROGMEM, ...) {
|
||||
Stash::load(WRITEBUF, 0);
|
||||
uint16_t* segs = Stash::bufs[WRITEBUF].words;
|
||||
*segs++ = strlen_P(fmt);
|
||||
#ifdef __AVR__
|
||||
*segs++ = (uint16_t) fmt;
|
||||
#else
|
||||
*segs++ = (uint32_t) fmt;
|
||||
*segs++ = (uint32_t) fmt >> 16;
|
||||
#endif
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
for (;;) {
|
||||
char c = pgm_read_byte(fmt++);
|
||||
if (c == 0)
|
||||
break;
|
||||
if (c == '$') {
|
||||
#ifdef __AVR__
|
||||
uint16_t argval = va_arg(ap, uint16_t), arglen = 0;
|
||||
#else
|
||||
uint32_t argval = va_arg(ap, int), arglen = 0;
|
||||
#endif
|
||||
switch (pgm_read_byte(fmt++)) {
|
||||
case 'D': {
|
||||
char buf[7];
|
||||
wtoa(argval, buf);
|
||||
arglen = strlen(buf);
|
||||
break;
|
||||
}
|
||||
case 'S':
|
||||
arglen = strlen((const char*) argval);
|
||||
break;
|
||||
case 'F':
|
||||
arglen = strlen_P((const char*) argval);
|
||||
break;
|
||||
case 'E': {
|
||||
byte* s = (byte*) argval;
|
||||
char d;
|
||||
while ((d = eeprom_read_byte(s++)) != 0)
|
||||
++arglen;
|
||||
break;
|
||||
}
|
||||
case 'H': {
|
||||
Stash stash (argval);
|
||||
arglen = stash.size();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef __AVR__
|
||||
*segs++ = argval;
|
||||
#else
|
||||
*segs++ = argval;
|
||||
*segs++ = argval >> 16;
|
||||
#endif
|
||||
Stash::bufs[WRITEBUF].words[0] += arglen - 2;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
uint16_t Stash::length () {
|
||||
Stash::load(WRITEBUF, 0);
|
||||
return Stash::bufs[WRITEBUF].words[0];
|
||||
}
|
||||
|
||||
void Stash::extract (uint16_t offset, uint16_t count, void* buf) {
|
||||
Stash::load(WRITEBUF, 0);
|
||||
uint16_t* segs = Stash::bufs[WRITEBUF].words;
|
||||
#ifdef __AVR__
|
||||
const char* fmt PROGMEM = (const char*) *++segs;
|
||||
#else
|
||||
const char* fmt PROGMEM = (const char*)((segs[2] << 16) | segs[1]);
|
||||
segs += 2;
|
||||
#endif
|
||||
Stash stash;
|
||||
char mode = '@', tmp[7], *ptr = NULL, *out = (char*) buf;
|
||||
for (uint16_t i = 0; i < offset + count; ) {
|
||||
char c = 0;
|
||||
switch (mode) {
|
||||
case '@': {
|
||||
c = pgm_read_byte(fmt++);
|
||||
if (c == 0)
|
||||
return;
|
||||
if (c != '$')
|
||||
break;
|
||||
#ifdef __AVR__
|
||||
uint16_t arg = *++segs;
|
||||
#else
|
||||
uint32_t arg = *++segs;
|
||||
arg |= *++segs << 16;
|
||||
#endif
|
||||
mode = pgm_read_byte(fmt++);
|
||||
switch (mode) {
|
||||
case 'D':
|
||||
wtoa(arg, tmp);
|
||||
ptr = tmp;
|
||||
break;
|
||||
case 'S':
|
||||
case 'F':
|
||||
case 'E':
|
||||
ptr = (char*) arg;
|
||||
break;
|
||||
case 'H':
|
||||
stash.open(arg);
|
||||
ptr = (char*) &stash;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case 'D':
|
||||
case 'S':
|
||||
c = *ptr++;
|
||||
break;
|
||||
case 'F':
|
||||
c = pgm_read_byte(ptr++);
|
||||
break;
|
||||
case 'E':
|
||||
c = eeprom_read_byte((byte*) ptr++);
|
||||
break;
|
||||
case 'H':
|
||||
c = ((Stash*) ptr)->get();
|
||||
break;
|
||||
}
|
||||
if (c == 0) {
|
||||
mode = '@';
|
||||
continue;
|
||||
}
|
||||
if (i >= offset)
|
||||
*out++ = c;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void Stash::cleanup () {
|
||||
Stash::load(WRITEBUF, 0);
|
||||
uint16_t* segs = Stash::bufs[WRITEBUF].words;
|
||||
#ifdef __AVR__
|
||||
const char* fmt PROGMEM = (const char*) *++segs;
|
||||
#else
|
||||
const char* fmt PROGMEM = (const char*)((segs[2] << 16) | segs[1]);
|
||||
segs += 2;
|
||||
#endif
|
||||
for (;;) {
|
||||
char c = pgm_read_byte(fmt++);
|
||||
if (c == 0)
|
||||
break;
|
||||
if (c == '$') {
|
||||
#ifdef __AVR__
|
||||
uint16_t arg = *++segs;
|
||||
#else
|
||||
uint32_t arg = *++segs;
|
||||
arg |= *++segs << 16;
|
||||
#endif
|
||||
if (pgm_read_byte(fmt++) == 'H') {
|
||||
Stash stash (arg);
|
||||
stash.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BufferFiller::emit_p(const char* fmt PROGMEM, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
for (;;) {
|
||||
char c = pgm_read_byte(fmt++);
|
||||
if (c == 0)
|
||||
break;
|
||||
if (c != '$') {
|
||||
*ptr++ = c;
|
||||
continue;
|
||||
}
|
||||
c = pgm_read_byte(fmt++);
|
||||
switch (c) {
|
||||
case 'D':
|
||||
#ifdef __AVR__
|
||||
wtoa(va_arg(ap, uint16_t), (char*) ptr);
|
||||
#else
|
||||
wtoa(va_arg(ap, int), (char*) ptr);
|
||||
#endif
|
||||
break;
|
||||
#ifdef FLOATEMIT
|
||||
case 'T':
|
||||
dtostrf ( va_arg(ap, double), 10, 3, (char*)ptr );
|
||||
break;
|
||||
#endif
|
||||
case 'H': {
|
||||
#ifdef __AVR__
|
||||
char p1 = va_arg(ap, uint16_t);
|
||||
#else
|
||||
char p1 = va_arg(ap, int);
|
||||
#endif
|
||||
char p2;
|
||||
p2 = (p1 >> 4) & 0x0F;
|
||||
p1 = p1 & 0x0F;
|
||||
if (p1 > 9) p1 += 0x07; // adjust 0x0a-0x0f to come out 'a'-'f'
|
||||
p1 += 0x30; // and complete
|
||||
if (p2 > 9) p2 += 0x07; // adjust 0x0a-0x0f to come out 'a'-'f'
|
||||
p2 += 0x30; // and complete
|
||||
*ptr++ = p2;
|
||||
*ptr++ = p1;
|
||||
continue;
|
||||
}
|
||||
case 'L':
|
||||
ltoa(va_arg(ap, long), (char*) ptr, 10);
|
||||
break;
|
||||
case 'S':
|
||||
strcpy((char*) ptr, va_arg(ap, const char*));
|
||||
break;
|
||||
case 'F': {
|
||||
const char* s PROGMEM = va_arg(ap, const char*);
|
||||
char d;
|
||||
while ((d = pgm_read_byte(s++)) != 0)
|
||||
*ptr++ = d;
|
||||
continue;
|
||||
}
|
||||
case 'E': {
|
||||
byte* s = va_arg(ap, byte*);
|
||||
char d;
|
||||
while ((d = eeprom_read_byte(s++)) != 0)
|
||||
*ptr++ = d;
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
*ptr++ = c;
|
||||
continue;
|
||||
}
|
||||
ptr += strlen((char*) ptr);
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
EtherCard ether;
|
||||
|
||||
uint8_t EtherCard::mymac[ETH_LEN]; // my MAC address
|
||||
|
@ -431,3 +57,12 @@ bool EtherCard::staticSetup (const uint8_t* my_ip,
|
|||
delaycnt = 0; //request gateway ARP lookup
|
||||
return true;
|
||||
}
|
||||
|
||||
char* EtherCard::wtoa(uint16_t value, char* ptr)
|
||||
{
|
||||
if (value > 9)
|
||||
ptr = wtoa(value / 10, ptr);
|
||||
*ptr = '0' + value % 10;
|
||||
*++ptr = 0;
|
||||
return ptr;
|
||||
}
|
||||
|
|
175
src/EtherCard.h
175
src/EtherCard.h
|
@ -37,8 +37,10 @@
|
|||
#endif
|
||||
|
||||
#include <avr/pgmspace.h>
|
||||
#include "bufferfiller.h"
|
||||
#include "enc28j60.h"
|
||||
#include "net.h"
|
||||
#include "stash.h"
|
||||
|
||||
/** Enable DHCP.
|
||||
* Setting this to zero disables the use of DHCP; if a program uses DHCP it will
|
||||
|
@ -99,173 +101,6 @@ typedef void (*DhcpOptionCallback)(
|
|||
uint8_t len); ///< Length of the DHCP option data
|
||||
|
||||
|
||||
/** This structure describes the structure of memory used within the ENC28J60 network interface. */
|
||||
typedef struct {
|
||||
uint8_t count; ///< Number of allocated pages
|
||||
uint8_t first; ///< First allocated page
|
||||
uint8_t last; ///< Last allocated page
|
||||
} StashHeader;
|
||||
|
||||
/** This class provides access to the memory within the ENC28J60 network interface. */
|
||||
class Stash : public /*Stream*/ Print, private StashHeader {
|
||||
uint8_t curr; //!< Current page
|
||||
uint8_t offs; //!< Current offset in page
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
uint8_t bytes[64];
|
||||
uint16_t words[32];
|
||||
struct {
|
||||
StashHeader head; // StashHeader is only stored in first block
|
||||
uint8_t filler[59];
|
||||
uint8_t tail; // only meaningful if bnum==last; number of bytes in last block
|
||||
uint8_t next; // pointer to next block
|
||||
};
|
||||
};
|
||||
uint8_t bnum;
|
||||
} Block;
|
||||
|
||||
static uint8_t allocBlock ();
|
||||
static void freeBlock (uint8_t block);
|
||||
static uint8_t fetchByte (uint8_t blk, uint8_t off);
|
||||
|
||||
static Block bufs[2];
|
||||
static uint8_t map[SCRATCH_MAP_SIZE];
|
||||
|
||||
public:
|
||||
static void initMap (uint8_t last=SCRATCH_PAGE_NUM);
|
||||
static void load (uint8_t idx, uint8_t blk);
|
||||
static uint8_t freeCount ();
|
||||
|
||||
Stash () : curr (0) { first = 0; }
|
||||
Stash (uint8_t fd) { open(fd); }
|
||||
|
||||
uint8_t create ();
|
||||
uint8_t open (uint8_t blk);
|
||||
void save ();
|
||||
void release ();
|
||||
|
||||
void put (char c);
|
||||
char get ();
|
||||
uint16_t size ();
|
||||
|
||||
virtual WRITE_RESULT write(uint8_t b) { put(b); WRITE_RETURN }
|
||||
|
||||
// virtual int available() {
|
||||
// if (curr != last)
|
||||
// return 1;
|
||||
// load(1, last);
|
||||
// return offs < bufs[1].tail;
|
||||
// }
|
||||
// virtual int read() {
|
||||
// return available() ? get() : -1;
|
||||
// }
|
||||
// virtual int peek() {
|
||||
// return available() ? bufs[1].bytes[offs] : -1;
|
||||
// }
|
||||
// virtual void flush() {
|
||||
// curr = last;
|
||||
// offs = 63;
|
||||
// }
|
||||
|
||||
static void prepare (const char* fmt PROGMEM, ...);
|
||||
static uint16_t length ();
|
||||
static void extract (uint16_t offset, uint16_t count, void* buf);
|
||||
static void cleanup ();
|
||||
|
||||
friend void dumpBlock (const char* msg, uint8_t idx); // optional
|
||||
friend void dumpStash (const char* msg, void* ptr); // optional
|
||||
};
|
||||
|
||||
/** This class populates network send and receive buffers.
|
||||
*
|
||||
* This class provides formatted printing into memory. Users can use it to write into send buffers.
|
||||
*
|
||||
* Nota: PGM_P: is a pointer to a string in program space (defined in the source code, updated to PROGMEM)
|
||||
*
|
||||
* # Format string
|
||||
*
|
||||
* | Format | Parameter | Output
|
||||
* |--------|-------------|----------
|
||||
* | $D | uint16_t | Decimal representation
|
||||
* | $T ¤ | double | Decimal representation with 3 digits after decimal sign ([-]d.ddd)
|
||||
* | $H | uint16_t | Hexadecimal value of lsb (from 00 to ff)
|
||||
* | $L | long | Decimal representation
|
||||
* | $S | const char* | Copy null terminated string from main memory
|
||||
* | $F | PGM_P | Copy null terminated string from program space
|
||||
* | $E | byte* | Copy null terminated string from EEPROM space
|
||||
* | $$ | _none_ | '$'
|
||||
*
|
||||
* ¤ _Available only if FLOATEMIT is defined_
|
||||
*
|
||||
* # Examples
|
||||
* ~~~~~~~~~~~~~{.c}
|
||||
* uint16_t ddd = 123;
|
||||
* double ttt = 1.23;
|
||||
* uint16_t hhh = 0xa4;
|
||||
* long lll = 123456789;
|
||||
* char * sss;
|
||||
* char fff[] PROGMEM = "MyMemory";
|
||||
*
|
||||
* sss[0] = 'G';
|
||||
* sss[1] = 'P';
|
||||
* sss[2] = 'L';
|
||||
* sss[3] = 0;
|
||||
* buf.emit_p( PSTR("ddd=$D\n"), ddd ); // "ddd=123\n"
|
||||
* buf.emit_p( PSTR("ttt=$T\n"), ttt ); // "ttt=1.23\n" **TO CHECK**
|
||||
* buf.emit_p( PSTR("hhh=$H\n"), hhh ); // "hhh=a4\n"
|
||||
* buf.emit_p( PSTR("lll=$L\n"), lll ); // "lll=123456789\n"
|
||||
* buf.emit_p( PSTR("sss=$S\n"), sss ); // "sss=GPL\n"
|
||||
* buf.emit_p( PSTR("fff=$F\n"), fff ); // "fff=MyMemory\n"
|
||||
* ~~~~~~~~~~~~~
|
||||
*
|
||||
*/
|
||||
class BufferFiller : public Print {
|
||||
uint8_t *start; //!< Pointer to start of buffer
|
||||
uint8_t *ptr; //!< Pointer to cursor position
|
||||
public:
|
||||
/** @brief Empty constructor
|
||||
*/
|
||||
BufferFiller () {}
|
||||
|
||||
/** @brief Constructor
|
||||
* @param buf Pointer to the ethernet data buffer
|
||||
*/
|
||||
BufferFiller (uint8_t* buf) : start (buf), ptr (buf) {}
|
||||
|
||||
/** @brief Add formatted text to buffer
|
||||
* @param fmt Format string (see Class description)
|
||||
* @param ... parameters for format string
|
||||
*/
|
||||
void emit_p (const char* fmt PROGMEM, ...);
|
||||
|
||||
/** @brief Add data to buffer from main memory
|
||||
* @param s Pointer to data
|
||||
* @param n Number of characters to copy
|
||||
*/
|
||||
void emit_raw (const char* s, uint16_t n) { memcpy(ptr, s, n); ptr += n; }
|
||||
|
||||
/** @brief Add data to buffer from program space string
|
||||
* @param p Program space string pointer
|
||||
* @param n Number of characters to copy
|
||||
*/
|
||||
void emit_raw_p (const char* p PROGMEM, uint16_t n) { memcpy_P(ptr, p, n); ptr += n; }
|
||||
|
||||
/** @brief Get pointer to start of buffer
|
||||
* @return <i>uint8_t*</i> Pointer to start of buffer
|
||||
*/
|
||||
uint8_t* buffer () const { return start; }
|
||||
|
||||
/** @brief Get cursor position
|
||||
* @return <i>uint16_t</i> Cursor position
|
||||
*/
|
||||
uint16_t position () const { return ptr - start; }
|
||||
|
||||
/** @brief Write one byte to buffer
|
||||
* @param v Byte to add to buffer
|
||||
*/
|
||||
virtual WRITE_RESULT write (uint8_t v) { *ptr++ = v; WRITE_RETURN }
|
||||
};
|
||||
|
||||
/** This class provides the main interface to a ENC28J60 based network interface card and is the class most users will use.
|
||||
* @note All TCP/IP client (outgoing) connections are made from source port in range 2816-3071. Do not use these source ports for other purposes.
|
||||
|
@ -630,6 +465,12 @@ public:
|
|||
static void makeNetStr(char *resultstr,uint8_t *bytestr,uint8_t len,
|
||||
char separator,uint8_t base);
|
||||
|
||||
/** @brief Convert a 16-bit integer into a string
|
||||
* @param value The number to convert
|
||||
* @param ptr The string location to write to
|
||||
*/
|
||||
char* wtoa(uint16_t value, char* ptr);
|
||||
|
||||
/** @brief Return the sequence number of the current TCP package
|
||||
*/
|
||||
static uint32_t getSequenceNumber();
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
#include "bufferfiller.h"
|
||||
|
||||
void BufferFiller::emit_p(const char* fmt PROGMEM, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
for (;;) {
|
||||
char c = pgm_read_byte(fmt++);
|
||||
if (c == 0)
|
||||
break;
|
||||
if (c != '$') {
|
||||
*ptr++ = c;
|
||||
continue;
|
||||
}
|
||||
c = pgm_read_byte(fmt++);
|
||||
switch (c) {
|
||||
case 'D':
|
||||
#ifdef __AVR__
|
||||
ether.wtoa(va_arg(ap, uint16_t), (char*) ptr);
|
||||
#else
|
||||
ether.wtoa(va_arg(ap, int), (char*) ptr);
|
||||
#endif
|
||||
break;
|
||||
#ifdef FLOATEMIT
|
||||
case 'T':
|
||||
dtostrf ( va_arg(ap, double), 10, 3, (char*)ptr );
|
||||
break;
|
||||
#endif
|
||||
case 'H': {
|
||||
#ifdef __AVR__
|
||||
char p1 = va_arg(ap, uint16_t);
|
||||
#else
|
||||
char p1 = va_arg(ap, int);
|
||||
#endif
|
||||
char p2;
|
||||
p2 = (p1 >> 4) & 0x0F;
|
||||
p1 = p1 & 0x0F;
|
||||
if (p1 > 9) p1 += 0x07; // adjust 0x0a-0x0f to come out 'a'-'f'
|
||||
p1 += 0x30; // and complete
|
||||
if (p2 > 9) p2 += 0x07; // adjust 0x0a-0x0f to come out 'a'-'f'
|
||||
p2 += 0x30; // and complete
|
||||
*ptr++ = p2;
|
||||
*ptr++ = p1;
|
||||
continue;
|
||||
}
|
||||
case 'L':
|
||||
ltoa(va_arg(ap, long), (char*) ptr, 10);
|
||||
break;
|
||||
case 'S':
|
||||
strcpy((char*) ptr, va_arg(ap, const char*));
|
||||
break;
|
||||
case 'F': {
|
||||
const char* s PROGMEM = va_arg(ap, const char*);
|
||||
char d;
|
||||
while ((d = pgm_read_byte(s++)) != 0)
|
||||
*ptr++ = d;
|
||||
continue;
|
||||
}
|
||||
case 'E': {
|
||||
byte* s = va_arg(ap, byte*);
|
||||
char d;
|
||||
while ((d = eeprom_read_byte(s++)) != 0)
|
||||
*ptr++ = d;
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
*ptr++ = c;
|
||||
continue;
|
||||
}
|
||||
ptr += strlen((char*) ptr);
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/** @file */
|
||||
|
||||
#ifndef BufferFiller_h
|
||||
#define BufferFiller_h
|
||||
|
||||
#include "EtherCard.h"
|
||||
|
||||
|
||||
/** This class populates network send and receive buffers.
|
||||
*
|
||||
* This class provides formatted printing into memory. Users can use it to write into send buffers.
|
||||
*
|
||||
* Nota: PGM_P: is a pointer to a string in program space (defined in the source code, updated to PROGMEM)
|
||||
*
|
||||
* # Format string
|
||||
*
|
||||
* | Format | Parameter | Output
|
||||
* |--------|-------------|----------
|
||||
* | $D | uint16_t | Decimal representation
|
||||
* | $T ¤ | double | Decimal representation with 3 digits after decimal sign ([-]d.ddd)
|
||||
* | $H | uint16_t | Hexadecimal value of lsb (from 00 to ff)
|
||||
* | $L | long | Decimal representation
|
||||
* | $S | const char* | Copy null terminated string from main memory
|
||||
* | $F | PGM_P | Copy null terminated string from program space
|
||||
* | $E | byte* | Copy null terminated string from EEPROM space
|
||||
* | $$ | _none_ | '$'
|
||||
*
|
||||
* ¤ _Available only if FLOATEMIT is defined_
|
||||
*
|
||||
* # Examples
|
||||
* ~~~~~~~~~~~~~{.c}
|
||||
* uint16_t ddd = 123;
|
||||
* double ttt = 1.23;
|
||||
* uint16_t hhh = 0xa4;
|
||||
* long lll = 123456789;
|
||||
* char * sss;
|
||||
* char fff[] PROGMEM = "MyMemory";
|
||||
*
|
||||
* sss[0] = 'G';
|
||||
* sss[1] = 'P';
|
||||
* sss[2] = 'L';
|
||||
* sss[3] = 0;
|
||||
* buf.emit_p( PSTR("ddd=$D\n"), ddd ); // "ddd=123\n"
|
||||
* buf.emit_p( PSTR("ttt=$T\n"), ttt ); // "ttt=1.23\n" **TO CHECK**
|
||||
* buf.emit_p( PSTR("hhh=$H\n"), hhh ); // "hhh=a4\n"
|
||||
* buf.emit_p( PSTR("lll=$L\n"), lll ); // "lll=123456789\n"
|
||||
* buf.emit_p( PSTR("sss=$S\n"), sss ); // "sss=GPL\n"
|
||||
* buf.emit_p( PSTR("fff=$F\n"), fff ); // "fff=MyMemory\n"
|
||||
* ~~~~~~~~~~~~~
|
||||
*
|
||||
*/
|
||||
class BufferFiller : public Print {
|
||||
uint8_t *start; //!< Pointer to start of buffer
|
||||
uint8_t *ptr; //!< Pointer to cursor position
|
||||
public:
|
||||
/** @brief Empty constructor
|
||||
*/
|
||||
BufferFiller () {}
|
||||
|
||||
/** @brief Constructor
|
||||
* @param buf Pointer to the ethernet data buffer
|
||||
*/
|
||||
BufferFiller (uint8_t* buf) : start (buf), ptr (buf) {}
|
||||
|
||||
/** @brief Add formatted text to buffer
|
||||
* @param fmt Format string (see Class description)
|
||||
* @param ... parameters for format string
|
||||
*/
|
||||
void emit_p (const char* fmt PROGMEM, ...);
|
||||
|
||||
/** @brief Add data to buffer from main memory
|
||||
* @param s Pointer to data
|
||||
* @param n Number of characters to copy
|
||||
*/
|
||||
void emit_raw (const char* s, uint16_t n) { memcpy(ptr, s, n); ptr += n; }
|
||||
|
||||
/** @brief Add data to buffer from program space string
|
||||
* @param p Program space string pointer
|
||||
* @param n Number of characters to copy
|
||||
*/
|
||||
void emit_raw_p (const char* p PROGMEM, uint16_t n) { memcpy_P(ptr, p, n); ptr += n; }
|
||||
|
||||
/** @brief Get pointer to start of buffer
|
||||
* @return <i>uint8_t*</i> Pointer to start of buffer
|
||||
*/
|
||||
uint8_t* buffer () const { return start; }
|
||||
|
||||
/** @brief Get cursor position
|
||||
* @return <i>uint16_t</i> Cursor position
|
||||
*/
|
||||
uint16_t position () const { return ptr - start; }
|
||||
|
||||
/** @brief Write one byte to buffer
|
||||
* @param v Byte to add to buffer
|
||||
*/
|
||||
virtual WRITE_RESULT write (uint8_t v) { *ptr++ = v; WRITE_RETURN }
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,300 @@
|
|||
#include <stdarg.h>
|
||||
#include <avr/eeprom.h>
|
||||
|
||||
#include "stash.h"
|
||||
|
||||
#define WRITEBUF 0
|
||||
#define READBUF 1
|
||||
#define BUFCOUNT 2
|
||||
|
||||
//#define FLOATEMIT // uncomment line to enable $T in emit_P for float emitting
|
||||
|
||||
byte Stash::map[SCRATCH_MAP_SIZE];
|
||||
Stash::Block Stash::bufs[BUFCOUNT];
|
||||
|
||||
uint8_t Stash::allocBlock () {
|
||||
for (uint8_t i = 0; i < sizeof map; ++i)
|
||||
if (map[i] != 0)
|
||||
for (uint8_t j = 0; j < 8; ++j)
|
||||
if (bitRead(map[i], j)) {
|
||||
bitClear(map[i], j);
|
||||
return (i << 3) + j;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Stash::freeBlock (uint8_t block) {
|
||||
bitSet(map[block>>3], block & 7);
|
||||
}
|
||||
|
||||
uint8_t Stash::fetchByte (uint8_t blk, uint8_t off) {
|
||||
return blk == bufs[WRITEBUF].bnum ? bufs[WRITEBUF].bytes[off] :
|
||||
blk == bufs[READBUF].bnum ? bufs[READBUF].bytes[off] :
|
||||
ether.peekin(blk, off);
|
||||
}
|
||||
|
||||
|
||||
// block 0 is special since always occupied
|
||||
void Stash::initMap (uint8_t last /*=SCRATCH_PAGE_NUM*/) {
|
||||
last = SCRATCH_PAGE_NUM;
|
||||
while (--last > 0)
|
||||
freeBlock(last);
|
||||
}
|
||||
|
||||
// load a page/block either into the write or into the readbuffer
|
||||
void Stash::load (uint8_t idx, uint8_t blk) {
|
||||
if (blk != bufs[idx].bnum) {
|
||||
if (idx == WRITEBUF) {
|
||||
ether.copyout(bufs[idx].bnum, bufs[idx].bytes);
|
||||
if (blk == bufs[READBUF].bnum)
|
||||
bufs[READBUF].bnum = 255; // forget read page if same
|
||||
} else if (blk == bufs[WRITEBUF].bnum) {
|
||||
// special case: read page is same as write buffer
|
||||
memcpy(&bufs[READBUF], &bufs[WRITEBUF], sizeof bufs[0]);
|
||||
return;
|
||||
}
|
||||
bufs[idx].bnum = blk;
|
||||
ether.copyin(bufs[idx].bnum, bufs[idx].bytes);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Stash::freeCount () {
|
||||
uint8_t count = 0;
|
||||
for (uint8_t i = 0; i < sizeof map; ++i)
|
||||
for (uint8_t m = 0x80; m != 0; m >>= 1)
|
||||
if (map[i] & m)
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
// create a new stash; make it the active stash; return the first block as a handle
|
||||
uint8_t Stash::create () {
|
||||
uint8_t blk = allocBlock();
|
||||
load(WRITEBUF, blk);
|
||||
bufs[WRITEBUF].head.count = 0;
|
||||
bufs[WRITEBUF].head.first = bufs[0].head.last = blk;
|
||||
bufs[WRITEBUF].tail = sizeof (StashHeader);
|
||||
bufs[WRITEBUF].next = 0;
|
||||
return open(blk); // you are now the active stash
|
||||
}
|
||||
|
||||
// the stashheader part only contains reasonable data if we are the first block
|
||||
uint8_t Stash::open (uint8_t blk) {
|
||||
curr = blk;
|
||||
offs = sizeof (StashHeader); // goto first byte
|
||||
load(READBUF, curr);
|
||||
memcpy((StashHeader*) this, bufs[READBUF].bytes, sizeof (StashHeader));
|
||||
return curr;
|
||||
}
|
||||
|
||||
// save the metadata of current block into the first block
|
||||
void Stash::save () {
|
||||
load(WRITEBUF, first);
|
||||
memcpy(bufs[WRITEBUF].bytes, (StashHeader*) this, sizeof (StashHeader));
|
||||
if (bufs[READBUF].bnum == first)
|
||||
load(READBUF, 0); // invalidates original in case it was the same block
|
||||
}
|
||||
|
||||
// follow the linked list of blocks and free every block
|
||||
void Stash::release () {
|
||||
while (first > 0) {
|
||||
freeBlock(first);
|
||||
first = ether.peekin(first, 63);
|
||||
}
|
||||
}
|
||||
|
||||
void Stash::put (char c) {
|
||||
load(WRITEBUF, last);
|
||||
uint8_t t = bufs[WRITEBUF].tail;
|
||||
bufs[WRITEBUF].bytes[t++] = c;
|
||||
if (t <= 62)
|
||||
bufs[WRITEBUF].tail = t;
|
||||
else {
|
||||
bufs[WRITEBUF].next = allocBlock();
|
||||
last = bufs[WRITEBUF].next;
|
||||
load(WRITEBUF, last);
|
||||
bufs[WRITEBUF].tail = bufs[WRITEBUF].next = 0;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
char Stash::get () {
|
||||
load(READBUF, curr);
|
||||
if (curr == last && offs >= bufs[READBUF].tail)
|
||||
return 0;
|
||||
uint8_t b = bufs[READBUF].bytes[offs];
|
||||
if (++offs >= 63 && curr != last) {
|
||||
curr = bufs[READBUF].next;
|
||||
offs = 0;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// fetchbyte(last, 62) is tail, i.e., number of characters in last block
|
||||
uint16_t Stash::size () {
|
||||
return 63 * count + fetchByte(last, 62) - sizeof (StashHeader);
|
||||
}
|
||||
|
||||
// write information about the fmt string and the arguments into special page/block 0
|
||||
// block 0 is initially marked as allocated and never returned by allocateBlock
|
||||
void Stash::prepare (const char* fmt PROGMEM, ...) {
|
||||
Stash::load(WRITEBUF, 0);
|
||||
uint16_t* segs = Stash::bufs[WRITEBUF].words;
|
||||
*segs++ = strlen_P(fmt);
|
||||
#ifdef __AVR__
|
||||
*segs++ = (uint16_t) fmt;
|
||||
#else
|
||||
*segs++ = (uint32_t) fmt;
|
||||
*segs++ = (uint32_t) fmt >> 16;
|
||||
#endif
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
for (;;) {
|
||||
char c = pgm_read_byte(fmt++);
|
||||
if (c == 0)
|
||||
break;
|
||||
if (c == '$') {
|
||||
#ifdef __AVR__
|
||||
uint16_t argval = va_arg(ap, uint16_t), arglen = 0;
|
||||
#else
|
||||
uint32_t argval = va_arg(ap, int), arglen = 0;
|
||||
#endif
|
||||
switch (pgm_read_byte(fmt++)) {
|
||||
case 'D': {
|
||||
char buf[7];
|
||||
ether.wtoa(argval, buf);
|
||||
arglen = strlen(buf);
|
||||
break;
|
||||
}
|
||||
case 'S':
|
||||
arglen = strlen((const char*) argval);
|
||||
break;
|
||||
case 'F':
|
||||
arglen = strlen_P((const char*) argval);
|
||||
break;
|
||||
case 'E': {
|
||||
byte* s = (byte*) argval;
|
||||
char d;
|
||||
while ((d = eeprom_read_byte(s++)) != 0)
|
||||
++arglen;
|
||||
break;
|
||||
}
|
||||
case 'H': {
|
||||
Stash stash (argval);
|
||||
arglen = stash.size();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef __AVR__
|
||||
*segs++ = argval;
|
||||
#else
|
||||
*segs++ = argval;
|
||||
*segs++ = argval >> 16;
|
||||
#endif
|
||||
Stash::bufs[WRITEBUF].words[0] += arglen - 2;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
uint16_t Stash::length () {
|
||||
Stash::load(WRITEBUF, 0);
|
||||
return Stash::bufs[WRITEBUF].words[0];
|
||||
}
|
||||
|
||||
void Stash::extract (uint16_t offset, uint16_t count, void* buf) {
|
||||
Stash::load(WRITEBUF, 0);
|
||||
uint16_t* segs = Stash::bufs[WRITEBUF].words;
|
||||
#ifdef __AVR__
|
||||
const char* fmt PROGMEM = (const char*) *++segs;
|
||||
#else
|
||||
const char* fmt PROGMEM = (const char*)((segs[2] << 16) | segs[1]);
|
||||
segs += 2;
|
||||
#endif
|
||||
Stash stash;
|
||||
char mode = '@', tmp[7], *ptr = NULL, *out = (char*) buf;
|
||||
for (uint16_t i = 0; i < offset + count; ) {
|
||||
char c = 0;
|
||||
switch (mode) {
|
||||
case '@': {
|
||||
c = pgm_read_byte(fmt++);
|
||||
if (c == 0)
|
||||
return;
|
||||
if (c != '$')
|
||||
break;
|
||||
#ifdef __AVR__
|
||||
uint16_t arg = *++segs;
|
||||
#else
|
||||
uint32_t arg = *++segs;
|
||||
arg |= *++segs << 16;
|
||||
#endif
|
||||
mode = pgm_read_byte(fmt++);
|
||||
switch (mode) {
|
||||
case 'D':
|
||||
ether.wtoa(arg, tmp);
|
||||
ptr = tmp;
|
||||
break;
|
||||
case 'S':
|
||||
case 'F':
|
||||
case 'E':
|
||||
ptr = (char*) arg;
|
||||
break;
|
||||
case 'H':
|
||||
stash.open(arg);
|
||||
ptr = (char*) &stash;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case 'D':
|
||||
case 'S':
|
||||
c = *ptr++;
|
||||
break;
|
||||
case 'F':
|
||||
c = pgm_read_byte(ptr++);
|
||||
break;
|
||||
case 'E':
|
||||
c = eeprom_read_byte((byte*) ptr++);
|
||||
break;
|
||||
case 'H':
|
||||
c = ((Stash*) ptr)->get();
|
||||
break;
|
||||
}
|
||||
if (c == 0) {
|
||||
mode = '@';
|
||||
continue;
|
||||
}
|
||||
if (i >= offset)
|
||||
*out++ = c;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void Stash::cleanup () {
|
||||
Stash::load(WRITEBUF, 0);
|
||||
uint16_t* segs = Stash::bufs[WRITEBUF].words;
|
||||
#ifdef __AVR__
|
||||
const char* fmt PROGMEM = (const char*) *++segs;
|
||||
#else
|
||||
const char* fmt PROGMEM = (const char*)((segs[2] << 16) | segs[1]);
|
||||
segs += 2;
|
||||
#endif
|
||||
for (;;) {
|
||||
char c = pgm_read_byte(fmt++);
|
||||
if (c == 0)
|
||||
break;
|
||||
if (c == '$') {
|
||||
#ifdef __AVR__
|
||||
uint16_t arg = *++segs;
|
||||
#else
|
||||
uint32_t arg = *++segs;
|
||||
arg |= *++segs << 16;
|
||||
#endif
|
||||
if (pgm_read_byte(fmt++) == 'H') {
|
||||
Stash stash (arg);
|
||||
stash.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
#ifndef Stash_h
|
||||
#define Stash_h
|
||||
|
||||
#include "EtherCard.h"
|
||||
|
||||
/** This structure describes the structure of memory used within the ENC28J60 network interface. */
|
||||
typedef struct {
|
||||
uint8_t count; ///< Number of allocated pages
|
||||
uint8_t first; ///< First allocated page
|
||||
uint8_t last; ///< Last allocated page
|
||||
} StashHeader;
|
||||
|
||||
/** This class provides access to the memory within the ENC28J60 network interface. */
|
||||
class Stash : public /*Stream*/ Print, private StashHeader {
|
||||
uint8_t curr; //!< Current page
|
||||
uint8_t offs; //!< Current offset in page
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
uint8_t bytes[64];
|
||||
uint16_t words[32];
|
||||
struct {
|
||||
StashHeader head; // StashHeader is only stored in first block
|
||||
uint8_t filler[59];
|
||||
uint8_t tail; // only meaningful if bnum==last; number of bytes in last block
|
||||
uint8_t next; // pointer to next block
|
||||
};
|
||||
};
|
||||
uint8_t bnum;
|
||||
} Block;
|
||||
|
||||
static uint8_t allocBlock ();
|
||||
static void freeBlock (uint8_t block);
|
||||
static uint8_t fetchByte (uint8_t blk, uint8_t off);
|
||||
|
||||
static Block bufs[2];
|
||||
static uint8_t map[SCRATCH_MAP_SIZE];
|
||||
|
||||
public:
|
||||
static void initMap (uint8_t last=SCRATCH_PAGE_NUM);
|
||||
static void load (uint8_t idx, uint8_t blk);
|
||||
static uint8_t freeCount ();
|
||||
|
||||
Stash () : curr (0) { first = 0; }
|
||||
Stash (uint8_t fd) { open(fd); }
|
||||
|
||||
uint8_t create ();
|
||||
uint8_t open (uint8_t blk);
|
||||
void save ();
|
||||
void release ();
|
||||
|
||||
void put (char c);
|
||||
char get ();
|
||||
uint16_t size ();
|
||||
|
||||
virtual WRITE_RESULT write(uint8_t b) { put(b); WRITE_RETURN }
|
||||
|
||||
// virtual int available() {
|
||||
// if (curr != last)
|
||||
// return 1;
|
||||
// load(1, last);
|
||||
// return offs < bufs[1].tail;
|
||||
// }
|
||||
// virtual int read() {
|
||||
// return available() ? get() : -1;
|
||||
// }
|
||||
// virtual int peek() {
|
||||
// return available() ? bufs[1].bytes[offs] : -1;
|
||||
// }
|
||||
// virtual void flush() {
|
||||
// curr = last;
|
||||
// offs = 63;
|
||||
// }
|
||||
|
||||
static void prepare (const char* fmt PROGMEM, ...);
|
||||
static uint16_t length ();
|
||||
static void extract (uint16_t offset, uint16_t count, void* buf);
|
||||
static void cleanup ();
|
||||
|
||||
friend void dumpBlock (const char* msg, uint8_t idx); // optional
|
||||
friend void dumpStash (const char* msg, void* ptr); // optional
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue