friendev EtherDune TCP/IP library
/home/jander/temp/etherdune/SharedBuffer.cpp
Go to the documentation of this file.
1 // EtherDune Shared circular buffer class
2 // Author: Javier Peletier <jm@friendev.com>
3 // Summary: Implements a "shared" circular buffer using spare ENC28J60 memory
4 //
5 // Copyright (c) 2015 All Rights Reserved, http://friendev.com
6 //
7 // This source is subject to the GPLv2 license.
8 // Please see the License.txt file for more information.
9 // All other rights reserved.
10 //
11 // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
12 // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13 // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
14 // PARTICULAR PURPOSE.
15 
16 #include "SharedBuffer.h"
17 
18 #include "ENC28J60.h"
19 #include "Checksum.h"
20 
21 #define AC_LOGLEVEL 2
22 #include <ACLog.h>
23 ACROSS_MODULE("SharedBuffer");
24 
26 
27 // Internal header type for each packet written to SharedBuffer
28 struct BufferHeader
29 {
30  uint16_t nextIndex;
31  uint16_t length;
32  uint16_t checksum;<
33 };
35 
36 
37 uint16_t SharedBuffer::head = 0;
38 uint16_t SharedBuffer::usedSpace = 0;
39 
40 SharedBuffer::SharedBuffer() :nextRead(0xFFFF), lastWritten(0xFFFF)
41 {
42  bufferList.add(this);
43 }
44 
46 {
47  flush();
48  bufferList.remove(this);
49 }
50 
51 
52 uint16_t SharedBuffer::writeAt(uint16_t index, uint16_t len, const byte* data)
53 {
54  // Write in a single step
55  if (len <= SHARED_BUFFER_CAPACITY - index)
56  {
57  ENC28J60::writeBuf(SHARED_BUFFER_INIT + index, len, data);
58  index += len;
59  if (index == SHARED_BUFFER_CAPACITY)
60  index = 0;
61 
62 
63  }
64  // Write in two steps
65  else
66  {
67  uint16_t size_1 = SHARED_BUFFER_CAPACITY - index;
68  ENC28J60::writeBuf(SHARED_BUFFER_INIT + index, size_1, data);
69 
70  uint16_t size_2 = len - size_1;
71  ENC28J60::writeBuf(SHARED_BUFFER_INIT , size_2, data+size_1);
72 
73  index = size_2;
74  }
75 
76 
77  return index;
78 
79 }
80 
81 uint16_t SharedBuffer::append(uint16_t len, const byte* data)
82 {
83  if (len == 0) return 0;
84 
85  uint16_t bytes_to_write = min(len, SHARED_BUFFER_CAPACITY - usedSpace);
86 
87  head = writeAt(head,bytes_to_write,data);
88 
89  usedSpace += bytes_to_write;
90 
91  return bytes_to_write;
92 }
93 
94 uint16_t SharedBuffer::readAt(uint16_t index, uint16_t len, byte* data)
95 {
96  // Read in a single step
97  if (len <= SHARED_BUFFER_CAPACITY - index)
98  {
99  ENC28J60::readBuf(SHARED_BUFFER_INIT + index, len, data);
100  index += len;
101  if (index == SHARED_BUFFER_CAPACITY) index = 0;
102  }
103  // Read in two steps
104  else
105  {
106  uint16_t size_1 = SHARED_BUFFER_CAPACITY - index;
107  ENC28J60::readBuf(SHARED_BUFFER_INIT + index, size_1, data);
108 
109  uint16_t size_2 = len - size_1;
110  ENC28J60::readBuf(SHARED_BUFFER_INIT, size_2, data + size_1);
111 
112  index = size_2;
113  }
114 
115 
116  return index;
117 }
118 
119 
126 uint16_t SharedBuffer::write(uint16_t len, const byte* data)
127 {
128  int16_t availableSpace = SHARED_BUFFER_CAPACITY - usedSpace - sizeof(BufferHeader);
129  if (availableSpace < 0)
130  availableSpace = 0;
131 
132  len = min(len, availableSpace);
133 
134  if (len == 0)
135  return len;
136 
137  BufferHeader header;
138  header.length = len;
139  header.checksum = Checksum::calc(len,data);
140 
141  header.nextIndex = 0xFFFF;
142 
143  uint16_t h = head;
144  append(sizeof(header), (byte*)&header);
145  append(len, data);
146 
147 
148  if (lastWritten != 0xFFFF)
149  writeAt(lastWritten, sizeof(h), (byte*)&h);
150  else
151  nextRead = h;
152 
153  lastWritten = h;
154 
155  return len;
156 }
157 
163 {
164  if (isEmpty())
165  return 0;
166 
167  BufferHeader header;
168 
169  readAt(nextRead, sizeof(header), (byte*) &header);
170 
171  nextRead = header.nextIndex;
172 
173  if (isEmpty())
174  lastWritten = 0xFFFF;
175 
176  usedSpace=0;
177 
178  for (SharedBuffer* s = (SharedBuffer*)bufferList.first; s->nextItem != NULL; s = (SharedBuffer*)s->nextItem)
179  {
180  if (s->isEmpty())
181  continue;
182 
183  uint16_t d = (s->nextRead < head) ? head - s->nextRead : SHARED_BUFFER_CAPACITY - s->nextRead + head;
184 
185  if (d > usedSpace)
186  usedSpace = d;
187 
188  }
189 
190  ACTRACE("release(). head=%d, usedSpace=%d", head,usedSpace);
191  return header.length;
192 
193 }
194 
199 {
200  while (release());
201 }
202 
203 
212 uint16_t SharedBuffer::fillTxBuffer(uint16_t dstOffset, uint16_t& checksum, uint16_t count )
213 {
214 
215  BufferHeader header;
216  checksum = 0;
217 
218  uint16_t txPtr = TXSTART_INIT_DATA + dstOffset;
219  bool startOdd = txPtr & 1;
220 
221  uint16_t n = nextRead;
222  while (n!=0xFFFF && count>0)
223  {
224  count--;
225  readAt(n, sizeof(header), (byte*)&header);
226  if ((txPtr + header.length > TXSTOP_INIT + 1))
227  break;
228 
229  uint16_t src = SHARED_BUFFER_INIT + n + sizeof(header);
230 
231  if (src >= SHARED_BUFFER_INIT + SHARED_BUFFER_CAPACITY)
232  src -= SHARED_BUFFER_CAPACITY;
233 
234  uint16_t len = header.length;
235  bool odd = startOdd ^ (txPtr & 1);
236 
237  if (src + len > SHARED_BUFFER_INIT + SHARED_BUFFER_CAPACITY)
238  {
239  len = SHARED_BUFFER_INIT + SHARED_BUFFER_CAPACITY - src;
240  ENC28J60::moveMem(txPtr, src, len);
241  txPtr += len;
242  len = header.length - len;
243  src = SHARED_BUFFER_INIT;
244  }
245 
246  ENC28J60::moveMem(txPtr, src,len);
247 
248  checksum = Checksum::add(checksum, header.checksum, odd);
249 
250  txPtr += len;
251  n = header.nextIndex;
252 
253  }
254 
255  return txPtr - (TXSTART_INIT_DATA + dstOffset);
256 }
257 
258 
264 {
265  return nextRead == 0xFFFF;
266 }
uint16_t release()
Releases one fragment of data
static void moveMem(uint16_t dest, uint16_t src, uint16_t len)
Definition: ENC28J60.cpp:198
ListItem * nextItem
Definition: List.h:39
static uint16_t calc(uint16_t len, const uint8_t *data)
Calculates the checksum of the specified buffer in memory
Definition: Checksum.cpp:59
ListItem * first
Definition: List.h:47
void add(ListItem *item)
Definition: List.cpp:27
void remove(ListItem *item)
Definition: List.cpp:33
void flush()
Releases all data in this buffer, freeing up space.
static uint16_t add(uint16_t a, uint16_t b)
Adds two checksums, taking carry into account.
Definition: Checksum.cpp:25
uint16_t write(uint16_t len, const byte *data)
Writes a fragment to the shared buffer
ACROSS_MODULE("SharedBuffer")
Implements a "shared" circular buffer using spare ENC28J60 memory.
Definition: SharedBuffer.h:158
bool isEmpty()
Determines whether this buffer is empty
static void readBuf(uint16_t src, uint16_t len, byte *data)
Definition: ENC28J60.cpp:109
static void writeBuf(uint16_t dst, uint16_t len, const byte *data)
Definition: ENC28J60.cpp:123
uint16_t fillTxBuffer(uint16_t dstOffset, uint16_t &checksum, uint16_t count=0xFFFF)
Copies up to count fragments to the transmit buffer via DMA, concatenating them starting at given off...