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