1 /* 2 * Copyright 2005-2008, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ring_buffer.h" 8 9 #include <KernelExport.h> 10 #if 0 11 #include <port.h> 12 #endif 13 14 #include <stdlib.h> 15 #include <string.h> 16 17 #ifndef HAIKU_TARGET_PLATFORM_HAIKU 18 #define user_memcpy(x...) (memcpy(x), B_OK) 19 #endif 20 21 /*! This is a light-weight ring_buffer implementation. 22 * It does not provide any locking - you are supposed to ensure thread-safety 23 * with the restrictions you choose. Unless you are passing in unsafe buffers, 24 * the functions are safe to be called with interrupts turned off as well (not 25 * the user functions). 26 * They also don't use malloc() or any kind of locking after initialization. 27 */ 28 29 30 static inline int32 31 space_left_in_buffer(struct ring_buffer *buffer) 32 { 33 return buffer->size - buffer->in; 34 } 35 36 37 static ssize_t 38 read_from_buffer(struct ring_buffer *buffer, uint8 *data, ssize_t length, 39 bool user) 40 { 41 int32 available = buffer->in; 42 43 if (length > available) 44 length = available; 45 46 if (length == 0) 47 return 0; 48 49 ssize_t bytesRead = length; 50 51 if (buffer->first + length <= buffer->size) { 52 // simple copy 53 if (user) { 54 if (user_memcpy(data, buffer->buffer + buffer->first, length) < B_OK) 55 return B_BAD_ADDRESS; 56 } else 57 memcpy(data, buffer->buffer + buffer->first, length); 58 } else { 59 // need to copy both ends 60 size_t upper = buffer->size - buffer->first; 61 size_t lower = length - upper; 62 63 if (user) { 64 if (user_memcpy(data, buffer->buffer + buffer->first, upper) < B_OK 65 || user_memcpy(data + upper, buffer->buffer, lower) < B_OK) 66 return B_BAD_ADDRESS; 67 } else { 68 memcpy(data, buffer->buffer + buffer->first, upper); 69 memcpy(data + upper, buffer->buffer, lower); 70 } 71 } 72 73 buffer->first = (buffer->first + bytesRead) % buffer->size; 74 buffer->in -= bytesRead; 75 76 return bytesRead; 77 } 78 79 80 static ssize_t 81 write_to_buffer(struct ring_buffer *buffer, const uint8 *data, ssize_t length, 82 bool user) 83 { 84 int32 left = space_left_in_buffer(buffer); 85 if (length > left) 86 length = left; 87 88 if (length == 0) 89 return 0; 90 91 ssize_t bytesWritten = length; 92 int32 position = (buffer->first + buffer->in) % buffer->size; 93 94 if (position + length <= buffer->size) { 95 // simple copy 96 if (user) { 97 if (user_memcpy(buffer->buffer + position, data, length) < B_OK) 98 return B_BAD_ADDRESS; 99 } else 100 memcpy(buffer->buffer + position, data, length); 101 } else { 102 // need to copy both ends 103 size_t upper = buffer->size - position; 104 size_t lower = length - upper; 105 106 if (user) { 107 if (user_memcpy(buffer->buffer + position, data, upper) < B_OK 108 || user_memcpy(buffer->buffer, data + upper, lower) < B_OK) 109 return B_BAD_ADDRESS; 110 } else { 111 memcpy(buffer->buffer + position, data, upper); 112 memcpy(buffer->buffer, data + upper, lower); 113 } 114 } 115 116 buffer->in += bytesWritten; 117 118 return bytesWritten; 119 } 120 121 122 // #pragma mark - 123 124 125 struct ring_buffer * 126 create_ring_buffer(size_t size) 127 { 128 struct ring_buffer *buffer = (ring_buffer *)malloc(sizeof(ring_buffer) + size); 129 if (buffer == NULL) 130 return NULL; 131 132 buffer->size = size; 133 ring_buffer_clear(buffer); 134 135 return buffer; 136 } 137 138 139 void 140 delete_ring_buffer(struct ring_buffer *buffer) 141 { 142 free(buffer); 143 } 144 145 146 void 147 ring_buffer_clear(struct ring_buffer *buffer) 148 { 149 buffer->in = 0; 150 buffer->first = 0; 151 } 152 153 154 size_t 155 ring_buffer_readable(struct ring_buffer *buffer) 156 { 157 return buffer->in; 158 } 159 160 161 size_t 162 ring_buffer_writable(struct ring_buffer *buffer) 163 { 164 return buffer->size - buffer->in; 165 } 166 167 168 void 169 ring_buffer_flush(struct ring_buffer *buffer, size_t length) 170 { 171 // we can't flush more bytes than there are 172 if (length > (size_t)buffer->in) 173 length = buffer->in; 174 175 buffer->in -= length; 176 buffer->first = (buffer->first + length) % buffer->size; 177 } 178 179 180 size_t 181 ring_buffer_read(struct ring_buffer *buffer, uint8 *data, ssize_t length) 182 { 183 return read_from_buffer(buffer, data, length, false); 184 } 185 186 187 size_t 188 ring_buffer_write(struct ring_buffer *buffer, const uint8 *data, ssize_t length) 189 { 190 return write_to_buffer(buffer, data, length, false); 191 } 192 193 194 ssize_t 195 ring_buffer_user_read(struct ring_buffer *buffer, uint8 *data, ssize_t length) 196 { 197 return read_from_buffer(buffer, data, length, true); 198 } 199 200 201 ssize_t 202 ring_buffer_user_write(struct ring_buffer *buffer, const uint8 *data, ssize_t length) 203 { 204 return write_to_buffer(buffer, data, length, true); 205 } 206 207 208 #if 0 209 /** Sends the contents of the ring buffer to a port. 210 * The buffer will be empty afterwards only if sending the data actually works. 211 */ 212 213 status_t 214 ring_buffer_write_to_port(struct ring_buffer *buffer, port_id port, int32 code, 215 uint32 flags, bigtime_t timeout) 216 { 217 int32 length = buffer->in; 218 if (length == 0) 219 return B_OK; 220 221 status_t status; 222 223 if (buffer->first + length <= buffer->size) { 224 // simple write 225 status = write_port_etc(port, code, buffer->buffer + buffer->first, length, 226 flags, timeout); 227 } else { 228 // need to write both ends 229 size_t upper = buffer->size - buffer->first; 230 size_t lower = length - upper; 231 232 iovec vecs[2]; 233 vecs[0].iov_base = buffer->buffer + buffer->first; 234 vecs[0].iov_len = upper; 235 vecs[1].iov_base = buffer->buffer; 236 vecs[1].iov_len = lower; 237 238 status = writev_port_etc(port, code, vecs, 2, length, flags, timeout); 239 } 240 241 if (status < B_OK) 242 return status; 243 244 buffer->first = (buffer->first + length) % buffer->size; 245 buffer->in -= length; 246 247 return status; 248 } 249 #endif 250