ringbuf.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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 <stdint.h>
  20. #include <string.h>
  21. #include <stdlib.h>
  22. #include <stdio.h>
  23. typedef enum {
  24. flag_allowsplit = 1,
  25. } rbflag_t;
  26. typedef enum {
  27. iflag_free = 1, //Buffer is not read and given back by application, free to overwrite
  28. iflag_dummydata = 2, //Data from here to end of ringbuffer is dummy. Restart reading at start of ringbuffer.
  29. } itemflag_t;
  30. //The ringbuffer structure
  31. typedef struct {
  32. SemaphoreHandle_t free_space_sem; //Binary semaphore, wakes up writing threads when there's more free space
  33. SemaphoreHandle_t items_buffered_sem; //Binary semaphore, indicates there are new packets in the circular buffer. See remark.
  34. size_t size; //Size of the data storage
  35. uint8_t *write_ptr; //Pointer where the next item is written
  36. uint8_t *read_ptr; //Pointer from where the next item is read
  37. uint8_t *free_ptr; //Pointer to the last block that hasn't been given back to the ringbuffer yet
  38. uint8_t *data; //Data storage
  39. portMUX_TYPE mux; //Spinlock for actual data/ptr/struct modification
  40. rbflag_t flags;
  41. } ringbuf_t;
  42. /*
  43. Remark: A counting semaphore for items_buffered_sem would be more logical, but counting semaphores in
  44. FreeRTOS need a maximum count, and allocate more memory the larger the maximum count is. Here, we
  45. would need to set the maximum to the maximum amount of times a null-byte unit firs in the buffer,
  46. which is quite high and so would waste a fair amount of memory.
  47. */
  48. //The header prepended to each ringbuffer entry. Size is assumed to be a multiple of 32bits.
  49. typedef struct {
  50. size_t len;
  51. itemflag_t flags;
  52. } buf_entry_hdr_t;
  53. //Calculate space free in the buffer
  54. static int ringbufferFreeMem(ringbuf_t *rb)
  55. {
  56. int free_size = rb->free_ptr-rb->write_ptr;
  57. if (free_size <= 0) free_size += rb->size;
  58. //Reserve one byte. If we do not do this and the entire buffer is filled, we get a situation
  59. //where read_ptr == free_ptr, messing up the next calculation.
  60. return free_size-1;
  61. }
  62. //Copies a single item to the ring buffer. Assumes there is space in the ringbuffer and
  63. //the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on
  64. //success, pdFALSE if it can't make the item fit and the calling routine needs to retry
  65. //later or fail.
  66. //This function by itself is not threadsafe, always call from within a muxed section.
  67. static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
  68. {
  69. size_t rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value
  70. configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned
  71. configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size
  72. //of a header to the end of the ringbuff
  73. size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer
  74. //See if we have enough contiguous space to write the buffer.
  75. if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) {
  76. //The buffer can't be contiguously written to the ringbuffer, but needs special handling. Do
  77. //that depending on how the ringbuffer is configured.
  78. //The code here is also expected to check if the buffer, mangled in whatever way is implemented,
  79. //will still fit, and return pdFALSE if that is not the case.
  80. if (rb->flags & flag_allowsplit) {
  81. //Buffer plus header is not going to fit in the room from wr_pos to the end of the
  82. //ringbuffer... we need to split the write in two.
  83. //First, see if this will fit at all.
  84. if (ringbufferFreeMem(rb) < (sizeof(buf_entry_hdr_t)*2)+rbuffer_size) {
  85. //Will not fit.
  86. return pdFALSE;
  87. }
  88. //Because the code at the end of the function makes sure we always have
  89. //room for a header, this should never assert.
  90. configASSERT(rem_len>=sizeof(buf_entry_hdr_t));
  91. //Okay, it should fit. Write everything.
  92. //First, place bit of buffer that does fit. Write header first...
  93. buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
  94. hdr->flags=0;
  95. hdr->len=rem_len-sizeof(buf_entry_hdr_t);
  96. rb->write_ptr+=sizeof(buf_entry_hdr_t);
  97. rem_len-=sizeof(buf_entry_hdr_t);
  98. if (rem_len!=0) {
  99. //..then write the data bit that fits.
  100. memcpy(rb->write_ptr, buffer, rem_len);
  101. //Update vars so the code later on will write the rest of the data.
  102. buffer+=rem_len;
  103. rbuffer_size-=rem_len;
  104. buffer_size-=rem_len;
  105. } else {
  106. //Huh, only the header fit. Mark as dummy so the receive function doesn't receive
  107. //an useless zero-byte packet.
  108. hdr->flags|=iflag_dummydata;
  109. }
  110. rb->write_ptr=rb->data;
  111. } else {
  112. //Buffer plus header is not going to fit in the room from wr_pos to the end of the
  113. //ringbuffer... but we're not allowed to split the buffer. We need to fill the
  114. //rest of the ringbuffer with a dummy item so we can place the data at the _start_ of
  115. //the ringbuffer..
  116. //First, find out if we actually have enough space at the start of the ringbuffer to
  117. //make this work (Again, we need 4 bytes extra because otherwise read_ptr==free_ptr)
  118. if (rb->free_ptr-rb->data < rbuffer_size+sizeof(buf_entry_hdr_t)+4) {
  119. //Will not fit.
  120. return pdFALSE;
  121. }
  122. //If the read buffer hasn't wrapped around yet, there's no way this will work either.
  123. if (rb->free_ptr > rb->write_ptr) {
  124. //No luck.
  125. return pdFALSE;
  126. }
  127. //Okay, it will fit. Mark the rest of the ringbuffer space with a dummy packet.
  128. buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
  129. hdr->flags=iflag_dummydata;
  130. //Reset the write pointer to the start of the ringbuffer so the code later on can
  131. //happily write the data.
  132. rb->write_ptr=rb->data;
  133. }
  134. } else {
  135. //No special handling needed. Checking if it's gonna fit probably still is a good idea.
  136. if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) {
  137. //Buffer is not going to fit, period.
  138. return pdFALSE;
  139. }
  140. }
  141. //If we are here, the buffer is guaranteed to fit in the space starting at the write pointer.
  142. buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
  143. hdr->len=buffer_size;
  144. hdr->flags=0;
  145. rb->write_ptr+=sizeof(buf_entry_hdr_t);
  146. memcpy(rb->write_ptr, buffer, buffer_size);
  147. rb->write_ptr+=rbuffer_size;
  148. //The buffer will wrap around if we don't have room for a header anymore.
  149. if ((rb->data+rb->size)-rb->write_ptr < sizeof(buf_entry_hdr_t)) {
  150. //'Forward' the write buffer until we are at the start of the ringbuffer.
  151. //The read pointer will always be at the start of a full header, which cannot
  152. //exist at the point of the current write pointer, so there's no chance of overtaking
  153. //that.
  154. rb->write_ptr=rb->data;
  155. }
  156. return pdTRUE;
  157. }
  158. //Retrieves a pointer to the data of the next item, or NULL if this is not possible.
  159. //This function by itself is not threadsafe, always call from within a muxed section.
  160. static uint8_t *getItemFromRingbuf(ringbuf_t *rb, size_t *length)
  161. {
  162. uint8_t *ret;
  163. configASSERT(((int)rb->read_ptr&3)==0);
  164. if (rb->read_ptr == rb->write_ptr) {
  165. //No data available.
  166. return NULL;
  167. }
  168. //The item written at the point of the read pointer may be a dummy item.
  169. //We need to skip past it first, if that's the case.
  170. buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->read_ptr;
  171. configASSERT((hdr->len < rb->size) || (hdr->flags & iflag_dummydata));
  172. if (hdr->flags & iflag_dummydata) {
  173. //Hdr is dummy data. Reset to start of ringbuffer.
  174. rb->read_ptr=rb->data;
  175. //Get real header
  176. hdr=(buf_entry_hdr_t *)rb->read_ptr;
  177. configASSERT(hdr->len < rb->size);
  178. //No need to re-check if the ringbuffer is empty: the write routine will
  179. //always write a dummy item plus the real data item in one go, so now we must
  180. //be at the real data item by definition.
  181. }
  182. //Okay, pass the data back.
  183. ret=rb->read_ptr+sizeof(buf_entry_hdr_t);
  184. *length=hdr->len;
  185. //...and move the read pointer past the data.
  186. rb->read_ptr+=sizeof(buf_entry_hdr_t)+((hdr->len+3)&~3);
  187. //The buffer will wrap around if we don't have room for a header anymore.
  188. if ((rb->data + rb->size) - rb->read_ptr < sizeof(buf_entry_hdr_t)) {
  189. rb->read_ptr=rb->data;
  190. }
  191. return ret;
  192. }
  193. //Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer
  194. //can be increase.
  195. //This function by itself is not threadsafe, always call from within a muxed section.
  196. static void returnItemToRingbuf(ringbuf_t *rb, void *item) {
  197. uint8_t *data=(uint8_t*)item;
  198. configASSERT(((int)rb->free_ptr&3)==0);
  199. configASSERT(data >= rb->data);
  200. configASSERT(data < rb->data+rb->size);
  201. //Grab the buffer entry that preceeds the buffer
  202. buf_entry_hdr_t *hdr=(buf_entry_hdr_t*)(data-sizeof(buf_entry_hdr_t));
  203. configASSERT(hdr->len < rb->size);
  204. configASSERT((hdr->flags & iflag_dummydata)==0);
  205. configASSERT((hdr->flags & iflag_free)==0);
  206. //Mark the buffer as free.
  207. hdr->flags|=iflag_free;
  208. //Do a cleanup pass.
  209. hdr=(buf_entry_hdr_t *)rb->free_ptr;
  210. //basically forward free_ptr until we run into either a block that is still in use or the write pointer.
  211. while (((hdr->flags & iflag_free) || (hdr->flags & iflag_dummydata)) && rb->free_ptr != rb->write_ptr) {
  212. if (hdr->flags & iflag_dummydata) {
  213. //Rest is dummy data. Reset to start of ringbuffer.
  214. rb->free_ptr=rb->data;
  215. } else {
  216. //Skip past item
  217. size_t len=(hdr->len+3)&~3;
  218. rb->free_ptr+=len+sizeof(buf_entry_hdr_t);
  219. configASSERT(rb->free_ptr<=rb->data+rb->size);
  220. }
  221. //The buffer will wrap around if we don't have room for a header anymore.
  222. if ((rb->data+rb->size)-rb->free_ptr < sizeof(buf_entry_hdr_t)) {
  223. rb->free_ptr=rb->data;
  224. }
  225. //Next header
  226. hdr=(buf_entry_hdr_t *)rb->free_ptr;
  227. }
  228. }
  229. void xRingbufferPrintInfo(RingbufHandle_t ringbuf)
  230. {
  231. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  232. configASSERT(rb);
  233. ets_printf("Rb size %d free %d rptr %d freeptr %d wptr %d\n",
  234. rb->size, ringbufferFreeMem(rb), rb->read_ptr-rb->data, rb->free_ptr-rb->data, rb->write_ptr-rb->data);
  235. }
  236. RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_items)
  237. {
  238. ringbuf_t *rb = malloc(sizeof(ringbuf_t));
  239. if (rb==NULL) goto err;
  240. memset(rb, 0, sizeof(ringbuf_t));
  241. rb->data = malloc(buf_length);
  242. if (rb->data == NULL) goto err;
  243. rb->size = buf_length;
  244. rb->free_ptr = rb->data;
  245. rb->read_ptr = rb->data;
  246. rb->write_ptr = rb->data;
  247. rb->free_space_sem = xSemaphoreCreateBinary();
  248. rb->items_buffered_sem = xSemaphoreCreateBinary();
  249. rb->flags=0;
  250. if (allow_split_items) rb->flags|=flag_allowsplit;
  251. if (rb->free_space_sem == NULL || rb->items_buffered_sem == NULL) goto err;
  252. vPortCPUInitializeMutex(&rb->mux);
  253. return (RingbufHandle_t)rb;
  254. err:
  255. //Some error has happened. Free/destroy all allocated things and return NULL.
  256. if (rb) {
  257. free(rb->data);
  258. if (rb->free_space_sem) vSemaphoreDelete(rb->free_space_sem);
  259. if (rb->items_buffered_sem) vSemaphoreDelete(rb->items_buffered_sem);
  260. }
  261. free(rb);
  262. return NULL;
  263. }
  264. void vRingbufferDelete(RingbufHandle_t ringbuf) {
  265. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  266. if (rb) {
  267. free(rb->data);
  268. if (rb->free_space_sem) vSemaphoreDelete(rb->free_space_sem);
  269. if (rb->items_buffered_sem) vSemaphoreDelete(rb->items_buffered_sem);
  270. }
  271. free(rb);
  272. }
  273. size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf)
  274. {
  275. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  276. configASSERT(rb);
  277. //In both cases, we return 4 bytes less than what we actually can have. If the ringbuffer is
  278. //indeed entirely filled, read_ptr==free_ptr, which throws off the free space calculation.
  279. if (rb->flags & flag_allowsplit) {
  280. //Worst case, we need to split an item into two, which means two headers of overhead.
  281. return rb->size-(sizeof(buf_entry_hdr_t)*2)-4;
  282. } else {
  283. //Worst case, we have the write ptr in such a position that we are lacking four bytes of free
  284. //memory to put an item into the rest of the memory. If this happens, we have to dummy-fill
  285. //(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill
  286. //with the real item. (item size being header+data)
  287. return (rb->size/2)-sizeof(buf_entry_hdr_t)-4;
  288. }
  289. }
  290. BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, TickType_t ticks_to_wait)
  291. {
  292. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  293. size_t needed_size=dataSize+sizeof(buf_entry_hdr_t);
  294. BaseType_t done=pdFALSE;
  295. portTickType ticks_end=xTaskGetTickCount() + ticks_to_wait;
  296. configASSERT(rb);
  297. if (dataSize > xRingbufferGetMaxItemSize(ringbuf)) {
  298. //Data will never ever fit in the queue.
  299. return pdFALSE;
  300. }
  301. while (!done) {
  302. //Check if there is enough room in the buffer. If not, wait until there is.
  303. do {
  304. if (ringbufferFreeMem(rb) < needed_size) {
  305. //Data does not fit yet. Wait until the free_space_sem is given, then re-evaluate.
  306. BaseType_t r = xSemaphoreTake(rb->free_space_sem, ticks_to_wait);
  307. if (r == pdFALSE) {
  308. //Timeout.
  309. return pdFALSE;
  310. }
  311. //Adjust ticks_to_wait; we may have waited less than that and in the case the free memory still is not enough,
  312. //we will need to wait some more.
  313. ticks_to_wait = ticks_end - xTaskGetTickCount();
  314. }
  315. } while (ringbufferFreeMem(rb) < needed_size && ticks_to_wait>=0);
  316. //Lock the mux in order to make sure no one else is messing with the ringbuffer and do the copy.
  317. portENTER_CRITICAL(&rb->mux);
  318. //Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry
  319. //everything if this is the case. Otherwise, we can write and are done.
  320. done=copyItemToRingbuf(rb, data, dataSize);
  321. portEXIT_CRITICAL(&rb->mux);
  322. }
  323. xSemaphoreGive(rb->items_buffered_sem);
  324. return pdTRUE;
  325. }
  326. BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t dataSize, BaseType_t *higher_prio_task_awoken)
  327. {
  328. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  329. BaseType_t write_succeeded;
  330. configASSERT(rb);
  331. size_t needed_size=dataSize+sizeof(buf_entry_hdr_t);
  332. portENTER_CRITICAL_ISR(&rb->mux);
  333. if (needed_size>ringbufferFreeMem(rb)) {
  334. //Does not fit in the remaining space in the ringbuffer.
  335. write_succeeded=pdFALSE;
  336. } else {
  337. copyItemToRingbuf(rb, data, dataSize);
  338. write_succeeded=pdTRUE;
  339. }
  340. portEXIT_CRITICAL_ISR(&rb->mux);
  341. if (write_succeeded) {
  342. xSemaphoreGiveFromISR(rb->items_buffered_sem, higher_prio_task_awoken);
  343. }
  344. return write_succeeded;
  345. }
  346. void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait)
  347. {
  348. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  349. uint8_t *itemData;
  350. BaseType_t done=pdFALSE;
  351. configASSERT(rb);
  352. while(!done) {
  353. //See if there's any data available. If not, wait until there is.
  354. while (rb->read_ptr == rb->write_ptr) {
  355. BaseType_t r=xSemaphoreTake(rb->items_buffered_sem, ticks_to_wait);
  356. if (r == pdFALSE) {
  357. //Timeout.
  358. return NULL;
  359. }
  360. }
  361. //Okay, we seem to have data in the buffer. Grab the mux and copy it out if it's still there.
  362. portENTER_CRITICAL(&rb->mux);
  363. itemData=getItemFromRingbuf(rb, item_size);
  364. portEXIT_CRITICAL(&rb->mux);
  365. if (itemData) {
  366. //We managed to get an item.
  367. done=pdTRUE;
  368. }
  369. }
  370. return (void*)itemData;
  371. }
  372. void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size)
  373. {
  374. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  375. uint8_t *itemData;
  376. configASSERT(rb);
  377. portENTER_CRITICAL_ISR(&rb->mux);
  378. itemData=getItemFromRingbuf(rb, item_size);
  379. portEXIT_CRITICAL_ISR(&rb->mux);
  380. return (void*)itemData;
  381. }
  382. void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item)
  383. {
  384. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  385. portENTER_CRITICAL_ISR(&rb->mux);
  386. returnItemToRingbuf(rb, item);
  387. portEXIT_CRITICAL_ISR(&rb->mux);
  388. xSemaphoreGive(rb->free_space_sem);
  389. }
  390. void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_t *higher_prio_task_awoken)
  391. {
  392. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  393. portENTER_CRITICAL_ISR(&rb->mux);
  394. returnItemToRingbuf(rb, item);
  395. portEXIT_CRITICAL_ISR(&rb->mux);
  396. xSemaphoreGiveFromISR(rb->free_space_sem, higher_prio_task_awoken);
  397. }
  398. BaseType_t xRingbufferAddToQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet)
  399. {
  400. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  401. configASSERT(rb);
  402. return xQueueAddToSet(rb->items_buffered_sem, xQueueSet);
  403. }
  404. BaseType_t xRingbufferAddToQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet)
  405. {
  406. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  407. configASSERT(rb);
  408. return xQueueAddToSet(rb->free_space_sem, xQueueSet);
  409. }
  410. BaseType_t xRingbufferRemoveFromQueueSetRead(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet)
  411. {
  412. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  413. configASSERT(rb);
  414. return xQueueRemoveFromSet(rb->items_buffered_sem, xQueueSet);
  415. }
  416. BaseType_t xRingbufferRemoveFromQueueSetWrite(RingbufHandle_t ringbuf, QueueSetHandle_t xQueueSet)
  417. {
  418. ringbuf_t *rb=(ringbuf_t *)ringbuf;
  419. configASSERT(rb);
  420. return xQueueRemoveFromSet(rb->free_space_sem, xQueueSet);
  421. }