linux/drivers/media/dvb-core/dvb_ringbuffer.c
<<
>>
Prefs
   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);
 370