Path: blob/master/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
15112 views
/*1*2* dvb_ringbuffer.c: ring buffer implementation for the dvb driver3*4* Copyright (C) 2003 Oliver Endriss5* Copyright (C) 2004 Andrew de Quincey6*7* based on code originally found in av7110.c & dvb_ci.c:8* Copyright (C) 1999-2003 Ralph Metzler9* & Marcus Metzler for convergence integrated media GmbH10*11* This program is free software; you can redistribute it and/or12* modify it under the terms of the GNU Lesser General Public License13* as published by the Free Software Foundation; either version 2.114* of the License, or (at your option) any later version.15*16* This program is distributed in the hope that it will be useful,17* but WITHOUT ANY WARRANTY; without even the implied warranty of18* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the19* GNU Lesser General Public License for more details.20*21* You should have received a copy of the GNU Lesser General Public License22* along with this program; if not, write to the Free Software23* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.24*/25262728#include <linux/errno.h>29#include <linux/kernel.h>30#include <linux/module.h>31#include <linux/sched.h>32#include <linux/string.h>33#include <asm/uaccess.h>3435#include "dvb_ringbuffer.h"3637#define PKT_READY 038#define PKT_DISPOSED 1394041void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)42{43rbuf->pread=rbuf->pwrite=0;44rbuf->data=data;45rbuf->size=len;46rbuf->error=0;4748init_waitqueue_head(&rbuf->queue);4950spin_lock_init(&(rbuf->lock));51}52535455int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)56{57return (rbuf->pread==rbuf->pwrite);58}59606162ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf)63{64ssize_t free;6566free = rbuf->pread - rbuf->pwrite;67if (free <= 0)68free += rbuf->size;69return free-1;70}71727374ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)75{76ssize_t avail;7778avail = rbuf->pwrite - rbuf->pread;79if (avail < 0)80avail += rbuf->size;81return avail;82}83848586void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf)87{88rbuf->pread = rbuf->pwrite;89rbuf->error = 0;90}91EXPORT_SYMBOL(dvb_ringbuffer_flush);9293void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf)94{95rbuf->pread = rbuf->pwrite = 0;96rbuf->error = 0;97}9899void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf)100{101unsigned long flags;102103spin_lock_irqsave(&rbuf->lock, flags);104dvb_ringbuffer_flush(rbuf);105spin_unlock_irqrestore(&rbuf->lock, flags);106107wake_up(&rbuf->queue);108}109110ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len)111{112size_t todo = len;113size_t split;114115split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;116if (split > 0) {117if (copy_to_user(buf, rbuf->data+rbuf->pread, split))118return -EFAULT;119buf += split;120todo -= split;121rbuf->pread = 0;122}123if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))124return -EFAULT;125126rbuf->pread = (rbuf->pread + todo) % rbuf->size;127128return len;129}130131void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len)132{133size_t todo = len;134size_t split;135136split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;137if (split > 0) {138memcpy(buf, rbuf->data+rbuf->pread, split);139buf += split;140todo -= split;141rbuf->pread = 0;142}143memcpy(buf, rbuf->data+rbuf->pread, todo);144145rbuf->pread = (rbuf->pread + todo) % rbuf->size;146}147148149ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len)150{151size_t todo = len;152size_t split;153154split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;155156if (split > 0) {157memcpy(rbuf->data+rbuf->pwrite, buf, split);158buf += split;159todo -= split;160rbuf->pwrite = 0;161}162memcpy(rbuf->data+rbuf->pwrite, buf, todo);163rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;164165return len;166}167168ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len)169{170int status;171ssize_t oldpwrite = rbuf->pwrite;172173DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);174DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);175DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY);176status = dvb_ringbuffer_write(rbuf, buf, len);177178if (status < 0) rbuf->pwrite = oldpwrite;179return status;180}181182ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,183int offset, u8 __user *buf, size_t len)184{185size_t todo;186size_t split;187size_t pktlen;188189pktlen = rbuf->data[idx] << 8;190pktlen |= rbuf->data[(idx + 1) % rbuf->size];191if (offset > pktlen) return -EINVAL;192if ((offset + len) > pktlen) len = pktlen - offset;193194idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;195todo = len;196split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;197if (split > 0) {198if (copy_to_user(buf, rbuf->data+idx, split))199return -EFAULT;200buf += split;201todo -= split;202idx = 0;203}204if (copy_to_user(buf, rbuf->data+idx, todo))205return -EFAULT;206207return len;208}209210ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,211int offset, u8* buf, size_t len)212{213size_t todo;214size_t split;215size_t pktlen;216217pktlen = rbuf->data[idx] << 8;218pktlen |= rbuf->data[(idx + 1) % rbuf->size];219if (offset > pktlen) return -EINVAL;220if ((offset + len) > pktlen) len = pktlen - offset;221222idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;223todo = len;224split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;225if (split > 0) {226memcpy(buf, rbuf->data+idx, split);227buf += split;228todo -= split;229idx = 0;230}231memcpy(buf, rbuf->data+idx, todo);232return len;233}234235void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)236{237size_t pktlen;238239rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED;240241// clean up disposed packets242while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) {243if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) {244pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8;245pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1);246DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE);247} else {248// first packet is not disposed, so we stop cleaning now249break;250}251}252}253254ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)255{256int consumed;257int curpktlen;258int curpktstatus;259260if (idx == -1) {261idx = rbuf->pread;262} else {263curpktlen = rbuf->data[idx] << 8;264curpktlen |= rbuf->data[(idx + 1) % rbuf->size];265idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;266}267268consumed = (idx - rbuf->pread) % rbuf->size;269270while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {271272curpktlen = rbuf->data[idx] << 8;273curpktlen |= rbuf->data[(idx + 1) % rbuf->size];274curpktstatus = rbuf->data[(idx + 2) % rbuf->size];275276if (curpktstatus == PKT_READY) {277*pktlen = curpktlen;278return idx;279}280281consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;282idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;283}284285// no packets available286return -1;287}288289290291EXPORT_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);299300301