root/drivers/media/dvb-core/dvb_ringbuffer.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dvb_ringbuffer_init
  2. dvb_ringbuffer_empty
  3. dvb_ringbuffer_free
  4. dvb_ringbuffer_avail
  5. dvb_ringbuffer_flush
  6. dvb_ringbuffer_reset
  7. dvb_ringbuffer_flush_spinlock_wakeup
  8. dvb_ringbuffer_read_user
  9. dvb_ringbuffer_read
  10. dvb_ringbuffer_write
  11. dvb_ringbuffer_write_user
  12. dvb_ringbuffer_pkt_write
  13. dvb_ringbuffer_pkt_read_user
  14. dvb_ringbuffer_pkt_read
  15. dvb_ringbuffer_pkt_dispose
  16. dvb_ringbuffer_pkt_next

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

/* [<][>][^][v][top][bottom][index][help] */