/*	
 *	clipip.h
 *
 *	IPプロトコルドライバ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2005 Naoyuki Sawa
 *
 *	* Wed Aug 25 05:43:00 JST 2005 Naoyuki Sawa
 *	- 1st リリース。
 *	* Wed Nov 18 21:37:53 JST 2015 Naoyuki Sawa
 *	- 'extern "C" {'〜'}'で囲みました。.cppを含むWin32プロジェクトからもインクルード出来るようにするためです。
 */
#ifndef __CLIP_IP_H__
#define __CLIP_IP_H__

#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus

/****************************************************************************
 *	IPプロトコルドライバ
 ****************************************************************************/

typedef struct _IPCLIENT IPCLIENT;

/* IPCLIENT.recv : 受信完了コールバックのプロトタイプ
 * - 通常、宛て先IPアドレスはデバイスのIPアドレスであるはずですから、
 *   受信完了コールバックに伝える必要は無いように思うかもしれません。
 *   しかし実際には、ブロードキャストアドレスである可能性もあります。
 * - TCPやUDPドライバの受信処理は、宛て先アドレスを含む疑似ヘッダを作成して、
 *   受信パケットのチェックサムを計算し、受信パケットのエラー検出を行います。
 *   従って、受信完了コールバックに宛て先IPアドレスを明示する必要があります。
 */
typedef void IPRECV(IPCLIENT* client,
	int source_ip_address,			/* 送信元IPアドレス */
	int destination_ip_address,		/* 宛て先IPアドレス */
	const unsigned char* data/*[length]*/,	/* IPデータグラムのデータ部分 */
	int length);				/* IPデータグラムのデータ長 */

/* IPCLIENT.conf : 設定変更コールバックのプロトタイプ */
typedef void IPCONF(IPCLIENT* client);

/* クライアント登録用の構造体 */
/*typedef*/ struct _IPCLIENT {

	/*------------------------------------------------------------------------------*
	 *	ip_open()を呼び出す前に、アプリケーションにて設定しておくフィールド。	*
	 *	ip_open()を呼び出した後は、変更してはいけない。				*
	 *------------------------------------------------------------------------------*/

	int protocol;		/* プロトコルID */
	IPRECV* recv;		/* 受信完了コールバック (NULL:無し) */
	IPCONF* conf;		/* 設定変更コールバック (NULL:無し) */

	/*------------------------------------------------------------------------------*
	 *	IPプロトコルドライバが、内部で使用するフィールド。			*
	 *	アプリケーションは、以下のフィールドにアクセスしてはいけない。		*
	 *------------------------------------------------------------------------------*/

	LIST_ENTRY list_entry;	/* クライアントリスト */

} /*IPCLIENT*/;

/* ネットワーク設定 */
typedef struct _IPCONFIG {
	int ip_address;		/* IPアドレス */
	int subnet_mask;	/* サブネットマスク */
	int default_gateway;	/* デフォルトゲートウェイ */
} IPCONFIG;

/*--------------------------------------------------------------------------*/

/* IPプロトコルドライバを開始します。
 * [note]
 *	* ip_start()を呼び出したアプリケーションは、終了前に必ず、ip_stop()を呼び出してください。
 *	  ip_stop()を呼び出さずにアプリケーションを終了すると、P/ECEシステムが異常動作を起こします。
 *	* ip_open()を呼び出してクライアントを登録する前に、必ず、ip_start()を呼び出してください。
 * [interrupt]
 *	通常処理	割り込み許可状態での呼び出し	○
 *	通常処理	割り込み禁止状態での呼び出し	○
 *	割り込み処理	割り込み禁止状態での呼び出し	×
 */
void ip_start();

/* IPプロトコルドライバを停止します。
 * [note]
 *	* ip_stop()は、常に安全に呼び出し可能です。
 *	  ip_start()を呼び出していなくても、いつでも、何回でも、ip_stop()を呼び出して構いません。
 *	* ip_stop()を呼び出すと、登録されているクライアントは、自動的にすべて登録解除されます。
 *	  ip_stop()を呼び出す前に、個々のクライアントを、明示的に登録解除する必要はありません。
 *	  また、ip_stop()を呼び出した後に、ip_close()を呼び出してはいけません。
 * [interrupt]
 *	通常処理	割り込み許可状態での呼び出し	○
 *	通常処理	割り込み禁止状態での呼び出し	○
 *	割り込み処理	割り込み禁止状態での呼び出し	×
 */
