/*	
 *	main.c
 *
 *	PceEth - P/ECE NDIS-WDM ミニポートドライバ
 *	Copyright (C) 2005 Naoyuki Sawa
 *
 *	* Tue Aug 16 06:19:00 JST 2005 Naoyuki Sawa
 *	- 1st リリース。
 */
#include "app.h"

/*****************************************************************************
 *	
 *****************************************************************************/

NDIS_HANDLE ndis_wrapper_handle;

typedef struct _INFORMATION {
	NDIS_OID oid;
	  int    bytes;
	__int64  value;
} INFORMATION;

static const INFORMATION information_table[] = {
/* General Operational Characteristics */
{ OID_GEN_SUPPORTED_LIST,         0, 0/*実行時処理*/                       },
{ OID_GEN_HARDWARE_STATUS,        4, NdisHardwareStatusReady               },
{ OID_GEN_MEDIA_SUPPORTED,        4, NdisMedium802_3                       },
{ OID_GEN_MEDIA_IN_USE,           4, NdisMedium802_3                       },
{ OID_GEN_MAXIMUM_LOOKAHEAD,      4, 1500                                  },
{ OID_GEN_MAXIMUM_FRAME_SIZE,     4, 1500                                  },
{ OID_GEN_LINK_SPEED,             4, 100000                                },
{ OID_GEN_TRANSMIT_BUFFER_SPACE,  4, 1514                                  },
{ OID_GEN_RECEIVE_BUFFER_SPACE,   4, 1514                                  },
{ OID_GEN_TRANSMIT_BLOCK_SIZE,    4, 1514                                  },
{ OID_GEN_RECEIVE_BLOCK_SIZE,     4, 1514                                  },
{ OID_GEN_VENDOR_ID,              4, 0x00ffffff                            },
{ OID_GEN_VENDOR_DESCRIPTION,     0, 0/*実行時処理*/                       },
{ OID_GEN_VENDOR_DRIVER_VERSION,  4, 0x00010000                            },
{ OID_GEN_CURRENT_PACKET_FILTER,  4, 0/*実行時処理*/                       },
{ OID_GEN_CURRENT_LOOKAHEAD,      4, 1500                                  },
{ OID_GEN_DRIVER_VERSION,         2, NDIS_MINIPORT_MAJOR_VERSION << 8 |
                                     NDIS_MINIPORT_MINOR_VERSION           },
{ OID_GEN_MAXIMUM_TOTAL_SIZE,     4, 1514                                  },
{ OID_GEN_PROTOCOL_OPTIONS,       4, 0/*セットのみ*/                       },
{ OID_GEN_MAC_OPTIONS,            4, NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA |
                                     NDIS_MAC_OPTION_TRANSFERS_NOT_PEND  |
                                     NDIS_MAC_OPTION_NO_LOOPBACK           },
{ OID_GEN_MEDIA_CONNECT_STATUS,   4, NdisMediaStateConnected               },
{ OID_GEN_MAXIMUM_SEND_PACKETS,   4, 1/*仮*/                               },
/* General Statistics */
{ OID_GEN_XMIT_OK,                4, 0/*実行時処理*/                       },
{ OID_GEN_RCV_OK,                 4, 0/*実行時処理*/                       },
{ OID_GEN_XMIT_ERROR,             4, 0/*実行時処理*/                       },
{ OID_GEN_RCV_ERROR,              4, 0/*実行時処理*/                       },
{ OID_GEN_RCV_NO_BUFFER,          4, 0                                     },
/* Ethernet Operational Characteristics */
{ OID_802_3_PERMANENT_ADDRESS,    6, 0/*実行時処理*/                       },
{ OID_802_3_CURRENT_ADDRESS,      6, 0/*実行時処理*/                       },
{ OID_802_3_MULTICAST_LIST,       0, 0/*マルチキャスト未対応*/             },
{ OID_802_3_MAXIMUM_LIST_SIZE,    4, 0                                     },
/* Ethernet Statistics */
{ OID_802_3_RCV_ERROR_ALIGNMENT,  4, 0                                     },
{ OID_802_3_XMIT_ONE_COLLISION,   4, 0                                     },
{ OID_802_3_XMIT_MORE_COLLISIONS, 4, 0                                     },
};
#define information_count (sizeof information_table / sizeof *information_table)

static const char vendor_description[] = "Piece Lab.";

/*****************************************************************************
 *	
 *****************************************************************************/

