ringbuf.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. #include "freertos/FreeRTOS.h"
  14. #include "freertos/task.h"
  15. #include "freertos/semphr.h"
  16. #include "freertos/queue.h"
  17. #include "freertos/xtensa_api.h"
  18. #include "freertos/ringbuf.h"
  19. #include "esp_attr.h"
  20. #include <stdint.h>
  21. #include <string.h>
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. typedef enum {
  25. flag_allowsplit = 1,
  26. flag_bytebuf = 2,
  27. } rbflag_t;
  28. typedef enum {
  29. iflag_free = 1, //Buffer is not read and given back by application, free to overwrite
  30. iflag_dummydata = 2, //Data from here to end of ringbuffer is dummy. Restart reading at start of ringbuffer.
  31. } itemflag_t;
  32. typedef struct ringbuf_t ringbuf_t;
  33. //The ringbuffer structure
  34. struct ringbuf_t {
  35. SemaphoreHandle_t free_space_sem; //Binary semaphore, wakes up writing threads when there's more free space
  36. SemaphoreHandle_t items_buffered_sem; //Binary semaphore, indicates there are new packets in the circular buffer. See remark.
  37. size_t size; //Size of the data storage
  38. uint8_t *write_ptr; //Pointer where the next item is written
  39. uint8_t *read_ptr; //Pointer from where the next item is read
  40. uint8_t *free_ptr; //Pointer to the last block that hasn't been given back to the ringbuffer yet
  41. uint8_t *data; //Data storage
  42. portMUX_TYPE mux; //Spinlock for actual data/ptr/struct modification
  43. rbflag_t flags;
  44. size_t maxItemSize;
  45. //The following keep function pointers to hold different implementations for ringbuffer management.
  46. BaseType_t (*copyItemToRingbufImpl)(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size);
  47. uint8_t *(*getItemFromRingbufImpl)(ringbuf_t *rb, size_t *length, int wanted_length);
  48. void (*returnItemToRingbufImpl)(ringbuf_t *rb, void *item);
  49. };
  50. /*
  51. Remark: A counting semaphore for items_buffered_sem would be more logical, but counting semaphores in
  52. FreeRTOS need a maximum count, and allocate more memory the larger the maximum count is. Here, we
  53. would need to set the maximum to the maximum amount of times a null-byte unit firs in the buffer,
  54. which is quite high and so would waste a fair amount of memory.
  55. */
  56. //The header prepended to each ringbuffer entry. Size is assumed to be a multiple of 32bits.
  57. typedef struct {
  58. size_t len;
  59. itemflag_t flags;
  60. } buf_entry_hdr_t;
  61. //Calculate space free in the buffer
  62. static int ringbufferFreeMem(ringbuf_t *rb)
  63. {
  64. int free_size = rb->free_ptr-rb->write_ptr;
  65. if (free_size <= 0) free_size += rb->size;
  66. //Reserve one byte. If we do not do this and the entire buffer is filled, we get a situation
  67. //where read_ptr == free_ptr, messing up the next calculation.
  68. return free_size-1;
  69. }
  70. //Copies a single item to the ring buffer; refuses to split items. Assumes there is space in the ringbuffer and
  71. //the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on
  72. //success, pdFALSE if it can't make the item fit and the calling routine needs to retry
  73. //later or fail.
  74. //This function by itself is not threadsafe, always call from within a muxed section.
  75. static BaseType_t copyItemToRingbufNoSplit(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
  76. {
  77. size_t rbuffer_size;
  78. rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value
  79. configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned
  80. configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size
  81. //of a header to the end of the ringbuff
  82. size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer
  83. //See if we have enough contiguous space to write the buffer.
  84. if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) {
  85. //Buffer plus header is not going to fit in the room from wr_pos to the end of the
  86. //ringbuffer... but we're not allowed to split the buffer. We need to fill the
  87. //rest of the ringbuffer with a dummy item so we can place the data at the _start_ of
  88. //the ringbuffer..
  89. //First, find out if we actually have enough space at the start of the ringbuffer to
  90. //make this work (Again, we need 4 bytes extra because otherwise read_ptr==free_ptr)
  91. if (rb->free_ptr-rb->data < rbuffer_size+sizeof(buf_entry_hdr_t)+4) {
  92. //Will not fit.
  93. return pdFALSE;
  94. }
  95. //If the read buffer hasn't wrapped around yet, there's no way this will work either.
  96. if (rb->free_ptr > rb->write_ptr) {
  97. //No luck.
  98. return pdFALSE;
  99. }
  100. //Okay, it will fit. Mark the rest of the ringbuffer space with a dummy packet.
  101. buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
  102. hdr->flags=iflag_dummydata;
  103. //Reset the write pointer to the start of the ringbuffer so the code later on can
  104. //happily write the data.
  105. rb->write_ptr=rb->data;
  106. } else {
  107. //No special handling needed. Checking if it's gonna fit probably still is a good idea.
  108. if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) {
  109. //Buffer is not going to fit, period.
  110. return pdFALSE;
  111. }
  112. }
  113. //If we are here, the buffer is guaranteed to fit in the space starting at the write pointer.
  114. buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
  115. hdr->len=buffer_size;
  116. hdr->flags=0;
  117. rb->write_ptr+=sizeof(buf_entry_hdr_t);
  118. memcpy(rb->write_ptr, buffer, buffer_size);
  119. rb->write_ptr+=rbuffer_size;
  120. //The buffer will wrap around if we don't have room for a header anymore.
  121. if ((rb->data+rb->size)-rb->write_ptr < sizeof(buf_entry_hdr_t)) {
  122. //'Forward' the write buffer until we are at the start of the ringbuffer.
  123. //The read pointer will always be at the start of a full header, which cannot
  124. //exist at the point of the current write pointer, so there's no chance of overtaking
  125. //that.
  126. rb->write_ptr=rb->data;
  127. }
  128. return pdTRUE;
  129. }
  130. //Copies a single item to the ring buffer; allows split items. Assumes there is space in the ringbuffer and
  131. //the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on
  132. //success, pdFALSE if it can't make the item fit and the calling routine needs to retry
  133. //later or fail.
  134. //This function by itself is not threadsafe, always call from within a muxed section.
  135. static BaseType_t copyItemToRingbufAllowSplit(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
  136. {
  137. size_t rbuffer_size;
  138. rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value
  139. configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned
  140. configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size
  141. //of a header to the end of the ringbuff
  142. size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer
  143. //See if we have enough contiguous space to write the buffer.
  144. if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) {
  145. //The buffer can't be contiguously written to the ringbuffer, but needs special handling. Do
  146. //that depending on how the ringbuffer is configured.
  147. //The code here is also expected to check if the buffer, mangled in whatever way is implemented,
  148. //will still fit, and return pdFALSE if that is not the case.
  149. //Buffer plus header is not going to fit in the room from wr_pos to the end of the
  150. //ringbuffer... we need to split the write in two.
  151. //First, see if this will fit at all.
  152. if (ringbufferFreeMem(rb) < (sizeof(buf_entry_hdr_t)*2)+rbuffer_size) {
  153. //Will not fit.
  154. return pdFALSE;
  155. }
  156. //Because the code at the end of the function makes sure we always have
  157. //room for a header, this should never assert.
  158. configASSERT(rem_len>=sizeof(buf_entry_hdr_t));
  159. //Okay, it should fit. Write everything.
  160. //First, place bit of buffer that does fit. Write header first...
  161. buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
  162. hdr->flags=0;
  163. hdr->len=rem_len-sizeof(buf_entry_hdr_t);
  164. rb->write_ptr+=sizeof(buf_entry_hdr_t);
  165. rem_len-=sizeof(buf_entry_hdr_t);
  166. if (rem_len!=0) {
  167. //..then write the data bit that fits.
  168. memcpy(rb->write_ptr, buffer, rem_len);
  169. //Update vars so the code later on will write the rest of the data.
  170. buffer+=rem_len;
  171. buffer_size-=rem_len;
  172. //Re-adjust the rbuffer value to be 4 byte aligned
  173. rbuffer_size=(buffer_size+3)&~3;
  174. //It is possible that we are here because we checked for 4byte aligned
  175. //size, but actual data was smaller.
  176. //Eg. For buffer_size = 34, rbuffer_size will be 36. Suppose we had only
  177. //42 bytes of memory available, the top level check will fail, as it will
  178. //check for availability of 36 + 8 = 44 bytes.
  179. //However, the 42 bytes available memory is sufficient for 34 + 8 bytes data
  180. //and so, we can return after writing the data. Hence, this check
  181. if (buffer_size == 0) {
  182. rb->write_ptr=rb->data;
  183. return pdTRUE;
  184. }
  185. } else {
  186. //Huh, only the header fit. Mark as dummy so the receive function doesn't receive
  187. //an useless zero-byte packet.
  188. hdr->flags|=iflag_dummydata;
  189. }
  190. rb->write_ptr=rb->data;
  191. } else {
  192. //No special handling needed. Checking if it's gonna fit probably still is a good idea.
  193. if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) {
  194. //Buffer is not going to fit, period.
  195. return pdFALSE;
  196. }
  197. }
  198. //If we are here, the buffer is guaranteed to fit in the space starting at the write pointer.
  199. buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
  200. hdr->len=buffer_size;
  201. hdr->flags=0;
  202. rb->write_ptr+=sizeof(buf_entry_hdr_t);
  203. memcpy(rb->write_ptr, buffer, buffer_size);
  204. rb->write_ptr+=rbuffer_size;
  205. //The buffer will wrap around if we don't have room for a header anymore.
  206. if ((rb->data+rb->size)-rb->write_ptr < sizeof(buf_entry_hdr_t)) {
  207. //'Forward' the write buffer until we are at the start of the ringbuffer.
  208. //The read pointer will always be at the start of a full header, which cannot
  209. //exist at the point of the current write pointer, so there's no chance of overtaking
  210. //that.
  211. rb->write_ptr=rb->data;
  212. }
  213. return pdTRUE;
  214. }
  215. //Copies a bunch of daya to the ring bytebuffer. Assumes there is space in the ringbuffer and
  216. //the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on
  217. //success, pdFALSE if it can't make the item fit and the calling routine needs to retry
  218. //later or fail.
  219. //This function by itself is not threadsafe, always call from within a muxed section.
  220. static BaseType_t copyItemToRingbufByteBuf(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
  221. {
  222. size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer
  223. //See if we have enough contiguous space to write the buffer.
  224. if (rem_len < buffer_size) {
  225. //...Nope. Write the data bit that fits.
  226. memcpy(rb->write_ptr, buffer, rem_len);
  227. //Update vars so the code later on will write the rest of the data.
  228. buffer+=rem_len;
  229. buffer_size-=rem_len;
  230. rb->write_ptr=rb->data;
  231. }
  232. //If we are here, the buffer is guaranteed to fit in the space starting at the write pointer.
  233. memcpy(rb->write_ptr, buffer, buffer_size);
  234. rb->write_ptr+=buffer_size;
  235. //The buffer will wrap around if we're at the end.
  236. if ((rb->data+rb->size)==rb->write_ptr) {
  237. rb->write_ptr=rb->data;
  238. }
  239. return pdTRUE;
  240. }
  241. //Retrieves a pointer to the data of the next item, or NULL if this is not possible.
  242. //This function by itself is not threadsafe, always call from within a muxed section.
  243. //Because we always return one item, this function ignores the wanted_length variable.
  244. static uint8_t *getItemFromRingbufDefault(ringbuf_t *rb, size_t *length, int wanted_length)
  245. {
  246. uint8_t *ret;
  247. configASSERT(((int)rb->read_ptr&3)==0);
  248. if (rb->read_ptr == rb->write_ptr) {
  249. //No data available.
  250. return NULL;
  251. }
  252. //The item written at the point of the read pointer may be a dummy item.
  253. //We need to skip past it first, if that's the case.
  254. buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->read_ptr;
  255. configASSERT((hdr->len < rb->size) || (hdr->flags & iflag_dummydata));
  256. if (hdr->flags & iflag_dummydata) {
  257. //Hdr is dummy data. Reset to start of ringbuffer.
  258. rb->read_ptr=rb->data;
  259. //Get real header
  260. hdr=(buf_entry_hdr_t *)rb->read_ptr;
  261. configASSERT(hdr->len < rb->size);
  262. //No need to re-check if the ringbuffer is empty: the write routine will
  263. //always write a dummy item plus the real data item in one go, so now we must
  264. //be at the real data item by definition.
  265. }
  266. //Okay, pass the data back.
  267. ret=rb->read_ptr+sizeof(buf_entry_hdr_t);
  268. *length=hdr->len;
  269. //...and move the read pointer past the data.
  270. rb->read_ptr+=sizeof(buf_entry_hdr_t)+((hdr->len+3)&~3);
  271. //The buffer will wrap around if we don't have room for a header anymore.
  272. //Integer typecasting is used because the first operand can result into a -ve
  273. //value for cases wherein the ringbuffer size is not a multiple of 4, but the
  274. //implementation logic aligns read_ptr to 4-byte boundary
  275. if ((int)((rb->data + rb->size) - rb->read_ptr) < (int)sizeof(buf_entry_hdr_t)) {
  276. rb->read_ptr=rb->data;
  277. }
  278. return ret;
  279. }
  280. //Retrieves a pointer to the data in the buffer, or NULL if this is not possible.
  281. //This function by itself is not threadsafe, always call from within a muxed section.
  282. //This function honours the wanted_length and will never return more data than this.
  283. static uint8_t *getItemFromRingbufByteBuf(ringbuf_t *rb, size_t *length, int wanted_length)
  284. {
  285. uint8_t *ret;
  286. if (rb->read_ptr != rb->free_ptr) {
  287. //This type of ringbuff does not support multiple outstanding buffers.
  288. return NULL;
  289. }
  290. if (rb->read_ptr == rb->write_ptr) {
  291. //No data available.
  292. return NULL;
  293. }
  294. ret=rb->read_ptr;
  295. if (rb->read_ptr > rb->write_ptr) {
  296. //Available data wraps around. Give data until the end of the buffer.
  297. *length=rb->size-(rb->read_ptr - rb->data);
  298. if (wanted_length != 0 && *length > wanted_length) {
  299. *length=wanted_length;
  300. rb->read_ptr+=wanted_length;
  301. } else {
  302. rb->read_ptr=rb->data;
  303. }
  304. } else {
  305. //Return data up to write pointer.
  306. *length=rb->write_ptr -rb->read_ptr;
  307. if (wanted_length != 0 && *length > wanted_length) {
  308. *length=wanted_length;
  309. rb->read_ptr+=wanted_length;
  310. } else {
  311. rb->read_ptr=rb->write_ptr;
  312. }
  313. }
  314. return ret;
  315. }
  316. //Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer
  317. //can be increase.
  318. //This function by itself is not threadsafe, always call from within a muxed section.
  319. static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) {
  320. uint8_t *data=(uint8_t*)item;
  321. configASSERT(((int)rb->free_ptr&3)==0);
  322. configASSERT(data >= rb->data);
  323. configASSERT(data <= rb->data+rb->size);
  324. //Grab the buffer entry that preceeds the buffer
  325. buf_entry_hdr_t *hdr=(buf_entry_hdr_t*)(data-sizeof(buf_entry_hdr_t));
  326. configASSERT(hdr->len < rb->size);
  327. configASSERT((hdr->flags & iflag_dummydata)==0);
  328. configASSERT((hdr->flags & iflag_free)==0);
  329. //Mark the buffer as free.
  330. hdr->flags|=iflag_free;
  331. //Do a cleanup pass.
  332. hdr=(buf_entry_hdr_t *)rb->free_ptr;
  333. //basically forward free_ptr until we run into either a block that is still in use or the write pointer.
  334. while (((hdr->flags & iflag_free) || (hdr->flags & iflag_dummydata)) && rb->free_ptr != rb->write_ptr) {
  335. if (hdr->flags & iflag_dummydata) {
  336. //Rest is dummy data. Reset to start of ringbuffer.
  337. rb->free_ptr=rb->data;
  338. } else {
  339. //Skip past item
  340. rb->free_ptr+=sizeof(buf_entry_hdr_t);
  341. //Check if the free_ptr overshoots the buffer.
  342. //Checking this before aligning free_ptr since it is possible that alignment
  343. //will cause pointer to overshoot, if the ringbuf size is not a multiple of 4
  344. configASSERT(rb->free_ptr+hdr->len<=rb->data+rb->size);
  345. //Align free_ptr to 4 byte boundary. Overshoot condition will result in wrap around below
  346. size_t len=(hdr->len+3)&~3;
  347. rb->free_ptr+=len;
  348. }
  349. //The buffer will wrap around if we don't have room for a header anymore.
  350. //Integer typecasting is used because the first operand can result into a -ve
  351. //value for cases wherein the ringbuffer size is not a multiple of 4, but the
  352. //implementation logic aligns free_ptr to 4-byte boundary
  353. if ((int)((rb->data+rb->size)-rb->free_ptr) < (int)sizeof(buf_entry_hdr_t)) {
  354. rb->free_ptr=rb->data;
  355. }
  356. //The free_ptr can not exceed read_ptr, otherwise write_ptr might overwrite read_ptr.
  357. //Read_ptr can not set to rb->data with free_ptr, otherwise write_ptr might wrap around to rb->data.
  358. if(rb->free_ptr == rb->read_ptr) break;
  359. //Next header
  360. hdr=(buf_entry_hdr_t *)rb->free_ptr;
  361. }
  362. }
  363. //Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer
  364. //can be increase.
  365. //This function by itself is not threadsafe, always call from within a muxed section.
  366. static void returnItemToRingbufBytebuf(ringbuf_t *rb, void *item) {
  367. uint8_t *data=(uint8_t*)item;
  368. configASSERT(data >= rb->data);
  369. configASSERT(data < rb->data+rb->size);
  370. //Free the read memory.
  371. rb->free_ptr=rb->read_ptr;
  372. }
  373. void xRingbufferPrintInfo(RingbufHandle_t ringbuf)
  374. {
  375. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  376. configASSERT(rb);
  377. ets_printf("Rb size %d free %d rptr %d freeptr %d wptr %d\n",
  378. rb->size, ringbufferFreeMem(rb), rb->read_ptr-rb->data, rb->free_ptr-rb->data, rb->write_ptr-rb->data);
  379. }
  380. RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type)
  381. {
  382. ringbuf_t *rb = malloc(sizeof(ringbuf_t));
  383. if (rb==NULL) goto err;
  384. memset(rb, 0, sizeof(ringbuf_t));
  385. rb->data = malloc(buf_length);
  386. if (rb->data == NULL) goto err;
  387. rb->size = buf_length;
  388. rb->free_ptr = rb->data;
  389. rb->read_ptr = rb->data;
  390. rb->write_ptr = rb->data;
  391. rb->free_space_sem = xSemaphoreCreateBinary();
  392. rb->items_buffered_sem = xSemaphoreCreateBinary();
  393. rb->flags=0;
  394. if (type==RINGBUF_TYPE_ALLOWSPLIT) {
  395. rb->flags|=flag_allowsplit;
  396. rb->copyItemToRingbufImpl=copyItemToRingbufAllowSplit;
  397. rb->getItemFromRingbufImpl=getItemFromRingbufDefault;
  398. rb->returnItemToRingbufImpl=returnItemToRingbufDefault;
  399. //Calculate max item size. Worst case, we need to split an item into two, which means two headers of overhead.
  400. rb->maxItemSize=rb->size-(sizeof(buf_entry_hdr_t)*2)-4;
  401. } else if (type==RINGBUF_TYPE_BYTEBUF) {
  402. rb->flags|=flag_bytebuf;
  403. rb->copyItemToRingbufImpl=copyItemToRingbufByteBuf;
  404. rb->getItemFromRingbufImpl=getItemFromRingbufByteBuf;
  405. rb->returnItemToRingbufImpl=returnItemToRingbufBytebuf;
  406. //Calculate max item size. We have no headers and can split anywhere -> size is total size minus one.
  407. rb->maxItemSize=rb->size-1;
  408. } else if (type==RINGBUF_TYPE_NOSPLIT) {
  409. rb->copyItemToRingbufImpl=copyItemToRingbufNoSplit;
  410. rb->getItemFromRingbufImpl=getItemFromRingbufDefault;
  411. rb->returnItemToRingbufImpl=returnItemToRingbufDefault;
  412. //Calculate max item size. Worst case, we have the write ptr in such a position that we are lacking four bytes of free
  413. //memory to put an item into the rest of the memory. If this happens, we have to dummy-fill
  414. //(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill
  415. //with the real item. (item size being header+data)
  416. rb->maxItemSize=(rb->size/2)-sizeof(buf_entry_hdr_t)-4;
  417. } else {
  418. configASSERT(0);
  419. }
  420. if (rb->free_space_sem == NULL || rb->items_buffered_sem == NULL) goto err;
  421. vPortCPUInitializeMutex(&rb->mux);
  422. return (RingbufHandle_t)rb;
  423. err:
  424. //Some error has happened. Free/destroy all allocated things and return NULL.
  425. if (rb) {
  426. free(rb->data);
  427. if (rb->free_space_sem) vSemaphoreDelete(rb->free_space_sem);
  428. if (rb->items_buffered_sem) vSemaphoreDelete(rb->items_buffered_sem);
  429. }
  430. free(rb);
  431. return NULL;
  432. }
  433. void vRingbufferDelete(RingbufHandle_t ringbuf) {
  434. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  435. if (rb) {
  436. free(rb->data);
  437. if (rb->free_space_sem) vSemaphoreDelete(rb->free_space_sem);
  438. if (rb->items_buffered_sem) vSemaphoreDelete(rb->items_buffered_sem);
  439. }
  440. free(rb);
  441. }
  442. size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf)
  443. {
  444. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  445. configASSERT(rb);
  446. return rb->maxItemSize;
  447. }
  448. BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, TickType_t ticks_to_wait)
  449. {
  450. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  451. size_t needed_size=dataSize+sizeof(buf_entry_hdr_t);
  452. BaseType_t done=pdFALSE;
  453. TickType_t ticks_end = xTaskGetTickCount() + ticks_to_wait;
  454. TickType_t ticks_remaining = ticks_to_wait;
  455. configASSERT(rb);
  456. if (dataSize > xRingbufferGetMaxItemSize(ringbuf)) {
  457. //Data will never ever fit in the queue.
  458. return pdFALSE;
  459. }
  460. while (!done) {
  461. //Check if there is enough room in the buffer. If not, wait until there is.
  462. do {
  463. if (ringbufferFreeMem(rb) < needed_size) {
  464. //Data does not fit yet. Wait until the free_space_sem is given, then re-evaluate.
  465. BaseType_t r = xSemaphoreTake(rb->free_space_sem, ticks_remaining);
  466. if (r == pdFALSE) {
  467. //Timeout.
  468. return pdFALSE;
  469. }
  470. //Adjust ticks_remaining; we may have waited less than that and in the case the free memory still is not enough,
  471. //we will need to wait some more.
  472. if (ticks_to_wait != portMAX_DELAY) {
  473. ticks_remaining = ticks_end - xTaskGetTickCount();
  474. }
  475. // ticks_remaining will always be less than or equal to the original ticks_to_wait,
  476. // unless the timeout is reached - in which case it unsigned underflows to a much
  477. // higher value.
  478. //
  479. // (Check is written this non-intuitive way to allow for the case where xTaskGetTickCount()
  480. // has overflowed but the ticks_end value has not overflowed.)
  481. }
  482. } while (ringbufferFreeMem(rb) < needed_size && ticks_remaining > 0 && ticks_remaining <= ticks_to_wait);
  483. //Lock the mux in order to make sure no one else is messing with the ringbuffer and do the copy.
  484. portENTER_CRITICAL(&rb->mux);
  485. //Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry
  486. //everything if this is the case. Otherwise, we can write and are done.
  487. done=rb->copyItemToRingbufImpl(rb, data, dataSize);
  488. portEXIT_CRITICAL(&rb->mux);
  489. }
  490. xSemaphoreGive(rb->items_buffered_sem);
  491. return pdTRUE;
  492. }
  493. BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t dataSize, BaseType_t *higher_prio_task_awoken)
  494. {
  495. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  496. BaseType_t write_succeeded;
  497. configASSERT(rb);
  498. size_t needed_size=dataSize+sizeof(buf_entry_hdr_t);
  499. portENTER_CRITICAL_ISR(&rb->mux);
  500. if (needed_size>ringbufferFreeMem(rb)) {
  501. //Does not fit in the remaining space in the ringbuffer.
  502. write_succeeded=pdFALSE;
  503. } else {
  504. write_succeeded = rb->copyItemToRingbufImpl(rb, data, dataSize);
  505. }
  506. portEXIT_CRITICAL_ISR(&rb->mux);
  507. if (write_succeeded) {
  508. xSemaphoreGiveFromISR(rb->items_buffered_sem, higher_prio_task_awoken);
  509. }
  510. return write_succeeded;
  511. }
  512. static void *xRingbufferReceiveGeneric(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size)
  513. {
  514. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  515. uint8_t *itemData;
  516. BaseType_t done=pdFALSE;
  517. configASSERT(rb);
  518. while(!done) {
  519. //See if there's any data available. If not, wait until there is.
  520. while (rb->read_ptr == rb->write_ptr) {
  521. BaseType_t r=xSemaphoreTake(rb->items_buffered_sem, ticks_to_wait);
  522. if (r == pdFALSE) {
  523. //Timeout.
  524. return NULL;
  525. }
  526. }
  527. //Okay, we seem to have data in the buffer. Grab the mux and copy it out if it's still there.
  528. portENTER_CRITICAL(&rb->mux);
  529. itemData=rb->getItemFromRingbufImpl(rb, item_size, wanted_size);
  530. portEXIT_CRITICAL(&rb->mux);
  531. if (itemData) {
  532. //We managed to get an item.
  533. done=pdTRUE;
  534. }
  535. }
  536. return (void*)itemData;
  537. }
  538. void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait)
  539. {
  540. return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, 0);
  541. }
  542. void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size)
  543. {
  544. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  545. uint8_t *itemData;
  546. configASSERT(rb);
  547. portENTER_CRITICAL_ISR(&rb->mux);
  548. itemData=rb->getItemFromRingbufImpl(rb, item_size, 0);
  549. portEXIT_CRITICAL_ISR(&rb->mux);
  550. return (void*)itemData;
  551. }
  552. void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size) {
  553. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  554. if (wanted_size == 0) return NULL;
  555. configASSERT(rb);
  556. configASSERT(rb->flags & flag_bytebuf);
  557. return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, wanted_size);
  558. }
  559. void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size)
  560. {
  561. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  562. uint8_t *itemData;
  563. if (wanted_size == 0) return NULL;
  564. configASSERT(rb);
  565. configASSERT(rb->flags & flag_bytebuf);
  566. portENTER_CRITICAL_ISR(&rb->mux);
  567. itemData=rb->getItemFromRingbufImpl(rb, item_size, wanted_size);
  568. portEXIT_CRITICAL_ISR(&rb->mux);
  569. return (void*)itemData;
  570. }
  571. void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item)
  572. {
  573. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  574. portENTER_CRITICAL(&rb->mux);
  575. rb->returnItemToRingbufImpl(rb, item);
  576. portEXIT_CRITICAL(&rb->mux);
  577. xSemaphoreGive(rb->free_space_sem);
  578. }
  579. void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_t *higher_prio_task_awoken)
  580. {
  581. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  582. portENTER_CRITICAL_ISR(&rb->mux);
  583. rb->returnItemToRingbufImpl(rb, item);
  584. portEXIT_CRITICAL_ISR(&rb->mux);
  585. xSemaphoreGiveFromISR(rb->free_space_sem, higher_prio_task_awoken);
  586. }
  587. BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet)
  588. {
  589. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  590. configASSERT(rb);
  591. return xQueueAddToSet(rb->items_buffered_sem, xQueueSet);
  592. }
  593. BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet)
  594. {
  595. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  596. configASSERT(rb);
  597. return xQueueAddToSet(rb->free_space_sem, xQueueSet);
  598. }
  599. BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet)
  600. {
  601. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  602. configASSERT(rb);
  603. return xQueueRemoveFromSet(rb->items_buffered_sem, xQueueSet);
  604. }
  605. BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet)
  606. {
  607. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  608. configASSERT(rb);
  609. return xQueueRemoveFromSet(rb->free_space_sem, xQueueSet);
  610. }