/*
 * netlib.c
 *
 * Copyright 2005, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 */


#include<config.h>
#include<types.h>
#include<errno.h>
#include<mm.h>
#include<lock.h>
#include<time.h>
#include<lib.h>
#include<limits.h>
#include<debug.h>
#include<net/net.h>
#include<net/netlib.h>


/***********************************************************************************
 *
 * チェックサム
 *
 ***********************************************************************************/


/*
 * チェックサムの計算。
 * parameters : data1 address,data2 address,byte size1,byte size2
 * return : 合計の補数
 */
ushort calcSum(uint *data,int size)
{
	union{
		unsigned long long u64;
		uint               u32[2];
		ushort             u16[4];
	}sum;
	uint tmp;


	sum.u64=0;
	for(;size>=sizeof(uint);size-=sizeof(uint))
		sum.u64+=*data++;
	if(size>0)sum.u64+=*data&((1<<(size*8))-1);

	tmp=sum.u32[1];
	sum.u32[1]=0;
	sum.u64+=tmp;
	tmp=sum.u32[1];
	sum.u32[1]=0;
	sum.u32[0]+=tmp;

	tmp=sum.u16[1];
	sum.u16[1]=0;
	sum.u32[0]+=tmp;
	tmp=sum.u16[1];
	sum.u16[1]=0;
	sum.u16[0]+=tmp;

	return ~sum.u16[0];
}

/*
 * Note!
 *  size1はuintサイズで割り切れる事を前提にしている。
 */
ushort calcSumM(uint *data1,uint *data2,int size1,int size2)
{
	union{
		unsigned long long u64;
		uint               u32[2];
		ushort             u16[4];
	}sum;
	uint tmp;


	sum.u64=0;
	for(;size1>=sizeof(uint);size1-=sizeof(uint))
		sum.u64+=*data1++;
	for(;size2>=sizeof(uint);size2-=sizeof(uint))
		sum.u64+=*data2++;
	if(size2>0)sum.u64+=*data2&((1<<(size2*8))-1);

	tmp=sum.u32[1];
	sum.u32[1]=0;
	sum.u64+=tmp;
	tmp=sum.u32[1];
	sum.u32[1]=0;
	sum.u32[0]+=tmp;

	tmp=sum.u16[1];
	sum.u16[1]=0;
	sum.u32[0]+=tmp;
	tmp=sum.u16[1];
	sum.u16[1]=0;
	sum.u16[0]+=tmp;

	return ~sum.u16[0];
}


/***********************************************************************************
 *
 * IPバッファ
 *
 ***********************************************************************************/


enum{
	IPBUF_USE		= 1,	// ipBuf使用中サイン
	IPBUF_NOT_USE	= 0,	// ipBuf未使用中サイン
};


//================================== PRIVATE ============================================


//--------------------------- 固定IPBUF割当 -----------------------------------


enum{
	IPBUF_FIX_SIZE	= IP_SIZE_MAX - sizeof(IP_HEADER),
	IPBUF_NUM_MAX	= 20,								// ipBuf総数
};

typedef struct{
	IP_BUFFER ipBuf;
	char data[IPBUF_FIX_SIZE];
}IP_BUFFER_FIX;

static IP_BUFFER_FIX ipBufFix[IPBUF_NUM_MAX];
static int ipBufFixLock;


/*
 * *** allocIpBuf()からのみ呼び出す ***
 * ipBufを取得する
 * return : NOERR or ERR
 */
static int getIpBuf(IP_BUFFER **o_ipBuf)
{
	static int curNum = 0;	// 現在のipBufFix位置
	int start;
	
	cli();
	enter_spinlock(&ipBufFixLock);
	{
		for (start = curNum; ipBufFix[curNum].ipBuf.use == IPBUF_USE;){
			if (IPBUF_NUM_MAX <= ++curNum){
				curNum = 0;
			}
			if (curNum == start){
				exit_spinlock(&ipBufFixLock);
				sti();
				return ERR;
			}
		}
		ipBufFix[curNum].ipBuf.use = IPBUF_USE;
		*o_ipBuf = (IP_BUFFER*)&ipBufFix[curNum];
		if (IPBUF_NUM_MAX <= ++curNum){
			curNum = 0;
		}
	}
	exit_spinlock(&ipBufFixLock);
	sti();

	return NOERR;
}