NTSTATUS
DriverEntry(DRIVER_OBJECT* driver_object, UNICODE_STRING* registry_path)
{
	NDIS_STATUS ndis_status;
	NDIS_MINIPORT_CHARACTERISTICS miniport_characteristics;

	KdPrint(("DriverEntry called\n"));

	/* ラッパーを初期化します。 */
	NdisMInitializeWrapper(
		&ndis_wrapper_handle,	/* NdisWrapperHandle */
		driver_object,		/* SystemSpecific1 */
		registry_path,		/* SystemSpecific2 */
		NULL);			/* SystemSpecific3 */
	if(!ndis_wrapper_handle) {
		KdPrint(("NdisMInitializeWrapper failed\n"));
		goto L_ERR;
	}

	/* ドライバを登録します。 */
	memset(&miniport_characteristics, 0, sizeof(NDIS_MINIPORT_CHARACTERISTICS));
	miniport_characteristics.MajorNdisVersion	 = NDIS_MINIPORT_MAJOR_VERSION;
	miniport_characteristics.MinorNdisVersion	 = NDIS_MINIPORT_MINOR_VERSION;
	miniport_characteristics.InitializeHandler	 = miniport_initialize;
	miniport_characteristics.ResetHandler		 = miniport_reset;
	miniport_characteristics.HaltHandler		 = miniport_halt;
	miniport_characteristics.QueryInformationHandler = miniport_query_information;
	miniport_characteristics.SetInformationHandler	 = miniport_set_information;
	miniport_characteristics.SendPacketsHandler	 = miniport_send_packets;
	miniport_characteristics.ReturnPacketHandler	 = miniport_return_packet;
	ndis_status = NdisMRegisterMiniport(
		ndis_wrapper_handle,			/* NdisWrapperHandle */
		&miniport_characteristics,		/* MiniportCharacteristics */
		sizeof(NDIS_MINIPORT_CHARACTERISTICS));	/* CharacteristicsLength */
	if(ndis_status != NDIS_STATUS_SUCCESS) {
		KdPrint(("NdisMRegisterMiniport failed\n"));
		goto L_ERR;
	}

	return STATUS_SUCCESS;

L_ERR:	/*-------------------------------------------------------------------*/

	if(ndis_wrapper_handle) {
		NdisTerminateWrapper(
			ndis_wrapper_handle,	/* NdisWrapperHandle */
			NULL);			/* SystemSpecific */
		ndis_wrapper_handle = NULL;
	}

	return STATUS_UNSUCCESSFUL;
}

/*****************************************************************************
 *	
 *****************************************************************************/

NDIS_STATUS
miniport_initialize(
	NDIS_STATUS* open_error_status,
	UINT* selected_medium_index,
	NDIS_MEDIUM* medium_array,
	UINT medium_array_size,
	NDIS_HANDLE miniport_adapter_handle,
	NDIS_HANDLE wrapper_configuration_context)
{
	MINIPORT_ADAPTER* miniport_adapter = NULL;
	//
	int i;

	KdPrint(("miniport_initialize called\n"));

	/* 対応メディア(IEEE802.3=Ethernet)を探します。 */
	for(i = 0; i < (int)medium_array_size; i++) {
		if(medium_array[i] == NdisMedium802_3) {
			break;
		}
	}
	if(i == medium_array_size) {
		KdPrint(("unsupported media"));
		goto L_ERR;
	}
	*selected_medium_index = i;

	/* ミニポートアダプタを作成します。 */
	miniport_adapter = create_miniport_adapter(
		miniport_adapter_handle,
		wrapper_configuration_context);
	if(!miniport_adapter) {
		KdPrint(("create_miniport_adapter failed\n"));
		goto L_ERR;
	}

	/* ミニポートアダプタを登録します。 */
	NdisMSetAttributesEx(
		miniport_adapter_handle,		/* MiniportAdapterHandle */
		miniport_adapter,			/* MiniportAdapterContext */
		0,					/* CheckForHangTimeInSeconds */
		NDIS_ATTRIBUTE_DESERIALIZE |		/* AttributeFlags */
		NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK,
		NdisInterfaceInternal);			/* AdapterType */

	return NDIS_STATUS_SUCCESS;

L_ERR:	/*-------------------------------------------------------------------*/

	if(miniport_adapter) {
		delete_miniport_adapter(miniport_adapter);
		miniport_adapter = NULL;
	}

	return NDIS_STATUS_FAILURE;
}

/*****************************************************************************
 *	
 *****************************************************************************/

NDIS_STATUS
miniport_reset(
	BOOLEAN* addressing_reset,
	NDIS_HANDLE miniport_adapter_context)
{
	MINIPORT_ADAPTER* miniport_adapter = miniport_adapter_context;

	KdPrint(("miniport_reset called\n"));

	/* ソフトウェアリセット機能を持っていないことを返答します。
	 * もしも動作に問題がある場合は、きちんと実装してください。
	 */
	return NDIS_STATUS_NOT_RESETTABLE;
}

/*****************************************************************************
 *	
 *****************************************************************************/

