|
27 | 27 | #include <string.h> |
28 | 28 | #include "wiring.h" |
29 | 29 |
|
30 | | -#define BUFFER_END (VIRTIO_BUFFER_SIZE - 1) |
31 | | - |
32 | 30 | void virtio_buffer_init(virtio_buffer_t *ring) |
33 | 31 | { |
34 | | - ring->write = 0; |
35 | | - ring->read = 0; |
| 32 | + ring->write_index = 0; |
| 33 | + ring->read_index = 0; |
36 | 34 | } |
37 | 35 |
|
38 | 36 | uint16_t virtio_buffer_read_available(virtio_buffer_t *ring) |
39 | 37 | { |
40 | | - // This will make the function safe when write operations are done in interrupts |
41 | | - volatile uint16_t write = ring->write; |
| 38 | + int32_t delta = ring->write_index - ring->read_index; |
42 | 39 |
|
43 | | - if (write < ring->read) { |
44 | | - return (BUFFER_END - ring->read) + (write + 1); |
| 40 | + if (delta < 0) { |
| 41 | + return (VIRTIO_BUFFER_SIZE + delta); |
45 | 42 | } |
46 | | - return write - ring->read; |
| 43 | + return delta; |
47 | 44 | } |
48 | 45 |
|
| 46 | +/* WARNING no protection against race codition (on ring->read_index) when used in interruption */ |
49 | 47 | static uint16_t read(virtio_buffer_t *ring, uint8_t *dst, uint16_t size, bool peek) |
50 | 48 | { |
51 | | - // This will make the function safe when write operations are done in interrupts |
52 | | - volatile uint16_t write = ring->write; |
53 | | - uint16_t end = (write >= ring->read) ? write : BUFFER_END + 1; |
| 49 | + uint16_t read_index = ring->read_index; |
| 50 | + int32_t delta = ring->write_index - read_index; |
| 51 | + if (delta < 0) { |
| 52 | + delta += VIRTIO_BUFFER_SIZE; |
| 53 | + } |
| 54 | + |
| 55 | + size = min(size, delta); |
| 56 | + |
| 57 | + if ((size + read_index) > VIRTIO_BUFFER_SIZE) { |
| 58 | + // Manage ring buffer rollover |
| 59 | + // First, copy ring buffer from read index to end of buffer |
| 60 | + memcpy(dst, ring->buffer + read_index, VIRTIO_BUFFER_SIZE - read_index); |
| 61 | + // then, copy ring buffer from begining of buffer to end of read |
| 62 | + memcpy(dst + VIRTIO_BUFFER_SIZE - read_index, ring->buffer, size - (VIRTIO_BUFFER_SIZE - read_index)); |
| 63 | + } else { |
| 64 | + memcpy(dst, ring->buffer + read_index, size); |
| 65 | + } |
54 | 66 |
|
55 | | - size = min(end - ring->read, size); |
56 | | - memcpy(dst, ring->buffer + ring->read, size); |
| 67 | + // Update read index if not peeked |
57 | 68 | if (!peek) { |
58 | | - ring->read += size; |
| 69 | + ring->read_index += size; |
59 | 70 |
|
60 | | - if (ring->read > BUFFER_END) { |
61 | | - ring->read = 0; |
| 71 | + // Manage ring buffer rollover |
| 72 | + if (ring->read_index >= VIRTIO_BUFFER_SIZE) { |
| 73 | + ring->read_index -= VIRTIO_BUFFER_SIZE; |
62 | 74 | } |
63 | 75 | } |
64 | 76 | return size; |
65 | 77 | } |
66 | 78 |
|
67 | 79 | uint16_t virtio_buffer_read(virtio_buffer_t *ring, uint8_t *dst, uint16_t size) |
68 | 80 | { |
69 | | - uint16_t recv_size = read(ring, dst, size, false); |
70 | | - return recv_size; |
| 81 | + return read(ring, dst, size, false); |
71 | 82 | } |
72 | 83 |
|
73 | | -/** |
74 | | - * WARNING: The size of read cannot be larger than virtio_buffer_read_available(). |
75 | | - */ |
| 84 | + |
76 | 85 | uint16_t virtio_buffer_peek(virtio_buffer_t *ring, uint8_t *dst, uint16_t size) |
77 | 86 | { |
78 | | - size = min(size, virtio_buffer_read_available(ring)); |
79 | | - uint16_t recv_size = 0; |
80 | | - while (recv_size < size) { |
81 | | - recv_size += read(ring, dst + recv_size, size - recv_size, true); |
82 | | - } |
83 | | - return recv_size; |
| 87 | + return read(ring, dst, size, true); |
84 | 88 | } |
85 | 89 |
|
| 90 | + |
| 91 | + |
86 | 92 | uint16_t virtio_buffer_write_available(virtio_buffer_t *ring) |
87 | 93 | { |
88 | | - // This will make the function safe when read operations are done in interrupts |
89 | | - volatile uint16_t read = ring->read; |
| 94 | + int32_t delta = ring->read_index - ring->write_index - 1; |
90 | 95 |
|
91 | | - if (ring->write < read) { |
92 | | - return (read - 1) - ring->write; |
| 96 | + if (delta < 0) { |
| 97 | + return (VIRTIO_BUFFER_SIZE + delta); |
93 | 98 | } |
94 | | - return read + (BUFFER_END - ring->write); |
| 99 | + return delta; |
95 | 100 | } |
96 | 101 |
|
| 102 | +/* WARNING no protection against race codition (on ring->write_index) when used in interruption */ |
97 | 103 | uint16_t virtio_buffer_write(virtio_buffer_t *ring, uint8_t *src, uint16_t size) |
98 | 104 | { |
99 | | - // This will make the function safe when read operations are done in a interrupt |
100 | | - volatile uint16_t read = ring->read; |
101 | | - uint16_t end = (ring->write < read) ? read - 1 |
102 | | - : (read == 0) ? BUFFER_END : BUFFER_END + 1; |
103 | | - |
104 | | - size = min(end - ring->write, size); |
105 | | - memcpy(ring->buffer + ring->write, src, size); |
106 | | - ring->write += size; |
107 | | - if (ring->write > BUFFER_END) { |
108 | | - ring->write = 0; |
| 105 | + uint16_t write_index = ring->write_index; |
| 106 | + int32_t delta = ring->read_index - write_index - 1; |
| 107 | + if (delta < 0) { |
| 108 | + delta += VIRTIO_BUFFER_SIZE; |
| 109 | + } |
| 110 | + |
| 111 | + size = min(size, delta); |
| 112 | + |
| 113 | + if ((size + write_index) > VIRTIO_BUFFER_SIZE) { |
| 114 | + // Manage ring buffer rollover |
| 115 | + // First, write ring buffer from write index to end of buffer |
| 116 | + memcpy(ring->buffer + write_index, src, VIRTIO_BUFFER_SIZE - write_index); |
| 117 | + // then, write ring buffer from beginning of buffer to end of read |
| 118 | + memcpy(ring->buffer, src + VIRTIO_BUFFER_SIZE - write_index, size - (VIRTIO_BUFFER_SIZE - write_index)); |
| 119 | + } else { |
| 120 | + memcpy(ring->buffer + write_index, src, size); |
| 121 | + } |
| 122 | + |
| 123 | + ring->write_index += size; |
| 124 | + if (ring->write_index >= VIRTIO_BUFFER_SIZE) { |
| 125 | + ring->write_index -= VIRTIO_BUFFER_SIZE; |
109 | 126 | } |
110 | 127 | return size; |
111 | 128 | } |
|
0 commit comments