dvb_ringbuffer.c revision 48c01a9c2d245b229f8b709040cb1776b00dc683
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 110 111 112ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len, int usermem) 113{ 114 size_t todo = len; 115 size_t split; 116 117 split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; 118 if (split > 0) { 119 if (!usermem) 120 memcpy(buf, rbuf->data+rbuf->pread, split); 121 else 122 if (copy_to_user(buf, rbuf->data+rbuf->pread, split)) 123 return -EFAULT; 124 buf += split; 125 todo -= split; 126 rbuf->pread = 0; 127 } 128 if (!usermem) 129 memcpy(buf, rbuf->data+rbuf->pread, todo); 130 else 131 if (copy_to_user(buf, rbuf->data+rbuf->pread, todo)) 132 return -EFAULT; 133 134 rbuf->pread = (rbuf->pread + todo) % rbuf->size; 135 136 return len; 137} 138 139 140 141ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len) 142{ 143 size_t todo = len; 144 size_t split; 145 146 split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0; 147 148 if (split > 0) { 149 memcpy(rbuf->data+rbuf->pwrite, buf, split); 150 buf += split; 151 todo -= split; 152 rbuf->pwrite = 0; 153 } 154 memcpy(rbuf->data+rbuf->pwrite, buf, todo); 155 rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size; 156 157 return len; 158} 159 160ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len) 161{ 162 int status; 163 ssize_t oldpwrite = rbuf->pwrite; 164 165 DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8); 166 DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff); 167 DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY); 168 status = dvb_ringbuffer_write(rbuf, buf, len); 169 170 if (status < 0) rbuf->pwrite = oldpwrite; 171 return status; 172} 173 174ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, 175 int offset, u8* buf, size_t len, int usermem) 176{ 177 size_t todo; 178 size_t split; 179 size_t pktlen; 180 181 pktlen = rbuf->data[idx] << 8; 182 pktlen |= rbuf->data[(idx + 1) % rbuf->size]; 183 if (offset > pktlen) return -EINVAL; 184 if ((offset + len) > pktlen) len = pktlen - offset; 185 186 idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; 187 todo = len; 188 split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; 189 if (split > 0) { 190 if (!usermem) 191 memcpy(buf, rbuf->data+idx, split); 192 else 193 if (copy_to_user(buf, rbuf->data+idx, split)) 194 return -EFAULT; 195 buf += split; 196 todo -= split; 197 idx = 0; 198 } 199 if (!usermem) 200 memcpy(buf, rbuf->data+idx, todo); 201 else 202 if (copy_to_user(buf, rbuf->data+idx, todo)) 203 return -EFAULT; 204 205 return len; 206} 207 208void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx) 209{ 210 size_t pktlen; 211 212 rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED; 213 214 // clean up disposed packets 215 while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) { 216 if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) { 217 pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8; 218 pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1); 219 DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE); 220 } else { 221 // first packet is not disposed, so we stop cleaning now 222 break; 223 } 224 } 225} 226 227ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen) 228{ 229 int consumed; 230 int curpktlen; 231 int curpktstatus; 232 233 if (idx == -1) { 234 idx = rbuf->pread; 235 } else { 236 curpktlen = rbuf->data[idx] << 8; 237 curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; 238 idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; 239 } 240 241 consumed = (idx - rbuf->pread) % rbuf->size; 242 243 while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) { 244 245 curpktlen = rbuf->data[idx] << 8; 246 curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; 247 curpktstatus = rbuf->data[(idx + 2) % rbuf->size]; 248 249 if (curpktstatus == PKT_READY) { 250 *pktlen = curpktlen; 251 return idx; 252 } 253 254 consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE; 255 idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; 256 } 257 258 // no packets available 259 return -1; 260} 261 262 263 264EXPORT_SYMBOL(dvb_ringbuffer_init); 265EXPORT_SYMBOL(dvb_ringbuffer_empty); 266EXPORT_SYMBOL(dvb_ringbuffer_free); 267EXPORT_SYMBOL(dvb_ringbuffer_avail); 268EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup); 269EXPORT_SYMBOL(dvb_ringbuffer_read); 270EXPORT_SYMBOL(dvb_ringbuffer_write); 271