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 /* smp_load_acquire() to load write pointer on reader side 59 * this pairs with smp_store_release() in dvb_ringbuffer_write(), 60 * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset() 61 * 62 * for memory barriers also see Documentation/circular-buffers.txt 63 */ 64 return (rbuf->pread == smp_load_acquire(&rbuf->pwrite)); 65 } 66 67 68 69 ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf) 70 { 71 ssize_t free; 72 73 /* ACCESS_ONCE() to load read pointer on writer side 74 * this pairs with smp_store_release() in dvb_ringbuffer_read(), 75 * dvb_ringbuffer_read_user(), dvb_ringbuffer_flush(), 76 * or dvb_ringbuffer_reset() 77 */ 78 free = ACCESS_ONCE(rbuf->pread) - rbuf->pwrite; 79 if (free <= 0) 80 free += rbuf->size; 81 return free-1; 82 } 83 84 85 86 ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf) 87 { 88 ssize_t avail; 89 90 /* smp_load_acquire() to load write pointer on reader side 91 * this pairs with smp_store_release() in dvb_ringbuffer_write(), 92 * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset() 93 */ 94 avail = smp_load_acquire(&rbuf->pwrite) - rbuf->pread; 95 if (avail < 0) 96 avail += rbuf->size; 97 return avail; 98 } 99 100 101 102 void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf) 103 { 104 /* dvb_ringbuffer_flush() counts as read operation 105 * smp_load_acquire() to load write pointer 106 * smp_store_release() to update read pointer, this ensures that the 107 * correct pointer is visible for subsequent dvb_ringbuffer_free() 108 * calls on other cpu cores 109 */ 110 smp_store_release(&rbuf->pread, smp_load_acquire(&rbuf->pwrite)); 111 rbuf->error = 0; 112 } 113 EXPORT_SYMBOL(dvb_ringbuffer_flush); 114 115 void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf) 116 { 117 /* dvb_ringbuffer_reset() counts as read and write operation 118 * smp_store_release() to update read pointer 119 */ 120 smp_store_release(&rbuf->pread, 0); 121 /* smp_store_release() to update write pointer */ 122 smp_store_release(&rbuf->pwrite, 0); 123 rbuf->error = 0; 124 } 125 126 void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf) 127 { 128 unsigned long flags; 129 130 spin_lock_irqsave(&rbuf->lock, flags); 131 dvb_ringbuffer_flush(rbuf); 132 spin_unlock_irqrestore(&rbuf->lock, flags); 133 134 wake_up(&rbuf->queue); 135 } 136 137 ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len) 138 { 139 size_t todo = len; 140 size_t split; 141 142 split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; 143 if (split > 0) { 144 if (copy_to_user(buf, rbuf->data+rbuf->pread, split)) 145 return -EFAULT; 146 buf += split; 147 todo -= split; 148 /* smp_store_release() for read pointer update to ensure 149 * that buf is not overwritten until read is complete, 150 * this pairs with ACCESS_ONCE() in dvb_ringbuffer_free() 151 */ 152 smp_store_release(&rbuf->pread, 0); 153 } 154 if (copy_to_user(buf, rbuf->data+rbuf->pread, todo)) 155 return -EFAULT; 156 157 /* smp_store_release() to update read pointer, see above */ 158 smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size); 159 160 return len; 161 } 162 163 void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len) 164 { 165 size_t todo = len; 166 size_t split; 167 168 split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; 169 if (split > 0) { 170 memcpy(buf, rbuf->data+rbuf->pread, split); 171 buf += split; 172 todo -= split; 173 /* smp_store_release() for read pointer update to ensure 174 * that buf is not overwritten until read is complete, 175 * this pairs with ACCESS_ONCE() in dvb_ringbuffer_free() 176 */ 177 smp_store_release(&rbuf->pread, 0); 178 } 179 memcpy(buf, rbuf->data+rbuf->pread, todo); 180 181 /* smp_store_release() to update read pointer, see above */ 182 smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size); 183 } 184 185 186 ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len) 187 { 188 size_t todo = len; 189 size_t split; 190 191 split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0; 192 193 if (split > 0) { 194 memcpy(rbuf->data+rbuf->pwrite, buf, split); 195 buf += split; 196 todo -= split; 197 /* smp_store_release() for write pointer update to ensure that 198 * written data is visible on other cpu cores before the pointer 199 * update, this pairs with smp_load_acquire() in 200 * dvb_ringbuffer_empty() or dvb_ringbuffer_avail() 201 */ 202 smp_store_release(&rbuf->pwrite, 0); 203 } 204 memcpy(rbuf->data+rbuf->pwrite, buf, todo); 205 /* smp_store_release() for write pointer update, see above */ 206 smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size); 207 208 return len; 209 } 210 211 ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf, 212 const u8 __user *buf, size_t len) 213 { 214 int status; 215 size_t todo = len; 216 size_t split; 217 218 split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0; 219 220 if (split > 0) { 221 status = copy_from_user(rbuf->data+rbuf->pwrite, buf, split); 222 if (status) 223 return len - todo; 224 buf += split; 225 todo -= split; 226 /* smp_store_release() for write pointer update to ensure that 227 * written data is visible on other cpu cores before the pointer 228 * update, this pairs with smp_load_acquire() in 229 * dvb_ringbuffer_empty() or dvb_ringbuffer_avail() 230 */ 231 smp_store_release(&rbuf->pwrite, 0); 232 } 233 status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo); 234 if (status) 235 return len - todo; 236 /* smp_store_release() for write pointer update, see above */ 237 smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size); 238 239 return len; 240 } 241 242 ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len) 243 { 244 int status; 245 ssize_t oldpwrite = rbuf->pwrite; 246 247 DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8); 248 DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff); 249 DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY); 250 status = dvb_ringbuffer_write(rbuf, buf, len); 251 252 if (status < 0) rbuf->pwrite = oldpwrite; 253 return status; 254 } 255 256 ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, 257 int offset, u8 __user *buf, size_t len) 258 { 259 size_t todo; 260 size_t split; 261 size_t pktlen; 262 263 pktlen = rbuf->data[idx] << 8; 264 pktlen |= rbuf->data[(idx + 1) % rbuf->size]; 265 if (offset > pktlen) return -EINVAL; 266 if ((offset + len) > pktlen) len = pktlen - offset; 267 268 idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; 269 todo = len; 270 split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; 271 if (split > 0) { 272 if (copy_to_user(buf, rbuf->data+idx, split)) 273 return -EFAULT; 274 buf += split; 275 todo -= split; 276 idx = 0; 277 } 278 if (copy_to_user(buf, rbuf->data+idx, todo)) 279 return -EFAULT; 280 281 return len; 282 } 283 284 ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, 285 int offset, u8* buf, size_t len) 286 { 287 size_t todo; 288 size_t split; 289 size_t pktlen; 290 291 pktlen = rbuf->data[idx] << 8; 292 pktlen |= rbuf->data[(idx + 1) % rbuf->size]; 293 if (offset > pktlen) return -EINVAL; 294 if ((offset + len) > pktlen) len = pktlen - offset; 295 296 idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; 297 todo = len; 298 split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; 299 if (split > 0) { 300 memcpy(buf, rbuf->data+idx, split); 301 buf += split; 302 todo -= split; 303 idx = 0; 304 } 305 memcpy(buf, rbuf->data+idx, todo); 306 return len; 307 } 308 309 void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx) 310 { 311 size_t pktlen; 312 313 rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED; 314 315 // clean up disposed packets 316 while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) { 317 if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) { 318 pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8; 319 pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1); 320 DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE); 321 } else { 322 // first packet is not disposed, so we stop cleaning now 323 break; 324 } 325 } 326 } 327 328 ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen) 329 { 330 int consumed; 331 int curpktlen; 332 int curpktstatus; 333 334 if (idx == -1) { 335 idx = rbuf->pread; 336 } else { 337 curpktlen = rbuf->data[idx] << 8; 338 curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; 339 idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; 340 } 341 342 consumed = (idx - rbuf->pread) % rbuf->size; 343 344 while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) { 345 346 curpktlen = rbuf->data[idx] << 8; 347 curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; 348 curpktstatus = rbuf->data[(idx + 2) % rbuf->size]; 349 350 if (curpktstatus == PKT_READY) { 351 *pktlen = curpktlen; 352 return idx; 353 } 354 355 consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE; 356 idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; 357 } 358 359 // no packets available 360 return -1; 361 } 362 363 364 365 EXPORT_SYMBOL(dvb_ringbuffer_init); 366 EXPORT_SYMBOL(dvb_ringbuffer_empty); 367 EXPORT_SYMBOL(dvb_ringbuffer_free); 368 EXPORT_SYMBOL(dvb_ringbuffer_avail); 369 EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup); 370 EXPORT_SYMBOL(dvb_ringbuffer_read_user); 371 EXPORT_SYMBOL(dvb_ringbuffer_read); 372 EXPORT_SYMBOL(dvb_ringbuffer_write); 373 EXPORT_SYMBOL(dvb_ringbuffer_write_user); 374