/*
 * udp.c
 *
 * Copyright 2004, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * User datagram Protocl.
 */


#include<types.h>
#include<lib.h>
#include<errno.h>
#include<mm.h>
#include<proc.h>
#include<time.h>
#include<lock.h>
#include<interrupt.h>
#include<signal.h>
#include<time.h>
#include<net/net.h>
#include<net/ip.h>
#include<net/ether.h>
#include<net/udp.h>
#include<net/netlib.h>


//#define DEBUG_UDP 1

#ifdef DEBUG_UDP
	#define STATIC
#else
	#define STATIC static
#endif

/**************************************************************************
 *
 * 共通
 *
 **************************************************************************/


STATIC int sendNotReach()
{
	return 0;
}


/**************************************************************************
 *
 * 受信
 *
 **************************************************************************/


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


static SOCKET waitSocket;	/* ウエイトソケットリンク。 */
static int waitLock;		/* コネクションリンク用ロックゲート。 */


/*
 * 受信待ちソケットリンクに接続する。
 * すでに接続済でも切り離してから接続する。
 */
STATIC void setWaitLink(SOCKET *sock)
{
	cli();
	enter_spinlock(&waitLock);
	{
		sock->next->prev = sock->prev;
		sock->prev->next = sock->next;
		sock->next = waitSocket.next;
		sock->prev = &waitSocket;
		waitSocket.next->prev = sock;
		waitSocket.next = sock;
	}
	exit_spinlock(&waitLock);
	sti();
}

/*
 * 受信待ちソケットリンクから切り離す。
 * parameters : socket
 */
STATIC void resetWaitLink(SOCKET *sock)
{
	cli();
	enter_spinlock(&waitLock);
	{
		sock->next->prev = sock->prev;
		sock->prev->next = sock->next;
		sock->next = sock->prev = sock;
	}
	exit_spinlock(&waitLock);
	sti();
}

/*
 * ＊＊＊ 割り込み処理関数 ＊＊＊
 * Save receive data.
 * return : 0 or task switch=1
 */
STATIC int saveRecv(IP_HEADER *ip)
{
	SOCKET *sock;
	UDP_HEADER *udp = (UDP_HEADER*)ip->data;
	IP_BUFFER *ipBuf;

	for (sock = waitSocket.next; sock != &waitSocket; sock = sock->next)
	{
		/* 受信ソケットあり */
		if (sock->srcport == udp->dstport)
		{
			if (allocIpBuf(ip,&ipBuf) == ERR){
				return 0;
			}
			attachRecvBuf(ipBuf, (IP_BUFFER**)&sock->recvBuf, &sock->lockGate);
			if (sock->waitProc != NULL)
			{	
				sys_wake(sock->waitProc);
				return 1;
			}
			return 0;
		}
	}

	// 見つからず
	setIpTask(NULL, ip);

	return 0;
}


/*================================== パブリック ========================================*/


/*
 * 受信データを取得する
 * return : 0 or error number
 */
STATIC int waitReceive(SOCKET *sock,uint timeout)
{
	for (;;){
		/* シグナル割り込みが起きたか？ */
		doSignalRestart();
		if(isSigint() == TRUE){
			return -EINTR;
		}

		if (sock->recvBuf == NULL){
			sock->waitProc = get_current_task();
			if (sys_sleep(timeout) == 0){		/* Time out. */
				if (sock->waitProc == NULL){
					return -ETIMEDOUT;
				}
			}
		}
		else{
			break;
		}
	}

	return 0;
}

/*
 * 初期化
 */
STATIC void initRecv()
{
	waitSocket.next = waitSocket.prev = &waitSocket;
}


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


/*
 * ＊＊＊ 割り込み処理関数 ＊＊＊
 * Receive UDP frame.
 * return : 0 or task switch=1
 */
int receiveUdp(IP_HEADER *ip)
{
	PSEUDO_HEADER pshead;
	UDP_HEADER *udp = (UDP_HEADER*)ip->data;

/*********************************************************************************************
if (ip->srcip == 0x20019ac)
{
	union{
		uint ui;
		uchar uc[4];
	}src_ip;

	src_ip.ui = ip->srcip;
	printk("RECV UDP srcport=%x,dstport=%x,length=%d,ip=%u.%u.%u.%u\n",
		swapWord(udp->srcport),swapWord(udp->dstport),swapWord(udp->len),
		src_ip.uc[0],src_ip.uc[1],src_ip.uc[2],src_ip.uc[3]);
}
**********************************************************************************************/
	/* チェックサムの確認。 */
	pshead.srcip=ip->srcip;
	pshead.dstip=ip->dstip;
	pshead.tmp=0;
	pshead.prot=ip->prot;
	pshead.len=udp->len;
	if (calcSumM((uint*)&pshead,(uint*)udp,sizeof(PSEUDO_HEADER),swapWord(udp->len)))
		return 0;

	return saveRecv(ip);
}


/**************************************************************************
 *
 * UDPサービス関数
 *
 **************************************************************************/


/*
 * IPスレッドから呼び出される
 */
void doUdpService(SOCKET *sock, IP_HEADER *ip)
{
	if (sock == NULL){
		sendNotReach();
	}
}


/**************************************************************************
 *
 * ソケットサービス関数
 *
 **************************************************************************/


