/**************************************************************************
 *
 * Copyright (c) 2007-2009 Kip Macy kmacy@freebsd.org
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. The name of Kip Macy nor the names of other
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD$
 *
 ***************************************************************************/

#ifndef	_SHMBUF_RING_H_
#define	_SHMBUF_RING_H_

#include <machine/bsd_cpu.h>
#include <sys/bsd_mbuf.h>
#include <netinet/bsd_in.h>
#include <unistd.h>

#include <pthread.h>
#include "if_host.h"
#include "host_serv.h"

extern void mtx_lock(struct mtx *m);
extern void mtx_unlock(struct mtx * m);

struct shm_bufring {
	char	buf[HIF_SHMBUF_SIZE*HIF_MAX_SHMBUF];
	volatile int	read_off;
	volatile int 	write_off;
	char	pad[4088];
};

#if 0
struct shm_bufring {
	volatile uint32_t	br_prod_head;
	volatile uint32_t	br_prod_tail;	
	int              	br_prod_size;
	int              	br_prod_mask;
	uint64_t		br_drops;
	uint64_t		br_prod_bufs;
	uint64_t		br_prod_bytes;
	/*
	 * Pad out to next L2 cache line
	 */
	uint64_t	  	_pad0[11];

	volatile uint32_t	br_cons_head;
	volatile uint32_t	br_cons_tail;
	int		 	br_cons_size;
	int              	br_cons_mask;
	
	/*
	 * Pad out to next L2 cache line
	 */
	uint64_t	  	_pad1[14];
//#ifdef DEBUG_SHMBUFRING
	struct mtx		*br_lock;
//#endif	
	void			*br_ring[0];
};
#endif //0

static inline int shm_bufring_enqueue(struct shm_bufring *br, struct mbuf* m)
{
	struct mbuf  *mm;
	char    *shmbuf, *buf_ptr;
	int	next_off = (br->write_off + 1) % HIF_MAX_SHMBUF;

	/* wait for the full case */
	while(next_off == br->read_off)
		next_off = (br->write_off + 1) % HIF_MAX_SHMBUF;

	shmbuf = br->buf + HIF_SHMBUF_SIZE * br->write_off;
	
	/* copy data into shm buffer*/
	if(m->m_next == NULL){
		/* send out the packet*/
		bcopy(m->m_data, shmbuf, m->m_pkthdr.len);
	} else {
		buf_ptr = shmbuf;
		for(mm = m; mm != NULL; mm = mm->m_next){
			bcopy(mtod(mm, void *), (void*)buf_ptr, mm->m_len);
			buf_ptr += mm->m_len;
		}
	}

	/* update buffer*/
	br->write_off = (br->write_off + 1) % HIF_MAX_SHMBUF;

	return 0;
}

/*
 * single-consumer dequeue 
 * use where dequeue is protected by a lock
 * e.g. a network driver's tx queue lock
 */
static inline int shm_bufring_dequeue(struct shm_bufring *br, char* buf, int len)
{
	unsigned short pkt_len;
	char	*pkt_ptr;
	int	waited = 0;

	/* wait for the empty case */
	while(br->read_off == br->write_off){
		if(waited == 0){
			host_thread_enter_wait_region();
			waited = 1;
		}
		usleep(1);
	}

	if(waited == 1){
		host_thread_exit_wait_region();
		waited = 0;
	}

	pkt_ptr = br->buf + HIF_SHMBUF_SIZE * br->read_off;

	/* copy data from shm to mbuf*/
	pkt_len = ntohs(*((unsigned short*)(pkt_ptr+2)));
	//pkt_len = (pkt_len << 8) | pkt_ptr[3];
	if(pkt_len > len){
		return 0;
	} else{
		bcopy(pkt_ptr, buf, pkt_len);
		br->read_off = (br->read_off + 1) % HIF_MAX_SHMBUF;
		return pkt_len;
	}
}

#endif
