Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  *
0003  * dvb_ringbuffer.c: ring buffer implementation for the dvb driver
0004  *
0005  * Copyright (C) 2003 Oliver Endriss
0006  * Copyright (C) 2004 Andrew de Quincey
0007  *
0008  * based on code originally found in av7110.c & dvb_ci.c:
0009  * Copyright (C) 1999-2003 Ralph  Metzler
0010  *                       & Marcus Metzler for convergence integrated media GmbH
0011  *
0012  * This program is free software; you can redistribute it and/or
0013  * modify it under the terms of the GNU Lesser General Public License
0014  * as published by the Free Software Foundation; either version 2.1
0015  * of the License, or (at your option) any later version.
0016  *
0017  * This program is distributed in the hope that it will be useful,
0018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0020  * GNU Lesser General Public License for more details.
0021  */
0022 
0023 
0024 
0025 #include <linux/errno.h>
0026 #include <linux/kernel.h>
0027 #include <linux/module.h>
0028 #include <linux/sched.h>
0029 #include <linux/string.h>
0030 #include <linux/uaccess.h>
0031 
0032 #include <media/dvb_ringbuffer.h>
0033 
0034 #define PKT_READY 0
0035 #define PKT_DISPOSED 1
0036 
0037 
0038 void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
0039 {
0040     rbuf->pread=rbuf->pwrite=0;
0041     rbuf->data=data;
0042     rbuf->size=len;
0043     rbuf->error=0;
0044 
0045     init_waitqueue_head(&rbuf->queue);
0046 
0047     spin_lock_init(&(rbuf->lock));
0048 }
0049 
0050 
0051 
0052 int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
0053 {
0054     /* smp_load_acquire() to load write pointer on reader side
0055      * this pairs with smp_store_release() in dvb_ringbuffer_write(),
0056      * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset()
0057      *
0058      * for memory barriers also see Documentation/core-api/circular-buffers.rst
0059      */
0060     return (rbuf->pread == smp_load_acquire(&rbuf->pwrite));
0061 }
0062 
0063 
0064 
0065 ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf)
0066 {
0067     ssize_t free;
0068 
0069     /* READ_ONCE() to load read pointer on writer side
0070      * this pairs with smp_store_release() in dvb_ringbuffer_read(),
0071      * dvb_ringbuffer_read_user(), dvb_ringbuffer_flush(),
0072      * or dvb_ringbuffer_reset()
0073      */
0074     free = READ_ONCE(rbuf->pread) - rbuf->pwrite;
0075     if (free <= 0)
0076         free += rbuf->size;
0077     return free-1;
0078 }
0079 
0080 
0081 
0082 ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)
0083 {
0084     ssize_t avail;
0085 
0086     /* smp_load_acquire() to load write pointer on reader side
0087      * this pairs with smp_store_release() in dvb_ringbuffer_write(),
0088      * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset()
0089      */
0090     avail = smp_load_acquire(&rbuf->pwrite) - rbuf->pread;
0091     if (avail < 0)
0092         avail += rbuf->size;
0093     return avail;
0094 }
0095 
0096 
0097 
0098 void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf)
0099 {
0100     /* dvb_ringbuffer_flush() counts as read operation
0101      * smp_load_acquire() to load write pointer
0102      * smp_store_release() to update read pointer, this ensures that the
0103      * correct pointer is visible for subsequent dvb_ringbuffer_free()
0104      * calls on other cpu cores
0105      */
0106     smp_store_release(&rbuf->pread, smp_load_acquire(&rbuf->pwrite));
0107     rbuf->error = 0;
0108 }
0109 EXPORT_SYMBOL(dvb_ringbuffer_flush);
0110 
0111 void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf)
0112 {
0113     /* dvb_ringbuffer_reset() counts as read and write operation
0114      * smp_store_release() to update read pointer
0115      */
0116     smp_store_release(&rbuf->pread, 0);
0117     /* smp_store_release() to update write pointer */
0118     smp_store_release(&rbuf->pwrite, 0);
0119     rbuf->error = 0;
0120 }
0121 
0122 void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf)
0123 {
0124     unsigned long flags;
0125 
0126     spin_lock_irqsave(&rbuf->lock, flags);
0127     dvb_ringbuffer_flush(rbuf);
0128     spin_unlock_irqrestore(&rbuf->lock, flags);
0129 
0130     wake_up(&rbuf->queue);
0131 }
0132 
0133 ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len)
0134 {
0135     size_t todo = len;
0136     size_t split;
0137 
0138     split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
0139     if (split > 0) {
0140         if (copy_to_user(buf, rbuf->data+rbuf->pread, split))
0141             return -EFAULT;
0142         buf += split;
0143         todo -= split;
0144         /* smp_store_release() for read pointer update to ensure
0145          * that buf is not overwritten until read is complete,
0146          * this pairs with READ_ONCE() in dvb_ringbuffer_free()
0147          */
0148         smp_store_release(&rbuf->pread, 0);
0149     }
0150     if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
0151         return -EFAULT;
0152 
0153     /* smp_store_release() to update read pointer, see above */
0154     smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size);
0155 
0156     return len;
0157 }
0158 
0159 void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len)
0160 {
0161     size_t todo = len;
0162     size_t split;
0163 
0164     split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
0165     if (split > 0) {
0166         memcpy(buf, rbuf->data+rbuf->pread, split);
0167         buf += split;
0168         todo -= split;
0169         /* smp_store_release() for read pointer update to ensure
0170          * that buf is not overwritten until read is complete,
0171          * this pairs with READ_ONCE() in dvb_ringbuffer_free()
0172          */
0173         smp_store_release(&rbuf->pread, 0);
0174     }
0175     memcpy(buf, rbuf->data+rbuf->pread, todo);
0176 
0177     /* smp_store_release() to update read pointer, see above */
0178     smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size);
0179 }
0180 
0181 
0182 ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len)
0183 {
0184     size_t todo = len;
0185     size_t split;
0186 
0187     split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;
0188 
0189     if (split > 0) {
0190         memcpy(rbuf->data+rbuf->pwrite, buf, split);
0191         buf += split;
0192         todo -= split;
0193         /* smp_store_release() for write pointer update to ensure that
0194          * written data is visible on other cpu cores before the pointer
0195          * update, this pairs with smp_load_acquire() in
0196          * dvb_ringbuffer_empty() or dvb_ringbuffer_avail()
0197          */
0198         smp_store_release(&rbuf->pwrite, 0);
0199     }
0200     memcpy(rbuf->data+rbuf->pwrite, buf, todo);
0201     /* smp_store_release() for write pointer update, see above */
0202     smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size);
0203 
0204     return len;
0205 }
0206 
0207 ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
0208                   const u8 __user *buf, size_t len)
0209 {
0210     int status;
0211     size_t todo = len;
0212     size_t split;
0213 
0214     split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;
0215 
0216     if (split > 0) {
0217         status = copy_from_user(rbuf->data+rbuf->pwrite, buf, split);
0218         if (status)
0219             return len - todo;
0220         buf += split;
0221         todo -= split;
0222         /* smp_store_release() for write pointer update to ensure that
0223          * written data is visible on other cpu cores before the pointer
0224          * update, this pairs with smp_load_acquire() in
0225          * dvb_ringbuffer_empty() or dvb_ringbuffer_avail()
0226          */
0227         smp_store_release(&rbuf->pwrite, 0);
0228     }
0229     status = copy_from_user(rbuf->data+rbuf->pwrite, buf, todo);
0230     if (status)
0231         return len - todo;
0232     /* smp_store_release() for write pointer update, see above */
0233     smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size);
0234 
0235     return len;
0236 }
0237 
0238 ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len)
0239 {
0240     int status;
0241     ssize_t oldpwrite = rbuf->pwrite;
0242 
0243     DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
0244     DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
0245     DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY);
0246     status = dvb_ringbuffer_write(rbuf, buf, len);
0247 
0248     if (status < 0) rbuf->pwrite = oldpwrite;
0249     return status;
0250 }
0251 
0252 ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
0253                 int offset, u8 __user *buf, size_t len)
0254 {
0255     size_t todo;
0256     size_t split;
0257     size_t pktlen;
0258 
0259     pktlen = rbuf->data[idx] << 8;
0260     pktlen |= rbuf->data[(idx + 1) % rbuf->size];
0261     if (offset > pktlen) return -EINVAL;
0262     if ((offset + len) > pktlen) len = pktlen - offset;
0263 
0264     idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
0265     todo = len;
0266     split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
0267     if (split > 0) {
0268         if (copy_to_user(buf, rbuf->data+idx, split))
0269             return -EFAULT;
0270         buf += split;
0271         todo -= split;
0272         idx = 0;
0273     }
0274     if (copy_to_user(buf, rbuf->data+idx, todo))
0275         return -EFAULT;
0276 
0277     return len;
0278 }
0279 
0280 ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
0281                 int offset, u8* buf, size_t len)
0282 {
0283     size_t todo;
0284     size_t split;
0285     size_t pktlen;
0286 
0287     pktlen = rbuf->data[idx] << 8;
0288     pktlen |= rbuf->data[(idx + 1) % rbuf->size];
0289     if (offset > pktlen) return -EINVAL;
0290     if ((offset + len) > pktlen) len = pktlen - offset;
0291 
0292     idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
0293     todo = len;
0294     split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
0295     if (split > 0) {
0296         memcpy(buf, rbuf->data+idx, split);
0297         buf += split;
0298         todo -= split;
0299         idx = 0;
0300     }
0301     memcpy(buf, rbuf->data+idx, todo);
0302     return len;
0303 }
0304 
0305 void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
0306 {
0307     size_t pktlen;
0308 
0309     rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED;
0310 
0311     // clean up disposed packets
0312     while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) {
0313         if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) {
0314             pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8;
0315             pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1);
0316             DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE);
0317         } else {
0318             // first packet is not disposed, so we stop cleaning now
0319             break;
0320         }
0321     }
0322 }
0323 
0324 ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
0325 {
0326     int consumed;
0327     int curpktlen;
0328     int curpktstatus;
0329 
0330     if (idx == -1) {
0331            idx = rbuf->pread;
0332     } else {
0333         curpktlen = rbuf->data[idx] << 8;
0334         curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
0335         idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
0336     }
0337 
0338     consumed = (idx - rbuf->pread) % rbuf->size;
0339 
0340     while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {
0341 
0342         curpktlen = rbuf->data[idx] << 8;
0343         curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
0344         curpktstatus = rbuf->data[(idx + 2) % rbuf->size];
0345 
0346         if (curpktstatus == PKT_READY) {
0347             *pktlen = curpktlen;
0348             return idx;
0349         }
0350 
0351         consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
0352         idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
0353     }
0354 
0355     // no packets available
0356     return -1;
0357 }
0358 
0359 
0360 
0361 EXPORT_SYMBOL(dvb_ringbuffer_init);
0362 EXPORT_SYMBOL(dvb_ringbuffer_empty);
0363 EXPORT_SYMBOL(dvb_ringbuffer_free);
0364 EXPORT_SYMBOL(dvb_ringbuffer_avail);
0365 EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
0366 EXPORT_SYMBOL(dvb_ringbuffer_read_user);
0367 EXPORT_SYMBOL(dvb_ringbuffer_read);
0368 EXPORT_SYMBOL(dvb_ringbuffer_write);
0369 EXPORT_SYMBOL(dvb_ringbuffer_write_user);