/*---------------------------------------------------------------------------*\

	'Linear' ring buffers

	Author: Ron Lee, 13 Mar 2007


	Copyright (C) 2007 Ron Lee, Voicetronix www.voicetronix.com.au

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	version 2 as published by the Free Software Foundation.

	This library 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
	General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this library; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
	MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

#ifndef __VT_LINRINGBUF_H
#define __VT_LINRINGBUF_H

#include <linux/vmalloc.h>
#include <linux/pagemap.h>

// The buffer indexes wrap at 32 bits regardless of the size of the storage.
// The buffer is empty if (read == write).
// The buffer is full if (write - read == size).
// The next location to write is at (write & (size - 1))
// The next location to read is at (read & (size - 1))
// The read and write pointers are advanced by the number of bytes to remove
// from, or add to the buffer, respectively.
//
// There are two practical behaviours when the buffer is full.
//  - disallow more writes until it is emptied some.
//  - overwrite the oldest data and carry the read pointer forward
// The former may be thread safe with a single reader and a single writer.
// The latter requires writes to lock out reads when the buffer overflows.
//  - The size of a possible read will never get smaller when overflow occurs
//  - The reader must ensure their start pointer is valid and remains so from
//    the time they begin a read until the time they update the read pointer
//    themselves.  It it safe at other times for the writer to also move the
//    read pointer.
typedef struct {
	struct page *storage;
	char        *start;
	u32          read;
	u32          write;
} lring_buf;

inline int init_lring_buf( lring_buf *buf )
{ //{{{
	buf->storage = alloc_page( GFP_KERNEL );

	if( buf->storage )
	{
		struct page *pages[2] = { buf->storage, buf->storage };

		buf->read  = buf->write = 0;
		buf->start = vmap( pages, 2, VM_MAP, PAGE_KERNEL );

		return 1;
	}
	return 0;
} //}}}

inline void release_lring_buf( lring_buf *buf )
{ //{{{
	vunmap( buf->start );
	__free_page( buf->storage );
} //}}}

// Return the number of bytes available for reading.
inline int lring_buf_how_full( lring_buf *buf )
{ //{{{
	return buf->write - buf->read;
} //}}}

// Return the number of bytes available for writing.
inline int lring_buf_how_empty( lring_buf *buf )
{ //{{{
	return PAGE_SIZE - (buf->write - buf->read);
} //}}}

// Return the start address of the readable bytes.
inline void *lring_buf_read( lring_buf *buf )
{ //{{{
	return buf->start + (buf->read & (PAGE_SIZE - 1));
} //}}}

// Return the start address of the writable bytes.
inline void *lring_buf_write( lring_buf *buf )
{ //{{{
	return buf->start + (buf->write & (PAGE_SIZE - 1));
} //}}}


#endif  // __VT_LINRINGBUF_H

