linux/drivers/media/dvb/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 * You should have received a copy of the GNU Lesser General Public License
  23 * along with this program; if not, write to the Free Software
  24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  25 */
  26
  27
  28
  29#include <linux/errno.h>
  30#include <linux/kernel.h>
  31#include <linux/module.h>
  32#include <linux/sched.h>
  33#include <linux/string.h>
  34#include <asm/uaccess.h>
  35
  36#include "dvb_ringbuffer.h"
  37
  38#define PKT_READY 0
  39#define PKT_DISPOSED 1
  40
  41
  42void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
  43{
  44        rbuf->pread=rbuf->pwrite=0;
  45        rbuf->data=data;
  46        rbuf->size=len;
  47        rbuf->error=0;
  48
  49        init_waitqueue_head(&rbuf->queue);
  50
  51        spin_lock_init(&(rbuf->lock));
  52}
  53
  54
  55
  56int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
  57{
  58        return (rbuf->pread==rbuf->pwrite);
  59}
  60
  61
  62
  63ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf)
  64{
  65        ssize_t free;
  66
  67        free = rbuf->pread - rbuf->pwrite;
  68        if (free <= 0)
  69                free += rbuf->size;
  70        return free-1;
  71}
  72
  73
  74
  75ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)
  76{
  77        ssize_t avail;
  78
  79        avail = rbuf->pwrite - rbuf->pread;
  80        if (avail < 0)
  81                avail += rbuf->size;
  82        return avail;
  83}
  84
  85
  86
  87void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf)
  88{
  89        rbuf->pread = rbuf->pwrite;
  90        rbuf->error = 0;
  91}
  92
  93void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf)
  94{
  95        rbuf->pread = rbuf->pwrite = 0;
  96        rbuf->error = 0;
  97}
  98
  99void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf)
 100{
 101        unsigned long flags;
 102
 103        spin_lock_irqsave(&rbuf->lock, flags);
 104        dvb_ringbuffer_flush(rbuf);
 105        spin_unlock_irqrestore(&rbuf->lock, flags);
 106
 107        wake_up(&rbuf->queue);
 108}
 109
 110ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len)
 111{
 112        size_t todo = len;
 113        size_t split;
 114
 115        split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
 116        if (split > 0) {
 117                if (copy_to_user(buf, rbuf->data+rbuf->pread, split))
 118                        return -EFAULT;
 119                buf += split;
 120                todo -= split;
 121                rbuf->pread = 0;
 122        }
 123        if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
 124                return -EFAULT;
 125
 126        rbuf->pread = (rbuf->pread + todo) % rbuf->size;
 127
 128        return len;
 129}
 130
 131void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len)
 132{
 133        size_t todo = len;
 134        size_t split;
 135
 136        split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
 137        if (split > 0) {
 138                memcpy(buf, rbuf->data+rbuf->pread, split);
 139                buf += split;
 140                todo -= split;
 141                rbuf->pread = 0;
 142        }
 143        memcpy(buf, rbuf->data+rbuf->pread, todo);
 144
 145        rbuf->pread = (rbuf->pread + todo) % rbuf->size;
 146}
 147
 148
 149ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len)
 150{
 151        size_t todo = len;
 152        size_t split;
 153
 154        split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;
 155
 156        if (split > 0) {
 157                memcpy(rbuf->data+rbuf->pwrite, buf, split);
 158                buf += split;
 159                todo -= split;
 160                rbuf->pwrite = 0;
 161        }
 162        memcpy(rbuf->data+rbuf->pwrite, buf, todo);
 163        rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
 164
 165        return len;
 166}
 167
 168ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len)
 169{
 170        int status;
 171        ssize_t oldpwrite = rbuf->pwrite;
 172
 173        DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
 174        DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
 175        DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY);
 176        status = dvb_ringbuffer_write(rbuf, buf, len);
 177
 178        if (status < 0) rbuf->pwrite = oldpwrite;
 179        return status;
 180}
 181
 182ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
 183                                int offset, u8 __user *buf, size_t len)
 184{
 185        size_t todo;
 186        size_t split;
 187        size_t pktlen;
 188
 189        pktlen = rbuf->data[idx] << 8;
 190        pktlen |= rbuf->data[(idx + 1) % rbuf->size];
 191        if (offset > pktlen) return -EINVAL;
 192        if ((offset + len) > pktlen) len = pktlen - offset;
 193
 194        idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
 195        todo = len;
 196        split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
 197        if (split > 0) {
 198                if (copy_to_user(buf, rbuf->data+idx, split))
 199                        return -EFAULT;
 200                buf += split;
 201                todo -= split;
 202                idx = 0;
 203        }
 204        if (copy_to_user(buf, rbuf->data+idx, todo))
 205                return -EFAULT;
 206
 207        return len;
 208}
 209
 210ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
 211                                int offset, u8* buf, size_t len)
 212{
 213        size_t todo;
 214        size_t split;
 215        size_t pktlen;
 216
 217        pktlen = rbuf->data[idx] << 8;
 218        pktlen |= rbuf->data[(idx + 1) % rbuf->size];
 219        if (offset > pktlen) return -EINVAL;
 220        if ((offset + len) > pktlen) len = pktlen - offset;
 221
 222        idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
 223        todo = len;
 224        split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
 225        if (split > 0) {
 226                memcpy(buf, rbuf->data+idx, split);
 227                buf += split;
 228                todo -= split;
 229                idx = 0;
 230        }
 231        memcpy(buf, rbuf->data+idx, todo);
 232        return len;
 233}
 234
 235void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
 236{
 237        size_t pktlen;
 238
 239        rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED;
 240
 241        // clean up disposed packets
 242        while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) {
 243                if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) {
 244                        pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8;
 245                        pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1);
 246                        DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE);
 247                } else {
 248                        // first packet is not disposed, so we stop cleaning now
 249                        break;
 250                }
 251        }
 252}
 253
 254ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
 255{
 256        int consumed;
 257        int curpktlen;
 258        int curpktstatus;
 259
 260        if (idx == -1) {
 261               idx = rbuf->pread;
 262        } else {
 263                curpktlen = rbuf->data[idx] << 8;
 264                curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
 265                idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
 266        }
 267
 268        consumed = (idx - rbuf->pread) % rbuf->size;
 269
 270        while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {
 271
 272                curpktlen = rbuf->data[idx] << 8;
 273                curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
 274                curpktstatus = rbuf->data[(idx + 2) % rbuf->size];
 275
 276                if (curpktstatus == PKT_READY) {
 277                        *pktlen = curpktlen;
 278                        return idx;
 279                }
 280
 281                consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
 282                idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
 283        }
 284
 285        // no packets available
 286        return -1;
 287}
 288
 289
 290
 291EXPORT_SYMBOL(dvb_ringbuffer_init);
 292EXPORT_SYMBOL(dvb_ringbuffer_empty);
 293EXPORT_SYMBOL(dvb_ringbuffer_free);
 294EXPORT_SYMBOL(dvb_ringbuffer_avail);
 295EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
 296EXPORT_SYMBOL(dvb_ringbuffer_read_user);
 297EXPORT_SYMBOL(dvb_ringbuffer_read);
 298EXPORT_SYMBOL(dvb_ringbuffer_write);
 299