void ip_stop();

/* 特定の上位プロトコルを処理する、クライアントを登録します。
 * [in]
 *	client		呼び出し側にて確保した、クライアント登録用の構造体。
 *			各フィールドを、あらかじめ適切に初期化しておいてください。
 * [note]
 *	* 同じ上位プロトコルを処理するクライアントを、同時に二つ以上登録しようとすると、エラー終了します。
 *	* クライアント登録用の構造体は、IPプロトコルドライバ内部のリストにリンクされます。
 *	  ip_close()を呼び出して登録解除するまで、構造体を開放してはいけません。
 * [interrupt]
 *	通常処理	割り込み許可状態での呼び出し	○
 *	通常処理	割り込み禁止状態での呼び出し	○
 *	割り込み処理	割り込み禁止状態での呼び出し	△　可能だが、推奨しない
 */
void ip_open(IPCLIENT* client);

/* 登録済みクライアントを、登録解除します。
 * [in]
 *	client		登録済みクライアントの構造体。
 * [note]
 *	* 未登録のクライアントを登録解除しようとすると、エラー終了します。
 * [interrupt]
 *	通常処理	割り込み許可状態での呼び出し	○
 *	通常処理	割り込み禁止状態での呼び出し	○
 *	割り込み処理	割り込み禁止状態での呼び出し	△　可能だが、推奨しない
 */
void ip_close(IPCLIENT* client);

/* IPデータグラムを送信します。
 * [in]
 *	client		登録済みクライアントの構造体。
 *	address		宛て先IPアドレス。          (      6バイト)
 *	data		IPデータグラムのデータ部分。(0..1480バイト) ※2005/09/06現在の実装は、IPフラグメント処理未対応です。
 *	length		IPデータグラムのデータ長。  (0..1480      )   今後の対応により、1480バイト以上も可能となる予定です。
 * [out]
 *	戻り値		成功ならば、データ長(length引数の値)をそのまま返します。(0、または、正数)
 *			送信キューがいっぱいで送信できない場合、負数を返します。
 * [note]
 *	* 2005/09/06現在の実装は、IPフラグメント処理未対応です。
 *	  下位プロトコル(Ethernet)のMTUが1500であり、本ドライバの生成するIPデータグラムのヘッダサイズは20バイト固定なので、
 *	  IPデータグラムのデータ部分の最大長は、1480バイトとなります。
 *	  2005/09/06現在の実装は、lengthに1480を超える値を指定すると、呼び出し先のethernet_send()にてエラー終了します。
 *	* ip_send()は、内部でarp_request()を呼び出して、宛て先IPアドレスに対応するEthernetアドレスを取得しています。
 *	- ip_send()を割り込み許可状態で呼び出した場合、宛て先IPアドレスがARPテーブルに未登録ならば、
 *	  ip_send()から呼び出されたarp_request()は、ARP要求を発行し、既定のタイムアウト時間(1秒間)、ARP応答を待ちます。
 *	  すなわち、割り込み許可状態でip_send()を呼び出した場合、ip_send()が処理を返すまでに約1秒間かかる可能性があります。
 *	- ip_send()を割り込み禁止状態で呼び出した場合、宛て先IPアドレスがARPテーブルに未登録ならば、
 *	  ip_send()から呼び出されたarp_request()は、ARP要求を発行後、即座にタイムアウトします。
 *	  割り込み許可状態でip_send()を呼び出した場合、宛て先IPアドレスがARPテーブルに未登録ならば、ip_send()は失敗します。
 * [interrupt]
 *	通常処理	割り込み許可状態での呼び出し	○　(アドレス解決ができなければ、ブロックする)
 *	通常処理	割り込み禁止状態での呼び出し	○　(アドレス解決ができなければ、ブロックせずに失敗する)
 *	割り込み処理	割り込み禁止状態での呼び出し	○　(アドレス解決ができなければ、ブロックせずに失敗する)
 */