//--------------------------- 可変IPBUF割当 -----------------------------------


enum{
	VRL_SIZE_MAX = 0x10000,		// バッファ総サイズ
	VRL_SIZE_MIN = 0x40,
	VRL_ARRAY_MAX = VRL_SIZE_MAX / VRL_SIZE_MIN,	// バッファ配列総数
	VRL_TBL_NUM = 11,			// スラブテーブル数
};

typedef struct{
	ushort	next;
	ushort	prev;
#ifdef DEBUG
	size_t	size;
#endif
}SLAB_ALLOC;

static char variableArea[VRL_SIZE_MAX];

static SLAB_ALLOC slab[VRL_ARRAY_MAX + VRL_TBL_NUM];

static SLAB_ALLOC *slabTbl[] = {
	&slab[VRL_ARRAY_MAX],		// 0x40,	0
	&slab[VRL_ARRAY_MAX + 1],	// 0x80,	1
	&slab[VRL_ARRAY_MAX + 2],	// 0x100,	2
	&slab[VRL_ARRAY_MAX + 3],	// 0x200,	3
	&slab[VRL_ARRAY_MAX + 4],	// 0x400,	4
	&slab[VRL_ARRAY_MAX + 5],	// 0x800,	5
	&slab[VRL_ARRAY_MAX + 6],	// 0x1000,	6
	&slab[VRL_ARRAY_MAX + 7],	// 0x2000,	7
	&slab[VRL_ARRAY_MAX + 8],	// 0x4000,	8
	&slab[VRL_ARRAY_MAX + 9],	// 0x8000,	9
	&slab[VRL_ARRAY_MAX + 10],	// 0x10000,	10
};

static int slabSize[] = {
	1,		// 0
	2,		// 1
	4,		// 2
	8,		// 3
	16,		// 4
	32,		// 5
	64,		// 6
	128,	// 7
	256,	// 8
	512,	// 9
	1024,	// 10
};

// 下位８バイト
static uchar sizeTbl1[] = {
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
	1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
	2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
	2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
	2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
	2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
	2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
	2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
	2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
};
// 上位８バイト
static uchar sizeTbl2[] = {
	 0, 2, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6,
	 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
	 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
	 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
	 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
	 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
	 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
	10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,
};

static int variableLock;


// スラブ配列から実アドレスに変換
static IP_BUFFER *getIpBufAddr(int array)
{
	return (IP_BUFFER*)(variableArea + array * VRL_SIZE_MIN);
}


// 実アドレスからスラブ配列に変換
static int getSlabArray(IP_BUFFER *ipBuf)
{
	return ((uint)ipBuf - (uint)variableArea) / VRL_SIZE_MIN;
}


// ipBufサイズを取得する
static inline int getIpBufSize(IP_HEADER *ip)
{
	return sizeof(IP_BUFFER) - sizeof(IP_HEADER) + getIpSize(ip);
}


// サイズ配列を取得する
static int getTblIndex(const int size)
{
	union{
		int size;
		uchar uc[4];
	}value;
	
	// サイズはIPが割り当てられる最大サイズまで
	ASSERT(size <= VRL_SIZE_MAX);

	if (size < 0x100){
		return sizeTbl1[size];
	}
	else if (size < 0x10000){
		value.size = ROUNDUP(size, 0x100);
		return sizeTbl2[value.uc[1]];
	}
	else{
		return ARRAY_LENGTH(slabTbl) - 1;
	}
}


// 《注意》allocIpBuf()からのみ呼び出すこと
// 《競》《割》
// ipBufを取得する
// return : NOERR or ERR
static int getIpBufVariable(IP_HEADER *ip, IP_BUFFER **o_ipBuf)
{
	int index = getTblIndex(getIpBufSize(ip));
	int array;

	cli();
	enter_spinlock(&variableLock);
	{
		int getIndex;
		int i;

		// バッファテーブルから該当サイズ以上を取得
		for (getIndex = index; VRL_ARRAY_MAX <= slabTbl[getIndex]->next;){
			if (ARRAY_LENGTH(slabTbl) <= ++getIndex){
				exit_spinlock(&variableLock);
				sti();
				return ERR;
			}
		}

		// 該当サイズまで分割していく
		array = slabTbl[getIndex]->next;
		slab[slab[array].next].prev = slab[array].prev;
		slab[slab[array].prev].next = slab[array].next;
		for (i = getIndex - 1; index <= i; --i){
			int nextArray = array + slabSize[i];
			slab[nextArray].next = slab[nextArray].prev = slabTbl[i]->next;
			slabTbl[i]->next = slabTbl[i]->prev = nextArray;
		}
	}
	exit_spinlock(&variableLock);
	sti();

#ifdef DEBUG
	slab[array].size = getIpBufSize(ip);
#endif
	*o_ipBuf = getIpBufAddr(array);

	return NOERR;
}


