dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
circqueue.c
Go to the documentation of this file.
1 
7 /*
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16  * for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, see <http://www.gnu.org/licenses/>
20  *
21  * Additional note on redistribution: The copyright and license notices above
22  * must be maintained in each individual source file that is a derivative work
23  * of this source file; otherwise redistribution is prohibited.
24  */
25 
26 #include <circqueue.h>
27 
28 struct circ_queue {
29  uint16_t elem_size;
30  uint16_t num_elem;
31  volatile uint16_t write_head;
32  volatile uint16_t read_tail;
34  /* head == tail: empty.
35  * head == tail-1: full.
36  */
37 
38  /* This is declared as a uint32_t for alignment reasons. */
39  uint32_t contents[];
40 };
41 
48 circ_queue_t circ_queue_new(uint16_t elem_size, uint16_t num_elem) {
49  PIOS_Assert(elem_size > 0);
50  PIOS_Assert(num_elem >= 2);
51 
52  uint32_t size = elem_size * num_elem;
53 
54  /* PIOS_malloc_no_dma may not be safe for some later uses.. hmmm */
55  struct circ_queue *ret = PIOS_malloc(sizeof(*ret) + size);
56 
57  if (!ret)
58  return NULL;
59 
60  memset(ret, 0, sizeof(*ret) + size);
61 
62  ret->elem_size = elem_size;
63  ret->num_elem = num_elem;
64 
65  return ret;
66 }
67 
84 void *circ_queue_write_pos(circ_queue_t q, uint16_t *contig,
85  uint16_t *avail) {
86  void *contents = q->contents;
87  uint16_t wr_head = q->write_head;
88  uint16_t rd_tail = q->read_tail;
89 
90  if (contig) {
91  if (rd_tail <= wr_head) {
92  /* Avail is the num elems to the end of the buf */
93  int16_t offset = 0;
94 
95  if (rd_tail == 0) {
96  /* Can't advance to the beginning, because
97  * we'd catch our tail. [only when tail
98  * perfectly wrapped] */
99  offset = -1;
100  }
101  *contig = q->num_elem - wr_head + offset;
102  } else {
103  /* rd_tail > wr_head */
104  /* wr_head is not allowed to advance to meet tail,
105  * so minus one */
106  *contig = rd_tail - wr_head - 1;
107  }
108  }
109 
110  if (avail) {
111  if (rd_tail <= wr_head) {
112  /* To end of buf, to rd_tail, minus one. */
113  *avail = q->num_elem - wr_head + rd_tail - 1;
114  } else {
115  /* Otherwise just to 1 before rd_tail. */
116  *avail = rd_tail - wr_head - 1;
117  }
118  }
119 
120  return contents + wr_head * q->elem_size;
121 }
122 
123 static inline uint16_t advance_by_n(uint16_t num_pos, uint16_t current_pos,
124  uint16_t num_to_advance) {
125  PIOS_Assert(current_pos < num_pos);
126  PIOS_Assert(num_to_advance <= num_pos);
127 
128  uint32_t pos = current_pos + num_to_advance;
129 
130  if (pos >= num_pos) {
131  pos -= num_pos;
132  }
133 
134  return pos;
135 
136 }
137 
138 static inline uint16_t next_pos(uint16_t num_pos, uint16_t current_pos) {
139  return advance_by_n(num_pos, current_pos, 1);
140 }
141 
150  if (amt == 0) {
151  return 0;
152  }
153 
154  uint16_t orig_wr_head = q->write_head;
155 
156  uint16_t new_write_head = advance_by_n(q->num_elem, orig_wr_head,
157  amt);
158 
159  /* Legal states at the end of this are---
160  * a "later place" in the buffer without wrapping.
161  * or the 0th position-- if we've consumed all to the end.
162  */
163 
164  PIOS_Assert((new_write_head > orig_wr_head) || (new_write_head == 0));
165 
166  /* the head is not allowed to advance to meet the tail */
167  if (new_write_head == q->read_tail) {
168  /* This is only sane if they're trying to return one, like
169  * advance_write does */
170  PIOS_Assert(amt == 1);
171 
172  return -1; /* Full */
173 
174  /* Caller can either let the data go away, or try again to
175  * advance later. */
176  }
177 
178  q->write_head = new_write_head;
179 
180  return 0;
181 }
182 
193  return circ_queue_advance_write_multi(q, 1);
194 }
195 
208 void *circ_queue_read_pos(circ_queue_t q, uint16_t *contig, uint16_t *avail) {
209  uint16_t read_tail = q->read_tail;
210  uint16_t wr_head = q->write_head;
211 
212  void *contents = q->contents;
213 
214  if (contig) {
215  if (wr_head >= read_tail) {
216  /* read_tail is allowed to advance to meet head,
217  * so no minus one here. */
218  *contig = wr_head - read_tail;
219  } else {
220  /* Number of contiguous elements to end of the buf,
221  * otherwise. */
222  *contig = q->num_elem - read_tail;
223  }
224  }
225 
226  if (avail) {
227  if (wr_head >= read_tail) {
228  /* Same as immediately above; no wrap avail */
229  *avail = wr_head - read_tail;
230  } else {
231  /* Distance to end, plus distance from beginning
232  * to wr_head */
233  *avail = q->num_elem - read_tail + wr_head;
234  }
235  }
236 
237  if (wr_head == read_tail) {
238  /* There is nothing new to read. */
239  return NULL;
240  }
241 
242  return contents + q->read_tail * q->elem_size;
243 }
244 
247  q->read_tail = q->write_head;
248 }
249 
257  /* Avoid multiple accesses to a volatile */
258  uint16_t read_tail = q->read_tail;
259 
260  /* If this is being called, the queue had better not be empty--
261  * we're supposed to finish consuming this element after a prior call
262  * to circ_queue_read_pos.
263  */
264  PIOS_Assert(read_tail != q->write_head);
265 
266  q->read_tail = next_pos(q->num_elem, read_tail);
267 }
268 
277  if (num == 0) {
278  return;
279  }
280 
281  /* Avoid multiple accesses to a volatile */
282  uint16_t orig_read_tail = q->read_tail;
283 
284  /* If this is being called, the queue had better not be empty--
285  * we're supposed to finish consuming this element after a prior call
286  * to circ_queue_read_pos.
287  */
288  PIOS_Assert(orig_read_tail != q->write_head);
289 
290  uint16_t read_tail = advance_by_n(q->num_elem, orig_read_tail, num);
291 
292  /* Legal states at the end of this are---
293  * a "later place" in the buffer without wrapping.
294  * or the 0th position-- if we've consumed all to the end.
295  */
296 
297  PIOS_Assert((read_tail > orig_read_tail) || (read_tail == 0));
298 
299  q->read_tail = read_tail;
300 }
301 
302 uint16_t circ_queue_write_data(circ_queue_t q, const void *buf, uint16_t num) {
303  uint16_t total_put = 0;
304  uint16_t put_this_time = 0;
305 
306  const void *buf_pos = buf;
307 
308  do {
309  void *wr_pos = circ_queue_write_pos(q, &put_this_time, NULL);
310 
311  if (!put_this_time) break;
312 
313  if (put_this_time > (num - total_put)) {
314  put_this_time = num - total_put;
315  }
316 
317  uint32_t sz = put_this_time * q->elem_size;
318 
319  memcpy(wr_pos, buf_pos, sz);
320 
321  circ_queue_advance_write_multi(q, put_this_time);
322 
323  buf_pos += sz;
324 
325  total_put += put_this_time;
326  } while (total_put < num);
327 
328  return total_put;
329 }
330 
331 uint16_t circ_queue_read_data(circ_queue_t q, void *buf, uint16_t num) {
332  uint16_t total_read = 0;
333  uint16_t read_this_time = 0;
334 
335  void *buf_pos = buf;
336 
337  do {
338  void *rd_pos = circ_queue_read_pos(q, &read_this_time, NULL);
339 
340  if (!read_this_time) break;
341 
342  if (read_this_time > (num - total_read)) {
343  read_this_time = num - total_read;
344  }
345 
346  uint32_t sz = read_this_time * q->elem_size;
347 
348  memcpy(buf_pos, rd_pos, sz);
349 
350  circ_queue_read_completed_multi(q, read_this_time);
351 
352  buf_pos += sz;
353 
354  total_read += read_this_time;
355  } while (total_read < num);
356 
357  return total_read;
358 }
359 
uint16_t circ_queue_read_data(circ_queue_t q, void *buf, uint16_t num)
Definition: circqueue.c:331
volatile uint16_t write_head
Definition: circqueue.c:31
uint16_t circ_queue_write_data(circ_queue_t q, const void *buf, uint16_t num)
Definition: circqueue.c:302
void * PIOS_malloc(size_t size)
Definition: pios_heap.c:125
void circ_queue_read_completed_multi(circ_queue_t q, uint16_t num)
Definition: circqueue.c:276
uint32_t contents[]
Definition: circqueue.c:39
void * circ_queue_write_pos(circ_queue_t q, uint16_t *contig, uint16_t *avail)
Definition: circqueue.c:84
static uint16_t advance_by_n(uint16_t num_pos, uint16_t current_pos, uint16_t num_to_advance)
Definition: circqueue.c:123
circ_queue_t circ_queue_new(uint16_t elem_size, uint16_t num_elem)
Definition: circqueue.c:48
uint16_t num_elem
Definition: circqueue.c:30
struct _msp_pid_item pos
Definition: msp_messages.h:100
void circ_queue_read_completed(circ_queue_t q)
Definition: circqueue.c:256
Public header for 1 reader, 1 writer circular queue.
uint32_t offset
Definition: uavtalk_priv.h:51
int circ_queue_advance_write(circ_queue_t q)
Definition: circqueue.c:192
int circ_queue_advance_write_multi(circ_queue_t q, uint16_t amt)
Definition: circqueue.c:149
void circ_queue_clear(circ_queue_t q)
Definition: circqueue.c:246
uint16_t elem_size
Definition: circqueue.c:29
volatile uint16_t read_tail
Definition: circqueue.c:32
#define PIOS_Assert(test)
Definition: pios_debug.h:52
void * circ_queue_read_pos(circ_queue_t q, uint16_t *contig, uint16_t *avail)
Definition: circqueue.c:208
static uint16_t next_pos(uint16_t num_pos, uint16_t current_pos)
Definition: circqueue.c:138