at v2.6.33 298 lines 7.2 kB view raw
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);