void releaseSockUdp(SOCKET *sock)
{
	resetWaitLink(sock);
}

STATIC int send(SOCKET *sock, void *msg, size_t len, int flags, struct sockaddr *to)
{
	char head[sizeof(PSEUDO_HEADER) + sizeof(UDP_HEADER)];
	PSEUDO_HEADER *pshead;
	UDP_HEADER *udp;
	TRANS_BUF_INFO tbi;


	/* Set pseudo header. */
	pshead = (PSEUDO_HEADER*)head;
	if (to == NULL)
		pshead->dstip = sock->dstip;
	else
		pshead->dstip = ((struct sockaddr_in*)to)->sin_addr.s_addr;
	pshead->srcip = getSrcip(pshead->dstip);
	pshead->tmp = 0;
	pshead->prot = IPPROTO_UDP;
	pshead->len = swapWord(len + sizeof(UDP_HEADER));

	/* Set UDP head. */
	udp = (UDP_HEADER*)(head + sizeof(PSEUDO_HEADER));
	udp->srcport = sock->srcport;
	if (to == NULL)
		udp->dstport = sock->dstport;
	else
		udp->dstport = ((struct sockaddr_in*)to)->sin_port;
	udp->len = swapWord(len + sizeof(UDP_HEADER));
	udp->chksum = 0;
	udp->chksum = calcSumM((uint*)head,(uint*)msg,sizeof(PSEUDO_HEADER) + sizeof(UDP_HEADER),len);

	/* バッファテーブルの設定 */
	tbi.data[2] = (char*)msg;
	tbi.size[2] = len;
	tbi.data[1] = (char*)udp;
	tbi.size[1] = sizeof(UDP_HEADER);
	tbi.ipType = IPPROTO_UDP;
/********************************************************************************************
{
	union{
		uint ui;
		uchar uc[4];
	}dst_ip;

	dst_ip.ui = sock->dstip;
	printk("TRANS UDP srcport=%x,dstport=%x,length=%d,ip=%u.%u.%u.%u\n",
		swapWord(udp->srcport),swapWord(udp->dstport),swapWord(udp->len),
		dst_ip.uc[0],dst_ip.uc[1],dst_ip.uc[2],dst_ip.uc[3]);
}
*********************************************************************************************/
	transIp(sock, &tbi, pshead->dstip, 0);

	return len;
}

/*
 * return : copy data size or error number
 */
STATIC int recv(SOCKET *sock,struct msghdr *msg)
{
	enum{REPLY_TIMEOUT = 2000};	/* 返答タイムアウトミリ秒。 */
	int dataSize,cpSize,iovArrey,iovOffset;
	IP_BUFFER *ipBuf;
	IP_HEADER *ip;
	UDP_HEADER *udp;
	int rest;

	/* 受信データを取得する */
	setWaitLink(sock);
	rest = waitReceive(sock,REPLY_TIMEOUT);
	if (rest < 0)
		return rest;
	ipBuf = sock->recvBuf;

	/* データをバッファにコピー */
	ip = &ipBuf->ip;
	udp = (UDP_HEADER*)ip->data;
	dataSize = swapWord(udp->len) - sizeof(UDP_HEADER) - ipBuf->offset;
	iovArrey = iovOffset = 0;
	cpSize = copyIovec(udp->data + ipBuf->offset,dataSize,msg->msg_iov,msg->msg_iovlen,&iovArrey,&iovOffset);
	if (iovArrey == msg->msg_iovlen)
	{
		/*
		 * ユーザーバッファがいっぱいだったら再コピーまたはEOFを送るためにIPバッファは残しておく。
		 */
		ipBuf->offset += cpSize;
		return cpSize;
	}

	sock->dstip = ip->srcip;
	sock->dstport = udp->srcport;

	/* ソケットオプションを実行する */
	if (msg->msg_control != NULL)
		doSockOpt(sock,ipBuf,msg->msg_control,msg->msg_controllen);

	/* IPバッファを開放 */
	detachRecvBuf(ipBuf, (IP_BUFFER**)&sock->recvBuf, &sock->lockGate);
	freeIpBuf(ipBuf);

	return dataSize;
}

STATIC int connect(SOCKET *sock)
{
	setWaitLink(sock);
	
	return 0;
}

STATIC int listen(SOCKET *sock,int fd)
{
	return -EOPNOTSUPP;
}

STATIC int accept(SOCKET *sock,SOCKET **newSock,uint32_t *srcip)
{
	return -EOPNOTSUPP;
}

STATIC int shutdown(SOCKET *sock,int flag)
{
	return -EOPNOTSUPP;
}

STATIC int poll(SOCKET *sock)
{
	if (sock->recvBuf != NULL){
		return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
	}
	else{
		sock->waitProc = get_current_task();
		setWaitLink(sock);
		return POLLOUT | POLLWRNORM;
	}
}


/**************************************************************************
 *
 * 初期化
 *
 **************************************************************************/


STATIC SOCKET_INFO sockInfo = {send, recv, connect, shutdown, listen, accept, poll};


/*
 * GLOBAL
 * Init UDP.
 */
int initUdp()
{
	registSocket(&sockInfo,IPPROTO_UDP);
	initRecv();

	return 0;
}