// ipBufを開放する
// 《競》《割》
static void freeIpBufVariable(IP_BUFFER *ipBuf)
{
	int index = getTblIndex(getIpBufSize(&ipBuf->ip));
	int array = getSlabArray(ipBuf);
	int eflag;

#ifdef DEBUG
	ASSERT(slab[array].size == getIpBufSize(&ipBuf->ip));
#endif

	enterCli(&eflag);
	enter_spinlock(&variableLock);
	{
		for (; index < ARRAY_LENGTH(slabTbl); ++index){
			int nextArray;

			// 同サイズリンクに配列昇順で接続する
			for (nextArray = slabTbl[index]->next;; nextArray = slab[nextArray].next){;
				if (array < nextArray){
					slab[array].next = nextArray;
					slab[array].prev = slab[nextArray].prev;
					slab[slab[nextArray].prev].next = array;
					slab[nextArray].prev = array;
					break;
				}
			}
			
			// サイズの大きい方へ結合していく
			// 上のサイズのサイズ境界かどうかにより結合の相手をかえる
			if (array % (slabSize[index] * 2)){
				array = slab[array].prev;
			}
			if (slab[array].next == array + slabSize[index]){
				slab[slab[array].prev].next = slab[slab[array].next].next;
				slab[slab[slab[array].next].next].prev = slab[array].prev;
			}
			else{
				break;
			}
		}
	}
	exit_spinlock(&variableLock);
	exitCli(&eflag);
}


//================================== PUBLIC =============================================


/*
 * 《注意》受信割り込み時のみに使用すること
 * IPバッファを取得して初期化する
 * parameters : IPパケット,IPバッファポインタ
 * return : 0 or error number
 */
int allocIpBuf(IP_HEADER *ip,IP_BUFFER **o_ipBuf)
{
	IP_BUFFER *ipBuf;
	
	if (ip->prot == IPPROTO_TCP){
		if (getIpBuf(&ipBuf) == ERR){
			return ERR;
		}
	}
	else{
		if (getIpBufVariable(ip, &ipBuf) == ERR){
			return ERR;
		}
	}
	ipBuf->prev = ipBuf->next = ipBuf;
	gettimeofday(&ipBuf->timeval);
	ipBuf->offset = 0;
	ipBuf->access = 0;
	memcpy(&ipBuf->ip,ip,getIpSize(ip));
	*o_ipBuf = ipBuf;

	return 0;
}


/*
 * IPバッファを開放する
 */
void freeIpBuf(IP_BUFFER *ipBuf)
{
	if ((variableArea <= (char*)ipBuf) && ((char*)ipBuf < (variableArea + VRL_SIZE_MAX))){
		freeIpBufVariable(ipBuf);
	}
	else{
		ipBuf->use = IPBUF_NOT_USE;
	}
}


/*
 * IPバッファの空きが無くなったときソケットの保留中のIPバッファを一つ開放する
 */
void freeRecvBuf(SOCKET *sock)
{
	cli();
	enter_spinlock(&sock->lockGate);
	{
		// 最後に接続したIPバッファを開放する
		if (sock->recvBuf != NULL){
			IP_BUFFER *ipBuf = ((IP_BUFFER*)sock->recvBuf)->prev;
			
			if (sock->recvBuf != ipBuf){
				ipBuf->prev->next = ipBuf->next;
				ipBuf->next->prev = ipBuf->prev;
				freeIpBuf(ipBuf);
			}
		}
	}
	exit_spinlock(&sock->lockGate);
	sti();
}


/*
 * IPバッファリンクリストの開放
 */
void releaseIpBuf(IP_BUFFER *ipBuf)
{
	IP_BUFFER *p,*next;

	if (ipBuf == NULL){
		return;
	}
	p = ipBuf;
	do{
		next = p->next;
		freeIpBuf(p);
		p = next;
	}while (p != ipBuf);
}