void
miniport_halt(
	NDIS_HANDLE miniport_adapter_context)
{
	MINIPORT_ADAPTER* miniport_adapter = miniport_adapter_context;

	KdPrint(("miniport_halt called\n"));

	/* ミニポートアダプタを削除します。 */
	delete_miniport_adapter(miniport_adapter);
	miniport_adapter = NULL;
}

/*****************************************************************************
 *	
 *****************************************************************************/

NDIS_STATUS
miniport_query_information(
	NDIS_HANDLE miniport_adapter_context,
	NDIS_OID oid,
	void* information_buffer,
	ULONG information_buffer_length,
	ULONG* bytes_written,
	ULONG* bytes_needed)
{
	MINIPORT_ADAPTER* miniport_adapter = miniport_adapter_context;
	//
	int i;
	int bytes;
	int* list;

#ifdef VERBOSE
	KdPrint(("miniport_query_information called\n"));
	KdPrint(("%s (%d)\n", oid_name(oid), information_buffer_length));
#endif /*VERBOSE*/

	*bytes_needed  = 0;
	*bytes_written = 0;

	/* バッファサイズの確認と、固定値の格納を行います。 */
	for(i = 0; i < information_count; i++) {
		if(information_table[i].oid == oid) {
			break;
		}
	}
	if(i == information_count) {
#ifdef VERBOSE
		KdPrint(("not supported\n"));
#endif /*VERBOSE*/
		return NDIS_STATUS_NOT_SUPPORTED;
	}
	if((int)information_buffer_length < information_table[i].bytes) {
#ifdef VERBOSE
		KdPrint(("too short\n"));
#endif /*VERBOSE*/
		*bytes_needed = information_table[i].bytes;
		return NDIS_STATUS_BUFFER_TOO_SHORT;
	}
	memcpy(information_buffer, &information_table[i].value, information_table[i].bytes);
	*bytes_written = information_table[i].bytes;

	/* 可変値、配列、文字列などの実行時処理を行います。 */
	switch(oid) {
	case OID_GEN_SUPPORTED_LIST:
		bytes = 4 * information_count;
		if((int)information_buffer_length < bytes) {
#ifdef VERBOSE
			KdPrint(("too short\n"));
#endif /*VERBOSE*/
			*bytes_needed  = bytes;
			*bytes_written = 0;
			return NDIS_STATUS_BUFFER_TOO_SHORT;
		}
		list = information_buffer;
		for(i = 0; i < information_count; i++) {
			list[i] = information_table[i].oid;
		}
		*bytes_written = bytes;
		break;
	case OID_GEN_VENDOR_DESCRIPTION:
		bytes = sizeof vendor_description; /* nulも含む */
		if((int)information_buffer_length < bytes) {
#ifdef VERBOSE
			KdPrint(("too short\n"));
#endif /*VERBOSE*/
			*bytes_needed  = bytes;
			*bytes_written = 0;
			return NDIS_STATUS_BUFFER_TOO_SHORT;
		}
		memcpy(information_buffer, vendor_description, bytes);
		*bytes_written = bytes;
		break;
	case OID_GEN_CURRENT_PACKET_FILTER:
		ASSERT(*bytes_written == 4);
		*(int*)information_buffer = miniport_adapter->packet_filter;
		break;
	case OID_GEN_XMIT_OK:
		ASSERT(*bytes_written == 4);
		*(int*)information_buffer = miniport_adapter->xmit_ok;
		break;
	case OID_GEN_RCV_OK:
		ASSERT(*bytes_written == 4);
		*(int*)information_buffer = miniport_adapter->rcv_ok;
		break;
	case OID_GEN_XMIT_ERROR:
		ASSERT(*bytes_written == 4);
		*(int*)information_buffer = miniport_adapter->xmit_error;
		break;
	case OID_GEN_RCV_ERROR:
		ASSERT(*bytes_written == 4);
		*(int*)information_buffer = miniport_adapter->rcv_error;
		break;
	case OID_802_3_PERMANENT_ADDRESS:
	case OID_802_3_CURRENT_ADDRESS:
		ASSERT(*bytes_written == 6);
		memcpy(information_buffer, miniport_adapter->address, 6);
		break;
	}

	return NDIS_STATUS_SUCCESS;
}

/*****************************************************************************
 *	
 *****************************************************************************/

