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