/*
 * ! 注意！受信割り込み時のみに使用すること
 * 受信待ちバッファにIP識別番号順で接続する
 */
void attachRecvBuf(IP_BUFFER *ipBuf, IP_BUFFER **m_recvBuf, int *m_lock)
{
	cli();
	enter_spinlock(m_lock);
	{
		if (*m_recvBuf == NULL){
			ipBuf->next = ipBuf->prev = ipBuf;
			*m_recvBuf = ipBuf;
		}
		else{
			IP_BUFFER *p;
	
			for (p = (*m_recvBuf)->prev;; p = p->prev){
				if (swapWord(p->ip.id) < swapWord(ipBuf->ip.id)){
					ipBuf->next = p->next;
					ipBuf->prev = p;
					p->next->prev = ipBuf;
					p->next = ipBuf;
					break;
				}
				if (p == *m_recvBuf){
					ipBuf->next = p;
					ipBuf->prev = p->prev;
					p->prev->next = ipBuf;
					p->prev = ipBuf;
					*m_recvBuf = ipBuf;
					break;
				}
			}
		}
	}
	exit_spinlock(m_lock);
	sti();
}


/*
 *《注意》受信割り込み時のみに使用すること
 * 受信待ちバッファにTCPのシーケンス番号順に接続する
 * parameters : ソケット,受信バッファ
 */
void attachRecvBufTcp(IP_BUFFER *ipBuf, IP_BUFFER **m_recvBuf, int *m_lock)
{
	TCP_HEADER *ipBufTcp = (TCP_HEADER*)&ipBuf->ip.data;
	
	cli();
	enter_spinlock(m_lock);
	{
		if (*m_recvBuf == NULL){
			ipBuf->next = ipBuf->prev = ipBuf;
			*m_recvBuf = ipBuf;
		}
		else{
			IP_BUFFER *p;
	
			for (p = (*m_recvBuf)->prev;; p = p->prev){
				TCP_HEADER *tcp = (TCP_HEADER*)&p->ip.data;

				if ((swapInt32(tcp->seqnum) < swapInt32(ipBufTcp->seqnum)) || 
					((swapInt32(tcp->seqnum) == swapInt32(ipBufTcp->seqnum)) && (swapWord(p->ip.id) < swapWord(ipBuf->ip.id))) ){
					ipBuf->next = p->next;
					ipBuf->prev = p;
					p->next->prev = ipBuf;
					p->next = ipBuf;
					break;
				}
				if (p == *m_recvBuf){
					ipBuf->next = p;
					ipBuf->prev = p->prev;
					p->prev->next = ipBuf;
					p->prev = ipBuf;
					*m_recvBuf = ipBuf;
					break;
				}
			}
		}
	}
	exit_spinlock(m_lock);
	sti();
}


/*
 * 受信待ちバッファをリンク削除
 * parameters : 切断IPバッファ、リンク元、ロックゲート
 */
void detachRecvBuf(IP_BUFFER *ipBuf, IP_BUFFER **m_recvBuf, int *m_lock)
{
	// 競合する処理"attachRecvBuf()"が割り込み時に使用されるので
	// 割り込み禁止。
	cli();
	enter_spinlock(m_lock);
	{
		ipBuf->next->prev = ipBuf->prev;
		ipBuf->prev->next = ipBuf->next;
		if (*m_recvBuf == ipBuf){
			if (ipBuf->next == ipBuf){
				*m_recvBuf = NULL;
			}
			else{
				*m_recvBuf = ipBuf->next;
			}
		}
	}
	exit_spinlock(m_lock);
	sti();
}


// 受信待ちバッファから取り出す
// parameters : ソケット,受信バッファ
// return : ipBuf or NULL
IP_BUFFER *getRecvBuf(IP_BUFFER **m_recvBuf, int *m_lock)
{
	IP_BUFFER *ipBuf;
	
	// 競合する処理"attachRecvBuf()"が割り込み時に使用されるので
	// 割り込み禁止。
	cli();
	enter_spinlock(m_lock);
	{
		ipBuf = *m_recvBuf;

		if (ipBuf != NULL){
			if (ipBuf->next == ipBuf){
				*m_recvBuf = NULL;
			}
			else{
				ipBuf->next->prev = ipBuf->prev;
				ipBuf->prev->next = ipBuf->next;
				*m_recvBuf = ipBuf->next;
			}
		}
	}
	exit_spinlock(m_lock);
	sti();

	return ipBuf;
}