int ip_send(IPCLIENT* client, int address, const void* data/*[length]*/, int length);

/* ネットワーク設定を変更します。
 * [in]
 *	config		IPCONFIG構造体へのポインタ。(呼び出し側にて適切に設定しておくこと)
 * [interrupt]
 *	通常処理	割り込み許可状態での呼び出し	○
 *	通常処理	割り込み禁止状態での呼び出し	○
 *	割り込み処理	割り込み禁止状態での呼び出し	○
 */
void ip_set_config(const IPCONFIG* config);

/* ネットワーク設定を取得します。
 * [in]
 *	config		結果を格納するIPCONFIG構造体へのポインタ。
 * [interrupt]
 *	通常処理	割り込み許可状態での呼び出し	○
 *	通常処理	割り込み禁止状態での呼び出し	○
 *	割り込み処理	割り込み禁止状態での呼び出し	○
 */
void ip_get_config(IPCONFIG* config);

/****************************************************************************
 *	ARPプロトコルドライバ
 ****************************************************************************/

/* ARPテーブルの要素 */
typedef struct _ARPENTRY {
	int ip_address;				/* IPアドレス */
	unsigned char ethernet_address[6];	/* Ethernetアドレス */
} ARPENTRY;

/*--------------------------------------------------------------------------*/

/* ARPテーブルを検索し、IPアドレスに対応するEthernetアドレスを取得します。
 * ARPテーブルに登録されていなければ、ARP要求を送信し、ARP応答を待ちます。
 * [in]
 *	ip_address	検索するIPアドレス。
 *	ethernet_address IPアドレスに対応するEthernetアドレスを格納するためのバッファ。
 *	timeout		タイムアウト時間。(負数ならば、既定のタイムアウト時間を使用)
 * [out]
 *	戻り値		取得に成功したら、0を返します。
 *			取得に失敗したら、0以外の値を返します。
 * [note]
 *	* 検索するIPアドレスがARPテーブルに登録されている場合、arp_request()は即座に完了し、成功を返します。
 *	* 検索するIPアドレスがARPテーブルに登録されていない場合、arp_request()はARP要求を送信します。
 *	- 割り込み許可状態で呼ばれた場合、timeout引数にて指定されたタイムアウト時間の間、ARP応答を待ちます。
 *	  タイムアウト時間内にARP応答が得られた場合、arp_request()は成功を返します。
 *	  タイムアウト時間内にARP応答が得られなかった場合、arp_request()は失敗を返します。
 *	- 割り込み禁止状態で呼ばれた場合、arp_request()はARP要求を送信後、即座に完了し、常に失敗を返します。
 *	  割り込み禁止状態でもARP要求を送信することにしあｔ理由は、次回にarp_request()が呼ばれるまでの間にARP応答を受信し、
 *	  ARPテーブルに登録されて、次回のarp_request()が即座に成功することを期待して、このような仕様としました。
 * [interrupt]
 *	通常処理	割り込み許可状態での呼び出し	○　ブロッキング
 *	通常処理	割り込み禁止状態での呼び出し	○　ノンブロッキング (timeout=0と見なす)
 *	割り込み処理	割り込み禁止状態での呼び出し	○　ノンブロッキング (timeout=0と見なす)
 */
int arp_request(int ip_address, unsigned char* ethernet_address/*[6]*/, int timeout);

