dvb_ringbuffer.c revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2
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#define __KERNEL_SYSCALLS__ 30#include <linux/errno.h> 31#include <linux/kernel.h> 32#include <linux/module.h> 33#include <linux/sched.h> 34#include <linux/string.h> 35#include <asm/uaccess.h> 36 37#include "dvb_ringbuffer.h" 38 39#define PKT_READY 0 40#define PKT_DISPOSED 1 41 42 43void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len) 44{ 45 rbuf->pread=rbuf->pwrite=0; 46 rbuf->data=data; 47 rbuf->size=len; 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} 91 92 93 94void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf) 95{ 96 unsigned long flags; 97 98 spin_lock_irqsave(&rbuf->lock, flags); 99 dvb_ringbuffer_flush(rbuf); 100 spin_unlock_irqrestore(&rbuf->lock, flags); 101 102 wake_up(&rbuf->queue); 103} 104 105 106 107ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len, int usermem) 108{ 109 size_t todo = len; 110 size_t split; 111 112 split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; 113 if (split > 0) { 114 if (!usermem) 115 memcpy(buf, rbuf->data+rbuf->pread, split); 116 else 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 (!usermem) 124 memcpy(buf, rbuf->data+rbuf->pread, todo); 125 else 126 if (copy_to_user(buf, rbuf->data+rbuf->pread, todo)) 127 return -EFAULT; 128 129 rbuf->pread = (rbuf->pread + todo) % rbuf->size; 130 131 return len; 132} 133 134 135 136ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len) 137{ 138 size_t todo = len; 139 size_t split; 140 141 split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0; 142 143 if (split > 0) { 144 memcpy(rbuf->data+rbuf->pwrite, buf, split); 145 buf += split; 146 todo -= split; 147 rbuf->pwrite = 0; 148 } 149 memcpy(rbuf->data+rbuf->pwrite, buf, todo); 150 rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size; 151 152 return len; 153} 154 155ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len) 156{ 157 int status; 158 ssize_t oldpwrite = rbuf->pwrite; 159 160 DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8); 161 DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff); 162 DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY); 163 status = dvb_ringbuffer_write(rbuf, buf, len); 164 165 if (status < 0) rbuf->pwrite = oldpwrite; 166 return status; 167} 168 169ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, 170 int offset, u8* buf, size_t len, int usermem) 171{ 172 size_t todo; 173 size_t split; 174 size_t pktlen; 175 176 pktlen = rbuf->data[idx] << 8; 177 pktlen |= rbuf->data[(idx + 1) % rbuf->size]; 178 if (offset > pktlen) return -EINVAL; 179 if ((offset + len) > pktlen) len = pktlen - offset; 180 181 idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; 182 todo = len; 183 split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; 184 if (split > 0) { 185 if (!usermem) 186 memcpy(buf, rbuf->data+idx, split); 187 else 188 if (copy_to_user(buf, rbuf->data+idx, split)) 189 return -EFAULT; 190 buf += split; 191 todo -= split; 192 idx = 0; 193 } 194 if (!usermem) 195 memcpy(buf, rbuf->data+idx, todo); 196 else 197 if (copy_to_user(buf, rbuf->data+idx, todo)) 198 return -EFAULT; 199 200 return len; 201} 202 203void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx) 204{ 205 size_t pktlen; 206 207 rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED; 208 209 // clean up disposed packets 210 while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) { 211 if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) { 212 pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8; 213 pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1); 214 DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE); 215 } else { 216 // first packet is not disposed, so we stop cleaning now 217 break; 218 } 219 } 220} 221 222ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen) 223{ 224 int consumed; 225 int curpktlen; 226 int curpktstatus; 227 228 if (idx == -1) { 229 idx = rbuf->pread; 230 } else { 231 curpktlen = rbuf->data[idx] << 8; 232 curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; 233 idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; 234 } 235 236 consumed = (idx - rbuf->pread) % rbuf->size; 237 238 while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) { 239 240 curpktlen = rbuf->data[idx] << 8; 241 curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; 242 curpktstatus = rbuf->data[(idx + 2) % rbuf->size]; 243 244 if (curpktstatus == PKT_READY) { 245 *pktlen = curpktlen; 246 return idx; 247 } 248 249 consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE; 250 idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; 251 } 252 253 // no packets available 254 return -1; 255} 256 257 258 259EXPORT_SYMBOL(dvb_ringbuffer_init); 260EXPORT_SYMBOL(dvb_ringbuffer_empty); 261EXPORT_SYMBOL(dvb_ringbuffer_free); 262EXPORT_SYMBOL(dvb_ringbuffer_avail); 263EXPORT_SYMBOL(dvb_ringbuffer_flush); 264EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup); 265EXPORT_SYMBOL(dvb_ringbuffer_read); 266EXPORT_SYMBOL(dvb_ringbuffer_write); 267EXPORT_SYMBOL(dvb_ringbuffer_pkt_write); 268EXPORT_SYMBOL(dvb_ringbuffer_pkt_read); 269EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose); 270EXPORT_SYMBOL(dvb_ringbuffer_pkt_next); 271