2005-04-17 02:20:36 +04:00
/*
*
* dvb_ringbuffer . c : ring buffer implementation for the dvb driver
*
* Copyright ( C ) 2003 Oliver Endriss
* Copyright ( C ) 2004 Andrew de Quincey
*
* based on code originally found in av7110 . c & dvb_ci . c :
* Copyright ( C ) 1999 - 2003 Ralph Metzler
* & Marcus Metzler for convergence integrated media GmbH
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation ; either version 2.1
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Lesser General Public License for more details .
*/
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/string.h>
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
# include "dvb_ringbuffer.h"
# define PKT_READY 0
# define PKT_DISPOSED 1
void dvb_ringbuffer_init ( struct dvb_ringbuffer * rbuf , void * data , size_t len )
{
2005-12-12 11:37:27 +03:00
rbuf - > pread = rbuf - > pwrite = 0 ;
rbuf - > data = data ;
rbuf - > size = len ;
2006-03-13 19:14:34 +03:00
rbuf - > error = 0 ;
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
init_waitqueue_head ( & rbuf - > queue ) ;
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
spin_lock_init ( & ( rbuf - > lock ) ) ;
2005-04-17 02:20:36 +04:00
}
int dvb_ringbuffer_empty ( struct dvb_ringbuffer * rbuf )
{
2016-05-11 19:49:11 +03:00
/* smp_load_acquire() to load write pointer on reader side
* this pairs with smp_store_release ( ) in dvb_ringbuffer_write ( ) ,
* dvb_ringbuffer_write_user ( ) , or dvb_ringbuffer_reset ( )
*
* for memory barriers also see Documentation / circular - buffers . txt
*/
return ( rbuf - > pread = = smp_load_acquire ( & rbuf - > pwrite ) ) ;
2005-04-17 02:20:36 +04:00
}
ssize_t dvb_ringbuffer_free ( struct dvb_ringbuffer * rbuf )
{
2005-12-12 11:37:27 +03:00
ssize_t free ;
2005-04-17 02:20:36 +04:00
2016-05-11 19:49:11 +03:00
/* ACCESS_ONCE() to load read pointer on writer side
* this pairs with smp_store_release ( ) in dvb_ringbuffer_read ( ) ,
* dvb_ringbuffer_read_user ( ) , dvb_ringbuffer_flush ( ) ,
* or dvb_ringbuffer_reset ( )
*/
free = ACCESS_ONCE ( rbuf - > pread ) - rbuf - > pwrite ;
2005-12-12 11:37:27 +03:00
if ( free < = 0 )
free + = rbuf - > size ;
return free - 1 ;
2005-04-17 02:20:36 +04:00
}
ssize_t dvb_ringbuffer_avail ( struct dvb_ringbuffer * rbuf )
{
2005-12-12 11:37:27 +03:00
ssize_t avail ;
2005-04-17 02:20:36 +04:00
2016-05-11 19:49:11 +03:00
/* smp_load_acquire() to load write pointer on reader side
* this pairs with smp_store_release ( ) in dvb_ringbuffer_write ( ) ,
* dvb_ringbuffer_write_user ( ) , or dvb_ringbuffer_reset ( )
*/
avail = smp_load_acquire ( & rbuf - > pwrite ) - rbuf - > pread ;
2005-12-12 11:37:27 +03:00
if ( avail < 0 )
avail + = rbuf - > size ;
return avail ;
2005-04-17 02:20:36 +04:00
}
void dvb_ringbuffer_flush ( struct dvb_ringbuffer * rbuf )
{
2016-05-11 19:49:11 +03:00
/* dvb_ringbuffer_flush() counts as read operation
* smp_load_acquire ( ) to load write pointer
* smp_store_release ( ) to update read pointer , this ensures that the
* correct pointer is visible for subsequent dvb_ringbuffer_free ( )
* calls on other cpu cores
*/
smp_store_release ( & rbuf - > pread , smp_load_acquire ( & rbuf - > pwrite ) ) ;
2006-03-13 19:14:34 +03:00
rbuf - > error = 0 ;
2005-04-17 02:20:36 +04:00
}
2009-12-22 10:37:53 +03:00
EXPORT_SYMBOL ( dvb_ringbuffer_flush ) ;
2005-04-17 02:20:36 +04:00
2008-04-21 01:37:45 +04:00
void dvb_ringbuffer_reset ( struct dvb_ringbuffer * rbuf )
{
2016-05-11 19:49:11 +03:00
/* dvb_ringbuffer_reset() counts as read and write operation
* smp_store_release ( ) to update read pointer
*/
smp_store_release ( & rbuf - > pread , 0 ) ;
/* smp_store_release() to update write pointer */
smp_store_release ( & rbuf - > pwrite , 0 ) ;
2008-04-21 01:37:45 +04:00
rbuf - > error = 0 ;
}
2005-04-17 02:20:36 +04:00
void dvb_ringbuffer_flush_spinlock_wakeup ( struct dvb_ringbuffer * rbuf )
{
2005-12-12 11:37:27 +03:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
spin_lock_irqsave ( & rbuf - > lock , flags ) ;
dvb_ringbuffer_flush ( rbuf ) ;
spin_unlock_irqrestore ( & rbuf - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
wake_up ( & rbuf - > queue ) ;
2005-04-17 02:20:36 +04:00
}
2008-06-22 21:20:29 +04:00
ssize_t dvb_ringbuffer_read_user ( struct dvb_ringbuffer * rbuf , u8 __user * buf , size_t len )
2005-04-17 02:20:36 +04:00
{
2005-12-12 11:37:27 +03:00
size_t todo = len ;
size_t split ;
split = ( rbuf - > pread + len > rbuf - > size ) ? rbuf - > size - rbuf - > pread : 0 ;
if ( split > 0 ) {
2008-06-22 21:20:29 +04:00
if ( copy_to_user ( buf , rbuf - > data + rbuf - > pread , split ) )
return - EFAULT ;
2005-12-12 11:37:27 +03:00
buf + = split ;
todo - = split ;
2016-05-11 19:49:11 +03:00
/* smp_store_release() for read pointer update to ensure
* that buf is not overwritten until read is complete ,
* this pairs with ACCESS_ONCE ( ) in dvb_ringbuffer_free ( )
*/
smp_store_release ( & rbuf - > pread , 0 ) ;
2005-12-12 11:37:27 +03:00
}
2008-06-22 21:20:29 +04:00
if ( copy_to_user ( buf , rbuf - > data + rbuf - > pread , todo ) )
return - EFAULT ;
2005-12-12 11:37:27 +03:00
2016-05-11 19:49:11 +03:00
/* smp_store_release() to update read pointer, see above */
smp_store_release ( & rbuf - > pread , ( rbuf - > pread + todo ) % rbuf - > size ) ;
2005-12-12 11:37:27 +03:00
return len ;
2005-04-17 02:20:36 +04:00
}
2008-06-22 21:20:29 +04:00
void dvb_ringbuffer_read ( struct dvb_ringbuffer * rbuf , u8 * buf , size_t len )
{
size_t todo = len ;
size_t split ;
split = ( rbuf - > pread + len > rbuf - > size ) ? rbuf - > size - rbuf - > pread : 0 ;
if ( split > 0 ) {
memcpy ( buf , rbuf - > data + rbuf - > pread , split ) ;
buf + = split ;
todo - = split ;
2016-05-11 19:49:11 +03:00
/* smp_store_release() for read pointer update to ensure
* that buf is not overwritten until read is complete ,
* this pairs with ACCESS_ONCE ( ) in dvb_ringbuffer_free ( )
*/
smp_store_release ( & rbuf - > pread , 0 ) ;
2008-06-22 21:20:29 +04:00
}
memcpy ( buf , rbuf - > data + rbuf - > pread , todo ) ;
2016-05-11 19:49:11 +03:00
/* smp_store_release() to update read pointer, see above */
smp_store_release ( & rbuf - > pread , ( rbuf - > pread + todo ) % rbuf - > size ) ;
2008-06-22 21:20:29 +04:00
}
2005-04-17 02:20:36 +04:00
ssize_t dvb_ringbuffer_write ( struct dvb_ringbuffer * rbuf , const u8 * buf , size_t len )
{
2005-12-12 11:37:27 +03:00
size_t todo = len ;
size_t split ;
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
split = ( rbuf - > pwrite + len > rbuf - > size ) ? rbuf - > size - rbuf - > pwrite : 0 ;
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
if ( split > 0 ) {
memcpy ( rbuf - > data + rbuf - > pwrite , buf , split ) ;
buf + = split ;
todo - = split ;
2016-05-11 19:49:11 +03:00
/* smp_store_release() for write pointer update to ensure that
* written data is visible on other cpu cores before the pointer
* update , this pairs with smp_load_acquire ( ) in
* dvb_ringbuffer_empty ( ) or dvb_ringbuffer_avail ( )
*/
smp_store_release ( & rbuf - > pwrite , 0 ) ;
2005-12-12 11:37:27 +03:00
}
memcpy ( rbuf - > data + rbuf - > pwrite , buf , todo ) ;
2016-05-11 19:49:11 +03:00
/* smp_store_release() for write pointer update, see above */
smp_store_release ( & rbuf - > pwrite , ( rbuf - > pwrite + todo ) % rbuf - > size ) ;
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
return len ;
2005-04-17 02:20:36 +04:00
}
2014-09-04 03:44:04 +04:00
ssize_t dvb_ringbuffer_write_user ( struct dvb_ringbuffer * rbuf ,
const u8 __user * buf , size_t len )
{
int status ;
size_t todo = len ;
size_t split ;
split = ( rbuf - > pwrite + len > rbuf - > size ) ? rbuf - > size - rbuf - > pwrite : 0 ;
if ( split > 0 ) {
status = copy_from_user ( rbuf - > data + rbuf - > pwrite , buf , split ) ;
if ( status )
return len - todo ;
buf + = split ;
todo - = split ;
2016-05-11 19:49:11 +03:00
/* smp_store_release() for write pointer update to ensure that
* written data is visible on other cpu cores before the pointer
* update , this pairs with smp_load_acquire ( ) in
* dvb_ringbuffer_empty ( ) or dvb_ringbuffer_avail ( )
*/
smp_store_release ( & rbuf - > pwrite , 0 ) ;
2014-09-04 03:44:04 +04:00
}
status = copy_from_user ( rbuf - > data + rbuf - > pwrite , buf , todo ) ;
if ( status )
return len - todo ;
2016-05-11 19:49:11 +03:00
/* smp_store_release() for write pointer update, see above */
smp_store_release ( & rbuf - > pwrite , ( rbuf - > pwrite + todo ) % rbuf - > size ) ;
2014-09-04 03:44:04 +04:00
return len ;
}
2005-04-17 02:20:36 +04:00
ssize_t dvb_ringbuffer_pkt_write ( struct dvb_ringbuffer * rbuf , u8 * buf , size_t len )
{
2005-12-12 11:37:27 +03:00
int status ;
ssize_t oldpwrite = rbuf - > pwrite ;
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
DVB_RINGBUFFER_WRITE_BYTE ( rbuf , len > > 8 ) ;
DVB_RINGBUFFER_WRITE_BYTE ( rbuf , len & 0xff ) ;
DVB_RINGBUFFER_WRITE_BYTE ( rbuf , PKT_READY ) ;
status = dvb_ringbuffer_write ( rbuf , buf , len ) ;
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
if ( status < 0 ) rbuf - > pwrite = oldpwrite ;
return status ;
2005-04-17 02:20:36 +04:00
}
2008-06-22 21:20:29 +04:00
ssize_t dvb_ringbuffer_pkt_read_user ( struct dvb_ringbuffer * rbuf , size_t idx ,
int offset , u8 __user * buf , size_t len )
2005-04-17 02:20:36 +04:00
{
2005-12-12 11:37:27 +03:00
size_t todo ;
size_t split ;
size_t pktlen ;
pktlen = rbuf - > data [ idx ] < < 8 ;
pktlen | = rbuf - > data [ ( idx + 1 ) % rbuf - > size ] ;
if ( offset > pktlen ) return - EINVAL ;
if ( ( offset + len ) > pktlen ) len = pktlen - offset ;
idx = ( idx + DVB_RINGBUFFER_PKTHDRSIZE + offset ) % rbuf - > size ;
todo = len ;
split = ( ( idx + len ) > rbuf - > size ) ? rbuf - > size - idx : 0 ;
if ( split > 0 ) {
2008-06-22 21:20:29 +04:00
if ( copy_to_user ( buf , rbuf - > data + idx , split ) )
return - EFAULT ;
2005-12-12 11:37:27 +03:00
buf + = split ;
todo - = split ;
idx = 0 ;
}
2008-06-22 21:20:29 +04:00
if ( copy_to_user ( buf , rbuf - > data + idx , todo ) )
return - EFAULT ;
return len ;
}
2005-12-12 11:37:27 +03:00
2008-06-22 21:20:29 +04:00
ssize_t dvb_ringbuffer_pkt_read ( struct dvb_ringbuffer * rbuf , size_t idx ,
int offset , u8 * buf , size_t len )
{
size_t todo ;
size_t split ;
size_t pktlen ;
pktlen = rbuf - > data [ idx ] < < 8 ;
pktlen | = rbuf - > data [ ( idx + 1 ) % rbuf - > size ] ;
if ( offset > pktlen ) return - EINVAL ;
if ( ( offset + len ) > pktlen ) len = pktlen - offset ;
idx = ( idx + DVB_RINGBUFFER_PKTHDRSIZE + offset ) % rbuf - > size ;
todo = len ;
split = ( ( idx + len ) > rbuf - > size ) ? rbuf - > size - idx : 0 ;
if ( split > 0 ) {
memcpy ( buf , rbuf - > data + idx , split ) ;
buf + = split ;
todo - = split ;
idx = 0 ;
}
memcpy ( buf , rbuf - > data + idx , todo ) ;
2005-12-12 11:37:27 +03:00
return len ;
2005-04-17 02:20:36 +04:00
}
void dvb_ringbuffer_pkt_dispose ( struct dvb_ringbuffer * rbuf , size_t idx )
{
2005-12-12 11:37:27 +03:00
size_t pktlen ;
rbuf - > data [ ( idx + 2 ) % rbuf - > size ] = PKT_DISPOSED ;
// clean up disposed packets
while ( dvb_ringbuffer_avail ( rbuf ) > DVB_RINGBUFFER_PKTHDRSIZE ) {
if ( DVB_RINGBUFFER_PEEK ( rbuf , 2 ) = = PKT_DISPOSED ) {
2006-01-09 20:25:34 +03:00
pktlen = DVB_RINGBUFFER_PEEK ( rbuf , 0 ) < < 8 ;
pktlen | = DVB_RINGBUFFER_PEEK ( rbuf , 1 ) ;
DVB_RINGBUFFER_SKIP ( rbuf , pktlen + DVB_RINGBUFFER_PKTHDRSIZE ) ;
2005-12-12 11:37:27 +03:00
} else {
2006-01-09 20:25:34 +03:00
// first packet is not disposed, so we stop cleaning now
break ;
2005-12-12 11:37:27 +03:00
}
}
2005-04-17 02:20:36 +04:00
}
ssize_t dvb_ringbuffer_pkt_next ( struct dvb_ringbuffer * rbuf , size_t idx , size_t * pktlen )
{
2005-12-12 11:37:27 +03:00
int consumed ;
int curpktlen ;
int curpktstatus ;
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
if ( idx = = - 1 ) {
2005-04-17 02:20:36 +04:00
idx = rbuf - > pread ;
} else {
2005-12-12 11:37:27 +03:00
curpktlen = rbuf - > data [ idx ] < < 8 ;
curpktlen | = rbuf - > data [ ( idx + 1 ) % rbuf - > size ] ;
idx = ( idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE ) % rbuf - > size ;
2005-04-17 02:20:36 +04:00
}
2005-12-12 11:37:27 +03:00
consumed = ( idx - rbuf - > pread ) % rbuf - > size ;
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
while ( ( dvb_ringbuffer_avail ( rbuf ) - consumed ) > DVB_RINGBUFFER_PKTHDRSIZE ) {
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
curpktlen = rbuf - > data [ idx ] < < 8 ;
curpktlen | = rbuf - > data [ ( idx + 1 ) % rbuf - > size ] ;
curpktstatus = rbuf - > data [ ( idx + 2 ) % rbuf - > size ] ;
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
if ( curpktstatus = = PKT_READY ) {
2006-01-09 20:25:34 +03:00
* pktlen = curpktlen ;
return idx ;
2005-12-12 11:37:27 +03:00
}
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
consumed + = curpktlen + DVB_RINGBUFFER_PKTHDRSIZE ;
idx = ( idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE ) % rbuf - > size ;
}
2005-04-17 02:20:36 +04:00
2005-12-12 11:37:27 +03:00
// no packets available
return - 1 ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( dvb_ringbuffer_init ) ;
EXPORT_SYMBOL ( dvb_ringbuffer_empty ) ;
EXPORT_SYMBOL ( dvb_ringbuffer_free ) ;
EXPORT_SYMBOL ( dvb_ringbuffer_avail ) ;
EXPORT_SYMBOL ( dvb_ringbuffer_flush_spinlock_wakeup ) ;
2008-06-22 21:20:29 +04:00
EXPORT_SYMBOL ( dvb_ringbuffer_read_user ) ;
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( dvb_ringbuffer_read ) ;
EXPORT_SYMBOL ( dvb_ringbuffer_write ) ;
2014-09-04 03:44:04 +04:00
EXPORT_SYMBOL ( dvb_ringbuffer_write_user ) ;