dvb_ringbuffer.c (10108B)
1/* 2 * 3 * dvb_ringbuffer.c: ring buffer implementation for the dvb driver 4 * 5 * Copyright (C) 2003 Oliver Endriss 6 * Copyright (C) 2004 Andrew de Quincey 7 * 8 * based on code originally found in av7110.c & dvb_ci.c: 9 * Copyright (C) 1999-2003 Ralph Metzler 10 * & Marcus Metzler for convergence integrated media GmbH 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU Lesser General Public License 14 * as published by the Free Software Foundation; either version 2.1 15 * of the License, or (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU Lesser General Public License for more details. 21 */ 22 23 24 25#include <linux/errno.h> 26#include <linux/kernel.h> 27#include <linux/module.h> 28#include <linux/sched.h> 29#include <linux/string.h> 30#include <linux/uaccess.h> 31 32#include <media/dvb_ringbuffer.h> 33 34#define PKT_READY 0 35#define PKT_DISPOSED 1 36 37 38void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len) 39{ 40 rbuf->pread=rbuf->pwrite=0; 41 rbuf->data=data; 42 rbuf->size=len; 43 rbuf->error=0; 44 45 init_waitqueue_head(&rbuf->queue); 46 47 spin_lock_init(&(rbuf->lock)); 48} 49 50 51 52int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf) 53{ 54 /* smp_load_acquire() to load write pointer on reader side 55 * this pairs with smp_store_release() in dvb_ringbuffer_write(), 56 * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset() 57 * 58 * for memory barriers also see Documentation/core-api/circular-buffers.rst 59 */ 60 return (rbuf->pread == smp_load_acquire(&rbuf->pwrite)); 61} 62 63 64 65ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf) 66{ 67 ssize_t free; 68 69 /* READ_ONCE() to load read pointer on writer side 70 * this pairs with smp_store_release() in dvb_ringbuffer_read(), 71 * dvb_ringbuffer_read_user(), dvb_ringbuffer_flush(), 72 * or dvb_ringbuffer_reset() 73 */ 74 free = READ_ONCE(rbuf->pread) - rbuf->pwrite; 75 if (free <= 0) 76 free += rbuf->size; 77 return free-1; 78} 79 80 81 82ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf) 83{ 84 ssize_t avail; 85 86 /* smp_load_acquire() to load write pointer on reader side 87 * this pairs with smp_store_release() in dvb_ringbuffer_write(), 88 * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset() 89 */ 90 avail = smp_load_acquire(&rbuf->pwrite) - rbuf->pread; 91 if (avail < 0) 92 avail += rbuf->size; 93 return avail; 94} 95 96 97 98void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf) 99{ 100 /* dvb_ringbuffer_flush() counts as read operation 101 * smp_load_acquire() to load write pointer 102 * smp_store_release() to update read pointer, this ensures that the 103 * correct pointer is visible for subsequent dvb_ringbuffer_free() 104 * calls on other cpu cores 105 */ 106 smp_store_release(&rbuf->pread, smp_load_acquire(&rbuf->pwrite)); 107 rbuf->error = 0; 108} 109EXPORT_SYMBOL(dvb_ringbuffer_flush); 110 111void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf) 112{ 113 /* dvb_ringbuffer_reset() counts as read and write operation 114 * smp_store_release() to update read pointer 115 */ 116 smp_store_release(&rbuf->pread, 0); 117 /* smp_store_release() to update write pointer */ 118 smp_store_release(&rbuf->pwrite, 0); 119 rbuf->error = 0; 120} 121 122void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf) 123{ 124 unsigned long flags; 125 126 spin_lock_irqsave(&rbuf->lock, flags); 127 dvb_ringbuffer_flush(rbuf); 128 spin_unlock_irqrestore(&rbuf->lock, flags); 129 130 wake_up(&rbuf->queue); 131} 132 133ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len) 134{ 135 size_t todo = len; 136 size_t split; 137 138 split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; 139 if (split > 0) { 140 if (copy_to_user(buf, rbuf->data+rbuf->pread, split)) 141 return -EFAULT; 142 buf += split; 143 todo -= split; 144 /* smp_store_release() for read pointer update to ensure 145 * that buf is not overwritten until read is complete, 146 * this pairs with READ_ONCE() in dvb_ringbuffer_free() 147 */ 148 smp_store_release(&rbuf->pread, 0); 149 } 150 if (copy_to_user(buf, rbuf->data+rbuf->pread, todo)) 151 return -EFAULT; 152 153 /* smp_store_release() to update read pointer, see above */ 154 smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size); 155 156 return len; 157} 158 159void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len) 160{ 161 size_t todo = len; 162 size_t split; 163 164 split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; 165 if (split > 0) { 166 memcpy(buf, rbuf->data+rbuf->pread, split); 167 buf += split; 168 todo -= split; 169 /* smp_store_release() for read pointer update to ensure 170 * that buf is not overwritten until read is complete, 171 * this pairs with READ_ONCE() in dvb_ringbuffer_free() 172 */ 173 smp_store_release(&rbuf->pread, 0); 174 } 175 memcpy(buf, rbuf->data+rbuf->pread, todo); 176 177 /* smp_store_release() to update read pointer, see above */ 178 smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size); 179} 180 181 182ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len) 183{ 184 size_t todo = len; 185 size_t split; 186 187 split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0; 188 189 if (split > 0) { 190 memcpy(rbuf->data+rbuf->pwrite, buf, split); 191 buf += split; 192 todo -= split; 193 /* smp_store_release() for write pointer update to ensure that 194 * written data is visible on other cpu cores before the pointer 195 * update, this pairs with smp_load_acquire() in 196 * dvb_ringbuffer_empty() or dvb_ringbuffer_avail() 197 */ 198 smp_store_release(&rbuf->pwrite, 0); 199 } 200 memcpy(rbuf->data+rbuf->pwrite, buf, todo); 201 /* smp_store_release() for write pointer update, see above */ 202 smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size); 203 204 return len; 205} 206 207ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf, 208 const u8 __user *buf, size_t len) 209{ 210 int status; 211 size_t todo = len; 212 size_t split; 213 214 split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0; 215 216 if (split > 0) { 217 status = copy_from_user(rbuf->data+rbuf->pwrite, buf, split); 218 if (status) 219 return len - todo; 220 buf += split; 221 todo -= split; 222 /* smp_store_release() for write pointer update to ensure that 223 * written data is visible on other cpu cores before the pointer 224 * update, this pairs with smp_load_acquire() in 225 * dvb_ringbuffer_empty() or dvb_ringbuffer_avail() 226 */ 227 smp_store_release(&rbuf->pwrite, 0); 228 } 229 status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo); 230 if (status) 231 return len - todo; 232 /* smp_store_release() for write pointer update, see above */ 233 smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size); 234 235 return len; 236} 237 238ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len) 239{ 240 int status; 241 ssize_t oldpwrite = rbuf->pwrite; 242 243 DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8); 244 DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff); 245 DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY); 246 status = dvb_ringbuffer_write(rbuf, buf, len); 247 248 if (status < 0) rbuf->pwrite = oldpwrite; 249 return status; 250} 251 252ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, 253 int offset, u8 __user *buf, size_t len) 254{ 255 size_t todo; 256 size_t split; 257 size_t pktlen; 258 259 pktlen = rbuf->data[idx] << 8; 260 pktlen |= rbuf->data[(idx + 1) % rbuf->size]; 261 if (offset > pktlen) return -EINVAL; 262 if ((offset + len) > pktlen) len = pktlen - offset; 263 264 idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; 265 todo = len; 266 split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; 267 if (split > 0) { 268 if (copy_to_user(buf, rbuf->data+idx, split)) 269 return -EFAULT; 270 buf += split; 271 todo -= split; 272 idx = 0; 273 } 274 if (copy_to_user(buf, rbuf->data+idx, todo)) 275 return -EFAULT; 276 277 return len; 278} 279 280ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, 281 int offset, u8* buf, size_t len) 282{ 283 size_t todo; 284 size_t split; 285 size_t pktlen; 286 287 pktlen = rbuf->data[idx] << 8; 288 pktlen |= rbuf->data[(idx + 1) % rbuf->size]; 289 if (offset > pktlen) return -EINVAL; 290 if ((offset + len) > pktlen) len = pktlen - offset; 291 292 idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; 293 todo = len; 294 split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; 295 if (split > 0) { 296 memcpy(buf, rbuf->data+idx, split); 297 buf += split; 298 todo -= split; 299 idx = 0; 300 } 301 memcpy(buf, rbuf->data+idx, todo); 302 return len; 303} 304 305void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx) 306{ 307 size_t pktlen; 308 309 rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED; 310 311 // clean up disposed packets 312 while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) { 313 if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) { 314 pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8; 315 pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1); 316 DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE); 317 } else { 318 // first packet is not disposed, so we stop cleaning now 319 break; 320 } 321 } 322} 323 324ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen) 325{ 326 int consumed; 327 int curpktlen; 328 int curpktstatus; 329 330 if (idx == -1) { 331 idx = rbuf->pread; 332 } else { 333 curpktlen = rbuf->data[idx] << 8; 334 curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; 335 idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; 336 } 337 338 consumed = (idx - rbuf->pread) % rbuf->size; 339 340 while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) { 341 342 curpktlen = rbuf->data[idx] << 8; 343 curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; 344 curpktstatus = rbuf->data[(idx + 2) % rbuf->size]; 345 346 if (curpktstatus == PKT_READY) { 347 *pktlen = curpktlen; 348 return idx; 349 } 350 351 consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE; 352 idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; 353 } 354 355 // no packets available 356 return -1; 357} 358 359 360 361EXPORT_SYMBOL(dvb_ringbuffer_init); 362EXPORT_SYMBOL(dvb_ringbuffer_empty); 363EXPORT_SYMBOL(dvb_ringbuffer_free); 364EXPORT_SYMBOL(dvb_ringbuffer_avail); 365EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup); 366EXPORT_SYMBOL(dvb_ringbuffer_read_user); 367EXPORT_SYMBOL(dvb_ringbuffer_read); 368EXPORT_SYMBOL(dvb_ringbuffer_write); 369EXPORT_SYMBOL(dvb_ringbuffer_write_user);