// 受信待ちバッファを参照する
// return : ipBuf ro NULL
IP_BUFFER *refIpBuf(IP_BUFFER* ipBuf)
{
	return ipBuf;
}


// 受信待ちバッファ（TCP）の空きがあるか。
// return : TRUE or FALSE
int isIpBufFree()
{
	int i;

	for (i = 0; i < IPBUF_NUM_MAX; ++i){
		if (ipBufFix[i].ipBuf.use == IPBUF_NOT_USE){
			return TRUE;
		}
	}

	return FALSE;
}


// 初期化
void initIpBuf()
{
	int i;

	slab[0].next = slab[0].prev = VRL_ARRAY_MAX + ARRAY_LENGTH(slabTbl) - 1;
	for (i = VRL_ARRAY_MAX; i < VRL_ARRAY_MAX + ARRAY_LENGTH(slabTbl) - 1; ++i){
		slab[i].next = slab[i].prev = i;
	}
	slab[VRL_ARRAY_MAX + ARRAY_LENGTH(slabTbl) - 1].next = slab[VRL_ARRAY_MAX + ARRAY_LENGTH(slabTbl) - 1].prev = 0;
}


/***********************************************************************************
 *
 * リングバッファ
 * 	バッファサイズ内で循環して読み書きする。
 *
 ***********************************************************************************/


enum{
	RING_BUF_SIZE_MAX = 0x10000,	// バッファ最大サイズ
};

// リングバッファ
typedef struct{
	size_t size;		// バッファサイズ
	size_t start;		// バッファ内データ始め
	size_t last;		// 現在のバッファ内データ最後
	char *buf;			// バッファ
}RING_BUF;


//================================== グローバル ==========================================


// リングバッファの割当と初期化
// return : 0 or error number
int allocRingBuf(const size_t size, const size_t start, void **o_ringBuf)
{
	RING_BUF *ringBuf;
	
	// バッファの最大サイズはRING_BUF_SIZE_MAX
	ASSERT(size <= RING_BUF_SIZE_MAX);

	ringBuf = kmalloc(sizeof(*ringBuf));
	if (ringBuf == NULL){
		return -ENOMEM;
	}
	
	ringBuf->buf = kmalloc(size);
	if (ringBuf->buf == NULL){
		return -ENOMEM;
	}
	
	ringBuf->size = size;
	ringBuf->start = ringBuf->last = start;
	
	*o_ringBuf = ringBuf;
	
	return 0;
}


// リングバッファの開放
void freeRingBuf(void *i_ringBuf)
{
	RING_BUF *ringBuf = i_ringBuf;
	
	kfree(ringBuf->buf);
	kfree(ringBuf);
}


// リングバッファの"start"値を得る
size_t getStartRingBuf(void *i_ringBuf)
{
	RING_BUF *ringBuf = i_ringBuf;

	return ringBuf->start;
}


// リングバッファの"last"値を得る
size_t getLastRingBuf(void *i_ringBuf)
{
	RING_BUF *ringBuf = i_ringBuf;

	return ringBuf->last;
}


// リングバッファの"start"値の設定
void setStartRingBuf(void *i_ringBuf, size_t start)
{
	RING_BUF *ringBuf = i_ringBuf;

	if ((ringBuf->start < start) && (start <= ringBuf->last)){
		ringBuf->start = start;
	}
}


// リングバッファのバッファサイズを得る
// return : バッファサイズ
int getSizeRingBuf(void *i_ringBuf)
{
	RING_BUF *ringBuf = i_ringBuf;

	return ringBuf->size;
}


// リングバッファの残りバッファサイズを得る
// return : 残りのバッファサイズ
int getAvailableSizeRingBuf(void *i_ringBuf)
{
	RING_BUF *ringBuf = i_ringBuf;

	return ringBuf->size - (ringBuf->last - ringBuf->start);
}


