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