NDIS_STATUS
miniport_set_information(
	NDIS_HANDLE miniport_adapter_context,
	NDIS_OID oid,
	void* information_buffer,
	ULONG information_buffer_length,
	ULONG* bytes_read,
	ULONG* bytes_needed)
{
	MINIPORT_ADAPTER* miniport_adapter = miniport_adapter_context;

#ifdef VERBOSE
	KdPrint(("miniport_set_information called\n"));
	KdPrint(("%s (%d)\n", oid_name(oid), information_buffer_length));
#endif /*VERBOSE*/

	/* 未対応項目は成功扱いとします。(仮) */
	*bytes_needed = 0;
	*bytes_read   = information_buffer_length;

	/* 対応項目の設定処理を行います。 */
	switch(oid) {
	case OID_GEN_CURRENT_PACKET_FILTER:
		if(information_buffer_length != 4) {
#ifdef VERBOSE
			KdPrint(("invalid length\n"));
#endif /*VERBOSE*/
			*bytes_needed = 4;
			*bytes_read   = 0;
			return NDIS_STATUS_INVALID_LENGTH;
		}
		miniport_adapter->packet_filter = *(int*)information_buffer;
//#ifdef VERBOSE
		if(miniport_adapter->packet_filter & NDIS_PACKET_TYPE_DIRECTED      ) KdPrint(("NDIS_PACKET_TYPE_DIRECTED      \n"));
		if(miniport_adapter->packet_filter & NDIS_PACKET_TYPE_MULTICAST     ) KdPrint(("NDIS_PACKET_TYPE_MULTICAST     \n"));
		if(miniport_adapter->packet_filter & NDIS_PACKET_TYPE_ALL_MULTICAST ) KdPrint(("NDIS_PACKET_TYPE_ALL_MULTICAST \n"));
		if(miniport_adapter->packet_filter & NDIS_PACKET_TYPE_BROADCAST     ) KdPrint(("NDIS_PACKET_TYPE_BROADCAST     \n"));
		if(miniport_adapter->packet_filter & NDIS_PACKET_TYPE_SOURCE_ROUTING) KdPrint(("NDIS_PACKET_TYPE_SOURCE_ROUTING\n"));
		if(miniport_adapter->packet_filter & NDIS_PACKET_TYPE_PROMISCUOUS   ) KdPrint(("NDIS_PACKET_TYPE_PROMISCUOUS   \n"));
		if(miniport_adapter->packet_filter & NDIS_PACKET_TYPE_SMT           ) KdPrint(("NDIS_PACKET_TYPE_SMT           \n"));
		if(miniport_adapter->packet_filter & NDIS_PACKET_TYPE_ALL_LOCAL     ) KdPrint(("NDIS_PACKET_TYPE_ALL_LOCAL     \n"));
		if(miniport_adapter->packet_filter & NDIS_PACKET_TYPE_GROUP         ) KdPrint(("NDIS_PACKET_TYPE_GROUP         \n"));
		if(miniport_adapter->packet_filter & NDIS_PACKET_TYPE_ALL_FUNCTIONAL) KdPrint(("NDIS_PACKET_TYPE_ALL_FUNCTIONAL\n"));
		if(miniport_adapter->packet_filter & NDIS_PACKET_TYPE_FUNCTIONAL    ) KdPrint(("NDIS_PACKET_TYPE_FUNCTIONAL    \n"));
		if(miniport_adapter->packet_filter & NDIS_PACKET_TYPE_MAC_FRAME     ) KdPrint(("NDIS_PACKET_TYPE_MAC_FRAME     \n"));
//#endif /*VERBOSE*/
		break;
	}

	return NDIS_STATUS_SUCCESS;
}

/*****************************************************************************
 *	
 *****************************************************************************/

void
miniport_send_packets(
	NDIS_HANDLE miniport_adapter_context,
	NDIS_PACKET* packet_array[],
	UINT number_of_packets)
{
	MINIPORT_ADAPTER* miniport_adapter = miniport_adapter_context;
	//
	int i;
	NDIS_PACKET* packet;

	KdPrint(("miniport_send_packets called\n"));

	/* 送信パケットを、キューへ追加します。 */
	for(i = 0; i < (int)number_of_packets; i++) {
		packet = packet_array[i];
		ExInterlockedInsertTailList(
			&miniport_adapter->tx_queue,
			(LIST_ENTRY*)packet->MiniportReserved,
			&miniport_adapter->spin_lock);
	}

	/* キューが増加したので、送信スレッドへヒントを送ります。 */
	KeSetEvent(&miniport_adapter->tx_event, IO_NO_INCREMENT, FALSE);
}

/*****************************************************************************
 *	
 *****************************************************************************/

void
miniport_return_packet(
	NDIS_HANDLE miniport_adapter_context,
	NDIS_PACKET* packet)
{
	MINIPORT_ADAPTER* miniport_adapter = miniport_adapter_context;

	KdPrint(("miniport_return_packet called\n"));

	/* 受信パケットを、プールへ戻します。 */
	rx_free_packet(miniport_adapter, packet);

	/* プールが増加したので、受信スレッドへヒントを送ります。 */
	KeSetEvent(&miniport_adapter->rx_event, IO_NO_INCREMENT, FALSE);
}