// リングバッファからデータを読み出す
void readRingBuf(void *i_ringBuf, const size_t i_start, const size_t len, void *o_readBuf)
{
	RING_BUF *ringBuf = i_ringBuf;
	int start = i_start % ringBuf->size;
	char *readBuf = o_readBuf;
	
	ASSERT((ringBuf->start <= i_start) && (i_start < ringBuf->last));
	ASSERT(i_start + len <= ringBuf->last);
	
	if (ringBuf->size < start + len){
		int firstSize = ringBuf->size - start;
		memcpy(readBuf, ringBuf->buf + start, firstSize);
		memcpy(readBuf + firstSize, ringBuf->buf, len - firstSize);
	}
	else{
		memcpy(readBuf, ringBuf->buf + start, len);
	}
}


// 書き込みは"last"値から書き込む
void writeRingBuf(void *i_ringBuf, const size_t len, void *i_writeBuf)
{
	RING_BUF *ringBuf = i_ringBuf;
	int start = ringBuf->last % ringBuf->size;
	char *writeBuf = i_writeBuf;
	
	ASSERT(ringBuf->last + len <= ringBuf->start + ringBuf->size);
	
	if (ringBuf->size < start + len){
		int firstSize = ringBuf->size - start;
		memcpy(ringBuf->buf + start, writeBuf, firstSize);
		memcpy(ringBuf->buf, writeBuf + firstSize, len - firstSize);
	}
	else{
		memcpy(ringBuf->buf + start, writeBuf, len);
	}
	ringBuf->last += len;
}


/************************************************************************************
 *
 * ソケットオプション
 *
 ************************************************************************************/


//================================== プライベート ==========================================


/*
 * ソケットレベルオプションの設定
 * return : 0 or error number
 */
static int setSockOpt(SOCKET *sock, int optName, const int *optVal, int optLen)
{
	switch (optName){
		case SO_TIMESTAMP:
			// 下へ
		case SO_REUSEADDR:
			if (optLen < sizeof(int)){
				return -EINVAL;
			}
			switch (*optVal){
				case 1:
					sock->sockOpt.name |= optName;
					break;
				case 0:
					sock->sockOpt.name &= ~optName;
					break;
				default:
					return -EINVAL;
			}
			break;
		default:
			return -ENOPROTOOPT;
	}

	return 0;
}


/*
 * IPレベルオプションの設定
 * return : 0 or error number
 */
static int setIpOpt(SOCKET *sock, int optName, const int *optVal, int optLen)
{
	switch (optName){
		case IP_TOS:
			if (optLen < sizeof(int)){
				return -EINVAL;
			}
			sock->ipOpt.name = optName;
			sock->ipOpt.value = *optVal;
			break;
		default:
			return -ENOPROTOOPT;
	}

	return 0;
}


//================================== グローバル ==========================================


/*
 * ソケットオプションの取得
 */
int getOpt(SOCKET *sock, int level, int optName, int *optVal, int *optLen)
{
/****************************************************************************************************************/
printk("%s getOpt() : getsockopt() is not suport!",__FILE__);
/****************************************************************************************************************/

	return -ENOPROTOOPT;
}


/*
 * ソケットオプションの設定
 */
int setOpt(SOCKET *sock, int level, int optName, const int *optVal, int optLen)
{
	switch (level){
		case SOL_SOCKET:
			return setSockOpt(sock, optName, optVal, optLen);
		case IPPROTO_IP:
			return setIpOpt(sock, optName, optVal, optLen);
		default:
			return -ENOPROTOOPT;
	}
	
	return 0;
}


/*
 * ソケットオプションの実行
 * return : 0
 */
int doSockOpt(SOCKET *sock,IP_BUFFER *ipBuf,caddr_t cmsghdr,u_int cmsgLen)
{
	struct cmsghdr *cmsg = (struct cmsghdr*)cmsghdr;
	
	if (sock->sockOpt.name & SO_TIMESTAMP){
		/* メッセージ長の確認 */
		if (cmsgLen < sizeof(struct cmsghdr) + sizeof(struct timeval)){
			cmsg->cmsg_len = 0;
			return 0;
		}

		/* 受信時刻の取得 */
		cmsg->cmsg_level = SOL_SOCKET;
		cmsg->cmsg_type = SCM_TIMESTAMP;
		cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(struct timeval);
		memcpy(CMSG_DATA(cmsg), &ipBuf->timeval, sizeof(struct timeval));
	}
	
	return 0;
}