/* Gratuitous ARPを送出します。
 * [out]
 *	戻り値		実際に、Gratuitous ARPを送出した場合は、0を返します。
 *			Ethernetアドレス、または、IPアドレスが設定されていない場合は、何もせずに-1を返します。
 * [note]
 *	* ethernet_set_address()、または、VendorRequestによるEthernetアドレスの変更、および、
 *	  ip_set_config()によるIPアドレスの変更時は、自動的にGratuitous ARPを送出します。
 *	  本来、アプリケーションプログラムが明示的にGratuitous ARP送出を要求する必要は無いのですが、
 *	  以下のような理由により、本関数をアプリケーションプログラムに公開しました。
 *	* Windows XPのネットワークブリッジ機能を使った場合、実インターフェイスの初期化から、
 *	  ブリッジインターフェイスの初期化までに、ややタイムラグがあるみたいです。
 *	  その間、実インターフェイスが受信したブロードキャストフレームは、ブリッジングされません。
 *	  P/ECE USB LANインターフェイス初期化直後に、P/ECEがGratuitous ARPをブロードキャストするのですが、
 *	  このメッセージがブリッジングされず、LAN側へブロードキャストされません。
 *	  そのため、LAN側のホストがGratuitous ARPを受信せず、P/ECEのMACアドレス変化を認識できません。
 *	  ブリッジインターフェイスの初期化を待つために、Gratuitous ARPの送出を少し遅らせれば良いのですが、
 *	  タイミングが明確でないため、ARPプロトコルドライバにて調整を行うのは良くないと考えました。
 *	* 上述のように、現状ではこの問題は解決していません。
 *	  アプリケーションにて定期的にarp_gratuitous()を呼び出し、Gratuitous ARPを送出することで対処してください。
 *	    <例> pceTimerSetCallback(0, PCE_TT_PERIODIC, 10*1000, (void*)arp_gratuitous); // 初期化時
 *	         pceTimerSetCallback(0, PCE_TT_NONE,           0,                  NULL); // 終了時
 *	* arp_gratuitous()は、EthernetアドレスやIPアドレスが変化したかどうか判断せずに、送出を行います。
 *	  あまり頻繁に呼び出しすぎると、ネットワーク帯域を無駄に消費するので、10〜30秒間隔程度が良いと思います。
 * [interrupt]
 *	通常処理	割り込み許可状態での呼び出し	○
 *	通常処理	割り込み禁止状態での呼び出し	○
 *	割り込み処理	割り込み禁止状態での呼び出し	○
 */
int arp_gratuitous();

/* ARPテーブルの要素を取得します。
 * [in]
 *	table		ARPテーブルの要素を格納するバッファ。
 *	table_size	バッファの要素数。
 * [out]
 *	戻り値		実際に格納されたARPテーブルの要素数。
 * [note]
 *	* バッファには、ARPテーブルのMRU先頭要素〜table_size要素までがコピーされます。
 *	  ARPテーブルの全要素を取得するには、戻り値がtable_sizeよりも小さくなるまで、
 *	  バッファを拡張し、table_sizeを増やしながら、繰り返し、呼び出してください。
 * [interrupt]
 *	通常処理	割り込み許可状態での呼び出し	○
 *	通常処理	割り込み禁止状態での呼び出し	○
 *	割り込み処理	割り込み禁止状態での呼び出し	○
 */
int arp_table(ARPENTRY* table/*[table_size]*/, int table_size);

/****************************************************************************
 *	ICMPプロトコルドライバ
 ****************************************************************************/

/* ICMP通信状態取得用 */
typedef struct _ICMPSTAT {
	int echo_reply;		/* エコー応答メッセージ受信回数 */
	int echo_request;	/* エコー要求メッセージ受信回数 */
} ICMPSTAT;

/*--------------------------------------------------------------------------*/

/* ICMPエコー要求を送信し、ICMPエコー応答を待ちます。
 * [in]
 *	address		宛て先IPアドレス。
 *	timeout		ICMPエコー応答を待つ、最大時間[ミリ秒]。(負数ならば、既定のタイムアウト時間を使用)
 * [out]
 *	戻り値		ICMPエコー応答を受信した場合、送出〜受信までの遅延時間[ミリ秒]を返します。(0、または、正数)
 *			ICMPエコー応答を受信する前にタイムアウトした場合、負数を返します。
 * [interrupt]
 *	通常処理	割り込み許可状態での呼び出し	○
 *	通常処理	割り込み禁止状態での呼び出し	×
 *	割り込み処理	割り込み禁止状態での呼び出し	×
 */
int icmp_echo_request(int address, int timeout);

/* ICMP通信状態を取得します。
 * [in]
 *	stat		結果を格納するICMPSTAT構造体へのポインタ。
 * [interrupt]
 *	通常処理	割り込み許可状態での呼び出し	○
 *	通常処理	割り込み禁止状態での呼び出し	○
 *	割り込み処理	割り込み禁止状態での呼び出し	○
 */
void icmp_stat(ICMPSTAT* stat);

#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus

#endif /*__CLIP_IP_H__*/
