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