// SoftEther VPN Source Code - Developer Edition Master Branch
// Kernel Device Driver


// NDIS6.c
// Windows NDIS 6.2 Routine

#include <GlobalConst.h>

#define	NEO_DEVICE_DRIVER

#include "Neo6.h"

static UINT64 max_speed = NEO_MAX_SPEED_DEFAULT;
static bool keep_link = false;
static UINT reg_if_type = IF_TYPE_ETHERNET_CSMACD;

BOOLEAN
PsGetVersion(
			 PULONG MajorVersion OPTIONAL,
			 PULONG MinorVersion OPTIONAL,
			 PULONG BuildNumber OPTIONAL,
			 PUNICODE_STRING CSDVersion OPTIONAL
			 );

// Memory related
static NDIS_PHYSICAL_ADDRESS HighestAcceptableMax = NDIS_PHYSICAL_ADDRESS_CONST(-1, -1);
NDIS_HANDLE ndis_miniport_driver_handle = NULL;

// Flag for whether Windows 8
bool g_is_win8 = false;

// Win32 driver entry point
NDIS_STATUS DriverEntry(DRIVER_OBJECT *DriverObject, UNICODE_STRING *RegistryPath)
{
	NDIS_MINIPORT_DRIVER_CHARACTERISTICS miniport;
	ULONG os_major_ver = 0, os_minor_ver = 0;
	NDIS_STATUS ret;

	// Initialize the Neo library
	if (NeoInit() == FALSE)
	{
		// Initialization Failed
		return STATUS_UNSUCCESSFUL;
	}

	g_is_win8 = false;

	// Get the OS version
	PsGetVersion(&os_major_ver, &os_minor_ver, NULL, NULL);

	if (os_major_ver >= 7 || (os_major_ver == 6 && os_minor_ver >= 2))
	{
		// Windows 8
		g_is_win8 = true;
	}

	// Register a NDIS miniport driver
	NeoZero(&miniport, sizeof(NDIS_MINIPORT_DRIVER_CHARACTERISTICS));

	miniport.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_DRIVER_CHARACTERISTICS;
	miniport.Header.Revision = NDIS_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2;
	miniport.Header.Size = NDIS_SIZEOF_MINIPORT_DRIVER_CHARACTERISTICS_REVISION_2;

	miniport.MajorNdisVersion = NEO_NDIS_MAJOR_VERSION;
	miniport.MinorNdisVersion = NEO_NDIS_MINOR_VERSION;

	// Register the handler
	miniport.InitializeHandlerEx = NeoNdisInitEx;
	miniport.HaltHandlerEx = NeoNdisHaltEx;
	miniport.OidRequestHandler = NeoNdisOidRequest;
	miniport.ResetHandlerEx = NeoNdisResetEx;
	miniport.CheckForHangHandlerEx = NeoNdisCheckForHangEx;
	miniport.UnloadHandler = NeoNdisDriverUnload;
	miniport.SendNetBufferListsHandler = NeoNdisSendNetBufferLists;

	miniport.SetOptionsHandler = NeoNdisSetOptions;
	miniport.PauseHandler = NeoNdisPause;
	miniport.RestartHandler = NeoNdisRestart;
	miniport.ReturnNetBufferListsHandler = NeoNdisReturnNetBufferLists;
	miniport.CancelSendHandler = NeoNdisCancelSend;
	miniport.DevicePnPEventNotifyHandler = NeoNdisDevicePnPEventNotify;
	miniport.ShutdownHandlerEx = NeoNdisShutdownEx;
	miniport.CancelOidRequestHandler = NeoNdisCancelOidRequest;

	ret = NdisMRegisterMiniportDriver(DriverObject, RegistryPath,
		NULL, &miniport, &ndis_miniport_driver_handle);

	if (NG(ret))
	{
		// Registration failure
		return STATUS_UNSUCCESSFUL;
	}

	// Initialization success
	return STATUS_SUCCESS;
}

NDIS_STATUS NeoNdisSetOptions(NDIS_HANDLE NdisDriverHandle, NDIS_HANDLE DriverContext)
{
	return NDIS_STATUS_SUCCESS;
}

NDIS_STATUS NeoNdisPause(NDIS_HANDLE MiniportAdapterContext, PNDIS_MINIPORT_PAUSE_PARAMETERS MiniportPauseParameters)
{
	UINT counter_dbg = 0;

	ctx->Paused = true;

	NeoLockPacketQueue();
	NeoUnlockPacketQueue();

	// Wait for complete all tasks
	while (ctx->NumCurrentDispatch != 0)
	{
		NdisMSleep(10000);
		counter_dbg++;
		if (counter_dbg >= 1500)
		{
			break;
		}
	}

	return NDIS_STATUS_SUCCESS;
}

NDIS_STATUS NeoNdisRestart(NDIS_HANDLE MiniportAdapterContext, PNDIS_MINIPORT_RESTART_PARAMETERS MiniportRestartParameters)
{
	ctx->Paused = false;

	return NDIS_STATUS_SUCCESS;
}

void NeoNdisReturnNetBufferLists(NDIS_HANDLE MiniportAdapterContext, PNET_BUFFER_LIST NetBufferLists, ULONG ReturnFlags)
{
}

void NeoNdisCancelSend(NDIS_HANDLE MiniportAdapterContext, PVOID CancelId)
{
	//NeoNdisCrash2(__LINE__, __LINE__, __LINE__, __LINE__);
}

void NeoNdisDevicePnPEventNotify(NDIS_HANDLE MiniportAdapterContext, PNET_DEVICE_PNP_EVENT NetDevicePnPEvent)
{
}

void NeoNdisShutdownEx(NDIS_HANDLE MiniportAdapterContext, NDIS_SHUTDOWN_ACTION ShutdownAction)
{
}

void NeoNdisCancelOidRequest(NDIS_HANDLE MiniportAdapterContext, PVOID RequestId)
{
	//NeoNdisCrash2(__LINE__, __LINE__, __LINE__, __LINE__);
}

// Initialization handler of adapter
NDIS_STATUS NeoNdisInitEx(NDIS_HANDLE MiniportAdapterHandle,
						  NDIS_HANDLE MiniportDriverContext,
						  PNDIS_MINIPORT_INIT_PARAMETERS MiniportInitParameters)
{
	NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES attr;
	NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES gen;
	NDIS_PM_CAPABILITIES pnpcap;

	if (ctx == NULL)
	{
		return NDIS_STATUS_FAILURE;
	}

	if (ctx->NdisMiniportDriverHandle == NULL)
	{
		ctx->NdisMiniportDriverHandle = ndis_miniport_driver_handle;
	}

	// Prevention of multiple start
	if (ctx->Initing != FALSE)
	{
		// Multiple started
		return NDIS_STATUS_FAILURE;
	}
	ctx->Initing = TRUE;

	// Examine whether it has already been initialized
	if (ctx->Inited != FALSE)
	{
		// Driver is started on another instance already.
		// VPN driver can start only one instance per one service.
		// User can start multiple drivers with different instance ID
		return NDIS_STATUS_FAILURE;
	}

	// Current value of the packet filter
	ctx->CurrentPacketFilter = NDIS_PACKET_TYPE_ALL_LOCAL | NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_ALL_FUNCTIONAL;

	// Initialize the adapter information
	ctx->NdisMiniport = MiniportAdapterHandle;
	ctx->NdisContext = ctx;
	ctx->HardwareStatus = NdisHardwareStatusReady;
	ctx->Halting = FALSE;
	ctx->Connected = ctx->ConnectedOld = FALSE;

	//if (keep_link == false)
	{
		ctx->ConnectedForce = TRUE;
	}

	// Read the information from the registry
	if (NeoLoadRegistry() == FALSE)
	{
		// Failure
		ctx->Initing = FALSE;
		return NDIS_STATUS_FAILURE;
	}

	// Register the device attributes
	NeoZero(&attr, sizeof(attr));
	attr.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES;
	attr.Header.Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1;
	attr.Header.Size = sizeof(NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES);
	attr.AttributeFlags = NDIS_MINIPORT_ATTRIBUTES_NO_HALT_ON_SUSPEND;
	attr.InterfaceType = NdisInterfaceInternal;
	attr.MiniportAdapterContext = ctx->NdisContext;

	NdisMSetMiniportAttributes(ctx->NdisMiniport, (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&attr);

	NeoZero(&pnpcap, sizeof(pnpcap));

	NeoZero(&gen, sizeof(gen));
	gen.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES;
	gen.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2;
	gen.Header.Size = NDIS_SIZEOF_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_2;
	gen.MediaType = NdisMedium802_3;
	gen.PhysicalMediumType = NdisPhysicalMedium802_3;
	gen.MtuSize = NEO_MAX_PACKET_SIZE_ANNOUNCE - NEO_MIN_PACKET_SIZE;
	gen.MaxXmitLinkSpeed = gen.MaxRcvLinkSpeed = max_speed;
	gen.RcvLinkSpeed = gen.XmitLinkSpeed = max_speed;
	gen.MediaConnectState = MediaConnectStateDisconnected;
	gen.LookaheadSize = NEO_MAX_PACKET_SIZE_ANNOUNCE - NEO_MIN_PACKET_SIZE;
	gen.MacOptions = NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | NDIS_MAC_OPTION_NO_LOOPBACK;
	gen.SupportedPacketFilters = NDIS_PACKET_TYPE_ALL_LOCAL | NDIS_PACKET_TYPE_BROADCAST | NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_ALL_FUNCTIONAL;
	gen.MaxMulticastListSize = NEO_MAX_MULTICASE;
	gen.MacAddressLength = NEO_MAC_ADDRESS_SIZE;
	NeoCopy(gen.PermanentMacAddress, ctx->MacAddress, NEO_MAC_ADDRESS_SIZE);
	NeoCopy(gen.CurrentMacAddress, ctx->MacAddress, NEO_MAC_ADDRESS_SIZE);
	gen.AccessType = NET_IF_ACCESS_BROADCAST;
	gen.DirectionType = NET_IF_DIRECTION_SENDRECEIVE;
	gen.ConnectionType = NET_IF_CONNECTION_DEDICATED;
	gen.IfType = reg_if_type;
	gen.IfConnectorPresent = FALSE;
	gen.SupportedStatistics =
		NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_RCV |
		NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_RCV |
		NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_RCV |
		NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV |
		NDIS_STATISTICS_FLAGS_VALID_RCV_DISCARDS |
		NDIS_STATISTICS_FLAGS_VALID_RCV_ERROR |
		NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_XMIT |
		NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_XMIT |
		NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_XMIT |
		NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT |
		NDIS_STATISTICS_FLAGS_VALID_XMIT_ERROR |
		NDIS_STATISTICS_FLAGS_VALID_XMIT_DISCARDS |
		NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_RCV |
		NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_RCV |
		NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_RCV |
		NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_XMIT |
		NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_XMIT |
		NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_XMIT;
	gen.SupportedPauseFunctions = NdisPauseFunctionsUnsupported;
	gen.AutoNegotiationFlags = NDIS_LINK_STATE_XMIT_LINK_SPEED_AUTO_NEGOTIATED |
		NDIS_LINK_STATE_RCV_LINK_SPEED_AUTO_NEGOTIATED |
		NDIS_LINK_STATE_DUPLEX_AUTO_NEGOTIATED |
		NDIS_LINK_STATE_PAUSE_FUNCTIONS_AUTO_NEGOTIATED;
	gen.SupportedOidList = SupportedOids;
	gen.SupportedOidListLength = sizeof(SupportedOids);

	NeoZero(&pnpcap, sizeof(pnpcap));
	pnpcap.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
	pnpcap.Header.Revision = NDIS_PM_CAPABILITIES_REVISION_1;
	pnpcap.Header.Size = NDIS_SIZEOF_NDIS_PM_CAPABILITIES_REVISION_1;
	pnpcap.MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
	pnpcap.MinPatternWakeUp  = NdisDeviceStateUnspecified;
	pnpcap.MinLinkChangeWakeUp = NdisDeviceStateUnspecified;
	gen.PowerManagementCapabilitiesEx = &pnpcap;

	NdisMSetMiniportAttributes(ctx->NdisMiniport, (PNDIS_MINIPORT_ADAPTER_ATTRIBUTES)&gen);

	// Initialize the received packet array
	NeoInitPacketArray();

	// Initialize the control device
	NeoInitControlDevice();

	// Start the adapter
	NeoStartAdapter();

	// Flag setting
	ctx->Initing = FALSE;
	ctx->Inited = TRUE;

	// Notify the connection state
	NeoSetConnectState(FALSE);

	return NDIS_STATUS_SUCCESS;
}

// Open the device
BOOL NeoNdisOnOpen(IRP *irp, IO_STACK_LOCATION *stack)
{
	char name[MAX_SIZE];

	if (ctx == NULL)
	{
		return FALSE;
	}

	if (ctx->Opened)
	{
		// Another client is connected already
		return FALSE;
	}
	ctx->Opened = TRUE;

	// Initialize the event name
	sprintf(name, NDIS_NEO_EVENT_NAME, ctx->HardwareID);

	// Register a Event
	ctx->Event = NeoNewEvent(name);
	if (ctx->Event == NULL)
	{
		ctx->Opened = FALSE;
		return FALSE;
	}

	// Set the connection state
	NeoSetConnectState(TRUE);

	return TRUE;
}

// Close the device
BOOL NeoNdisOnClose(IRP *irp, IO_STACK_LOCATION *stack)
{
	NEO_EVENT *free_event = NULL;
	if (ctx == NULL)
	{
		return FALSE;
	}

	if (ctx->Opened == FALSE)
	{
		// Client is not connected
		return FALSE;
	}
	ctx->Opened = FALSE;

	NeoLockPacketQueue();
	{
		// Release the event
		free_event = ctx->Event;
		ctx->Event = NULL;

		// Release all packets
		NeoClearPacketQueue(true);
	}
	NeoUnlockPacketQueue();

	if (free_event != NULL)
	{
		NeoFreeEvent(free_event);
	}

	NeoSetConnectState(FALSE);

	return TRUE;
}

// Crash 2
void NeoNdisCrash2(UINT a, UINT b, UINT c, UINT d)
{
	KeBugCheckEx(0x00000061, (ULONG_PTR)a, (ULONG_PTR)b, (ULONG_PTR)c, (ULONG_PTR)d);
}

// Crash
void NeoNdisCrash()
{
	NEO_QUEUE *q;
	q = (NEO_QUEUE *)0xACACACAC;
	q->Size = 128;
	NeoCopy(q->Buf, "ABCDEFG", 8);
}

// Dispatch table for control
NTSTATUS NeoNdisDispatch(DEVICE_OBJECT *DeviceObject, IRP *Irp)
{
	NTSTATUS status;
	IO_STACK_LOCATION *stack;
	void *buf;
	BOOL ok;
	status = STATUS_SUCCESS;

	if (ctx == NULL)
	{
		return NDIS_STATUS_FAILURE;
	}

	InterlockedIncrement(&ctx->NumCurrentDispatch);

	// Get the IRP stack
	stack = IoGetCurrentIrpStackLocation(Irp);

	// Initialize the number of bytes
	Irp->IoStatus.Information = 0;
	Irp->IoStatus.Status = STATUS_SUCCESS;

	buf = Irp->UserBuffer;

	if (ctx->Halting != FALSE)
	{
		// Device driver is terminating
		Irp->IoStatus.Information = STATUS_UNSUCCESSFUL;
		InterlockedDecrement(&ctx->NumCurrentDispatch);

		IoCompleteRequest(Irp, IO_NO_INCREMENT);

		return STATUS_SUCCESS;
	}

	// Branch to each operation
	switch (stack->MajorFunction)
	{
	case IRP_MJ_CREATE:
		// Device is opened
		if (NeoNdisOnOpen(Irp, stack) == FALSE)
		{
			Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
			status = STATUS_UNSUCCESSFUL;
		}
		break;

	case IRP_MJ_CLOSE:
		// Device is closed
		if (NeoNdisOnClose(Irp, stack) == FALSE)
		{
			Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
			status = STATUS_UNSUCCESSFUL;
		}
		break;

	case IRP_MJ_READ:
		// Read (Reading of the received packet)
		ok = false;
		if (buf != NULL)
		{
			if (ctx->Opened && ctx->Inited)
			{
				if (stack->Parameters.Read.Length == NEO_EXCHANGE_BUFFER_SIZE)
				{
					// Address check
					bool check_ok = true;
					__try
					{
						ProbeForWrite(buf, NEO_EXCHANGE_BUFFER_SIZE, 1);
					}
					__except (EXCEPTION_EXECUTE_HANDLER)
					{
						check_ok = false;
					}

					if (check_ok)
					{
						// Address check
						MDL *mdl = IoAllocateMdl(buf, NEO_EXCHANGE_BUFFER_SIZE, false, false, NULL);

						if (mdl != NULL)
						{
							MmProbeAndLockPages(mdl, KernelMode, IoWriteAccess);
						}

						// Read
						NeoRead(buf);
						Irp->IoStatus.Information = NEO_EXCHANGE_BUFFER_SIZE;
						ok = true;

						if (mdl != NULL)
						{
							MmUnlockPages(mdl);
							IoFreeMdl(mdl);
						}
					}
				}
			}
		}
		if (ok == FALSE)
		{
			// An error occurred
			Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
			status = STATUS_UNSUCCESSFUL;
		}
		break;

	case IRP_MJ_WRITE:
		// Write (Writing of a transmission packet)
		ok = false;
		if (buf != NULL)
		{
			if (ctx->Opened && ctx->Inited)
			{
				if (stack->Parameters.Write.Length == NEO_EXCHANGE_BUFFER_SIZE)
				{
					// Address check
					bool check_ok = true;
					__try
					{
						ProbeForRead(buf, NEO_EXCHANGE_BUFFER_SIZE, 1);
					}
					__except (EXCEPTION_EXECUTE_HANDLER)
					{
						check_ok = false;
					}

					if (check_ok)
					{
						// Address check
						MDL *mdl = IoAllocateMdl(buf, NEO_EXCHANGE_BUFFER_SIZE, false, false, NULL);

						if (mdl != NULL)
						{
							MmProbeAndLockPages(mdl, KernelMode, IoReadAccess);
						}
						__try
						{
							ProbeForRead(buf, NEO_EXCHANGE_BUFFER_SIZE, 1);	
						}
						__except (EXCEPTION_EXECUTE_HANDLER)
						{
							check_ok = false;	
						}
						if (check_ok) {
							// Write
							NeoWrite(buf);
							Irp->IoStatus.Information = stack->Parameters.Write.Length;
							ok = true;

							if (mdl != NULL)
							{
							MmUnlockPages(mdl);
							IoFreeMdl(mdl);
							}
						}
					}
				}
			}
		}
		if (ok == FALSE)
		{
			// An error occurred
			Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
			status = STATUS_UNSUCCESSFUL;
		}
		break;
	}

	InterlockedDecrement(&ctx->NumCurrentDispatch);

	IoCompleteRequest(Irp, IO_NO_INCREMENT);

	return STATUS_SUCCESS;
}

// Initialize the control device
void NeoInitControlDevice()
{
	char name_kernel[MAX_SIZE];
	char name_win32[MAX_SIZE];
	UNICODE *unicode_kernel, *unicode_win32;
	DEVICE_OBJECT *control_device_object;
	NDIS_HANDLE ndis_control_handle;
	NDIS_DEVICE_OBJECT_ATTRIBUTES t;

	if (ctx == NULL)
	{
		return;
	}

	// Initialize the dispatch table
	NeoZero(ctx->DispatchTable, sizeof(PDRIVER_DISPATCH) * IRP_MJ_MAXIMUM_FUNCTION);

	// Register the handler
	ctx->DispatchTable[IRP_MJ_CREATE] =
		ctx->DispatchTable[IRP_MJ_CLOSE] =
		ctx->DispatchTable[IRP_MJ_READ] =
		ctx->DispatchTable[IRP_MJ_WRITE] =
		ctx->DispatchTable[IRP_MJ_DEVICE_CONTROL] = NeoNdisDispatch;
	ctx->Opened = FALSE;
	ctx->Paused = FALSE;

	// Generate the device name
	sprintf(name_kernel, NDIS_NEO_DEVICE_NAME, ctx->HardwareID);
	unicode_kernel = NewUnicode(name_kernel);
	sprintf(name_win32, NDIS_NEO_DEVICE_NAME_WIN32, ctx->HardwareID);
	unicode_win32 = NewUnicode(name_win32);

	// Register the device
	NeoZero(&t, sizeof(t));
	t.Header.Type = NDIS_OBJECT_TYPE_DEVICE_OBJECT_ATTRIBUTES;
	t.Header.Revision = NDIS_DEVICE_OBJECT_ATTRIBUTES_REVISION_1;
	t.Header.Size = NDIS_SIZEOF_DEVICE_OBJECT_ATTRIBUTES_REVISION_1;
	t.DeviceName = GetUnicode(unicode_kernel);
	t.SymbolicName = GetUnicode(unicode_win32);
	t.MajorFunctions = ctx->DispatchTable;

	NdisRegisterDeviceEx(ndis_miniport_driver_handle, &t,
		&control_device_object,
		&ndis_control_handle);

	ctx->NdisControlDevice = control_device_object;
	ctx->NdisControl = ndis_control_handle;

	// Initialize the display name
	if (strlen(ctx->HardwareID) > 11)
	{
		sprintf(ctx->HardwarePrintableID, NDIS_NEO_HARDWARE_ID, ctx->HardwareID_Raw + 11);
	}
	else
	{
		sprintf(ctx->HardwarePrintableID, NDIS_NEO_HARDWARE_ID, ctx->HardwareID_Raw);
	}
}

// Release the control device
void NeoFreeControlDevice()
{
	if (ctx == NULL)
	{
		return;
	}

	if (ctx->Opened != FALSE)
	{
		// Delete the event
		NeoSet(ctx->Event);
		NeoFreeEvent(ctx->Event);
		ctx->Event = NULL;
		ctx->Opened = FALSE;
	}

	// Delete the device
	NdisDeregisterDeviceEx(ctx->NdisControl);
}


// Read the information from the registry
BOOL NeoLoadRegistry()
{
	void *buf;
	NDIS_STATUS ret;
	UINT size;
	NDIS_HANDLE config;
	NDIS_CONFIGURATION_PARAMETER *param;
	UNICODE *name;
	ANSI_STRING ansi;
	UNICODE_STRING *unicode;
	UINT64 speed;
	BOOL keep;
	NDIS_CONFIGURATION_OBJECT config_obj;

	// Get the Config handle
	NeoZero(&config_obj, sizeof(config_obj));
	config_obj.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT;
	config_obj.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1;
	config_obj.Header.Size = NDIS_SIZEOF_CONFIGURATION_OBJECT_REVISION_1;
	config_obj.NdisHandle = ctx->NdisMiniport;

	ret = NdisOpenConfigurationEx(&config_obj, &config);
	if (NG(ret))
	{
		// Failure
		return FALSE;
	}

	// Read the MAC address
	NdisReadNetworkAddress(&ret, &buf, &size, config);
	if (NG(ret))
	{
		// Failure
		NdisCloseConfiguration(config);
		return FALSE;
	}

	// Copy the MAC address
	if (size != NEO_MAC_ADDRESS_SIZE)
	{
		// Invalid size
		NdisCloseConfiguration(config);
		return FALSE;
	}
	NeoCopy(ctx->MacAddress, buf, NEO_MAC_ADDRESS_SIZE);

	if (ctx->MacAddress[0] == 0x00 &&
		ctx->MacAddress[1] == 0x00 &&
		ctx->MacAddress[2] == 0x01 &&
		ctx->MacAddress[3] == 0x00 &&
		ctx->MacAddress[4] == 0x00 &&
		ctx->MacAddress[5] == 0x01)
	{
		// Special MAC address
		UINT ptr32 = (UINT)((UINT64)ctx);
		LARGE_INTEGER current_time;
		UCHAR *current_time_bytes;

		KeQuerySystemTime(&current_time);

		current_time_bytes = (UCHAR *)&current_time;

		ctx->MacAddress[0] = 0x00;
		ctx->MacAddress[1] = 0xAD;
		ctx->MacAddress[2] = ((UCHAR *)(&ptr32))[0];
		ctx->MacAddress[3] = ((UCHAR *)(&ptr32))[1];
		ctx->MacAddress[4] = ((UCHAR *)(&ptr32))[2];
		ctx->MacAddress[5] = ((UCHAR *)(&ptr32))[3];

		ctx->MacAddress[2] ^= current_time_bytes[0];
		ctx->MacAddress[3] ^= current_time_bytes[1];
		ctx->MacAddress[4] ^= current_time_bytes[2];
		ctx->MacAddress[5] ^= current_time_bytes[3];

		ctx->MacAddress[2] ^= current_time_bytes[4];
		ctx->MacAddress[3] ^= current_time_bytes[5];
		ctx->MacAddress[4] ^= current_time_bytes[6];
		ctx->MacAddress[5] ^= current_time_bytes[7];
	}

	// Initialize the key name of the device name
	name = NewUnicode("MatchingDeviceId");

	// Read the hardware ID
	NdisReadConfiguration(&ret, &param, config, GetUnicode(name), NdisParameterString);
	FreeUnicode(name);
	if (NG(ret))
	{
		// Failure
		NdisCloseConfiguration(config);
		return FALSE;
	}
	// Type checking
	if (param->ParameterType != NdisParameterString)
	{
		// Failure
		NdisCloseConfiguration(config);
		return FALSE;
	}
	unicode = &param->ParameterData.StringData;

	// Prepare a buffer for ANSI string
	NeoZero(&ansi, sizeof(ANSI_STRING));
	ansi.MaximumLength = MAX_SIZE - 1;
	ansi.Buffer = NeoZeroMalloc(MAX_SIZE);

	// Convert to ANSI string
	NdisUnicodeStringToAnsiString(&ansi, unicode);
	// Copy
	strcpy(ctx->HardwareID, ansi.Buffer);
	strcpy(ctx->HardwareID_Raw, ctx->HardwareID);
	// Convert to upper case
	_strupr(ctx->HardwareID);
	// Release the memory
	NeoFree(ansi.Buffer);

	// Read the bit rate
	name = NewUnicode("MaxSpeed");
	NdisReadConfiguration(&ret, &param, config, GetUnicode(name), NdisParameterInteger);
	FreeUnicode(name);

	if (NG(ret) || param->ParameterType != NdisParameterInteger)
	{
		speed = NEO_MAX_SPEED_DEFAULT;
	}
	else
	{
		speed = (UINT64)param->ParameterData.IntegerData * 1000000ULL;
	}

	max_speed = speed;

	// Read the link keeping flag
	name = NewUnicode("KeepLink");
	NdisReadConfiguration(&ret, &param, config, GetUnicode(name), NdisParameterInteger);
	FreeUnicode(name);

	if (NG(ret) || param->ParameterType != NdisParameterInteger)
	{
		keep = false;
	}
	else
	{
		keep = (param->ParameterData.IntegerData == 0 ? false : true);
	}

	keep_link = keep;

	// Read the *IfType value
	name = NewUnicode("*IfType");
	NdisReadConfiguration(&ret, &param, config, GetUnicode(name), NdisParameterInteger);
	FreeUnicode(name);

	if (NG(ret) || param->ParameterType != NdisParameterInteger)
	{
		reg_if_type = IF_TYPE_ETHERNET_CSMACD;
	}
	else
	{
		reg_if_type = param->ParameterData.IntegerData;
	}

	// Close the config handle
	NdisCloseConfiguration(config);

	return TRUE;
}

// Unload the driver
VOID NeoNdisDriverUnload(PDRIVER_OBJECT DriverObject)
{
	NdisMDeregisterMiniportDriver(ndis_miniport_driver_handle);
}

// Stop handler of adapter
void NeoNdisHaltEx(NDIS_HANDLE MiniportAdapterContext, NDIS_HALT_ACTION HaltAction)
{
	NEO_EVENT *free_event = NULL;
	UINT counter_dbg = 0;
	if (ctx == NULL)
	{
		return;
	}

	if (ctx->Halting != FALSE)
	{
		// That has already been stopped
		return;
	}
	ctx->Halting = TRUE;

	ctx->Opened = FALSE;

	NeoLockPacketQueue();
	{
		// Release the event
		free_event = ctx->Event;
		ctx->Event = NULL;

		// Release all packets
		NeoClearPacketQueue(true);
	}
	NeoUnlockPacketQueue();

	if (free_event != NULL)
	{
		NeoSet(free_event);
	}

	// Wait for complete all tasks
	while (ctx->NumCurrentDispatch != 0)
	{
		NdisMSleep(10000);
		counter_dbg++;
		if (counter_dbg >= 1500)
		{
			break;
		}
	}

	if (free_event != NULL)
	{
		NeoFreeEvent(free_event);
	}

	// Delete the control device
	NeoFreeControlDevice();

	// Stop the adapter
	NeoStopAdapter();

	// Release the packet array
	NeoFreePacketArray();

	// Complete to stop
	ctx->Initing = ctx->Inited = FALSE;
	ctx->Connected = ctx->ConnectedForce = ctx->ConnectedOld = FALSE;
	ctx->Halting = FALSE;

	// Shutdown of Neo
	NeoShutdown();
}

// Reset handler of adapter
NDIS_STATUS NeoNdisResetEx(NDIS_HANDLE MiniportAdapterContext, PBOOLEAN AddressingReset)
{
	return NDIS_STATUS_SUCCESS;
}

// Hang-up check handler of adapter
BOOLEAN NeoNdisCheckForHangEx(NDIS_HANDLE MiniportAdapterContext)
{
	return FALSE;
}

// OID request handler
NDIS_STATUS NeoNdisOidRequest(NDIS_HANDLE MiniportAdapterContext,
							  PNDIS_OID_REQUEST OidRequest)
{
	NDIS_STATUS ret = STATUS_UNSUCCESSFUL;
	ULONG dummy = 0;

	switch (OidRequest->RequestType)
	{
	case NdisRequestQueryInformation:
	case NdisRequestQueryStatistics:
		ret = NeoNdisQuery(MiniportAdapterContext,
			OidRequest->DATA.QUERY_INFORMATION.Oid,
			OidRequest->DATA.QUERY_INFORMATION.InformationBuffer,
			OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
			&OidRequest->DATA.QUERY_INFORMATION.BytesWritten,
			&OidRequest->DATA.QUERY_INFORMATION.BytesNeeded);
		break;

	case NdisRequestSetInformation:
		ret = NeoNdisSet(MiniportAdapterContext,
			OidRequest->DATA.SET_INFORMATION.Oid,
			OidRequest->DATA.SET_INFORMATION.InformationBuffer,
			OidRequest->DATA.SET_INFORMATION.InformationBufferLength,
			&dummy,
			&OidRequest->DATA.SET_INFORMATION.BytesNeeded);
		break;

	default:
		ret = NDIS_STATUS_NOT_SUPPORTED;
		break;
	}

	return ret;
}


// Information acquisition handler of adapter
NDIS_STATUS NeoNdisQuery(NDIS_HANDLE MiniportAdapterContext,
					NDIS_OID Oid,
					void *InformationBuffer,
					ULONG InformationBufferLength,
					ULONG *BytesWritten,
					ULONG *BytesNeeded)
{
	NDIS_MEDIUM media;
	void *buf;
	UINT value32;
	USHORT value16;
	UINT size;
	NDIS_STATISTICS_INFO stat;
	NDIS_INTERRUPT_MODERATION_PARAMETERS intp;

	if (ctx == NULL)
	{
		return NDIS_STATUS_FAILURE;
	}

	// Initialization
	size = sizeof(UINT);
	value32 = value16 = 0;
	buf = &value32;

	// Branch processing
	switch (Oid)
	{
	case OID_GEN_SUPPORTED_LIST:
		// Return a list of supported OID
		buf = SupportedOids;
		size = sizeof(SupportedOids);
		break;

	case OID_GEN_MAC_OPTIONS:
		// Ethernet option
		value32 = NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | NDIS_MAC_OPTION_RECEIVE_SERIALIZED |
			NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | NDIS_MAC_OPTION_NO_LOOPBACK;
		break;

	case OID_GEN_HARDWARE_STATUS:
		// Hardware state
		buf = &ctx->HardwareStatus;
		size = sizeof(NDIS_HARDWARE_STATUS);
		break;

	case OID_GEN_MEDIA_SUPPORTED:
	case OID_GEN_MEDIA_IN_USE:
		// Type of media
		media = NdisMedium802_3;
		buf = &media;
		size = sizeof(NDIS_MEDIUM);
		break;

	case OID_GEN_CURRENT_LOOKAHEAD:
	case OID_GEN_MAXIMUM_LOOKAHEAD:
		// Read-ahead available size
		value32 = NEO_MAX_PACKET_SIZE_ANNOUNCE - NEO_MIN_PACKET_SIZE;
		break;

	case OID_GEN_MAXIMUM_FRAME_SIZE:
		// Maximum frame size
		value32 = NEO_MAX_PACKET_SIZE_ANNOUNCE - NEO_MIN_PACKET_SIZE;
		break;

	case OID_GEN_MAXIMUM_TOTAL_SIZE:
	case OID_GEN_TRANSMIT_BLOCK_SIZE:
	case OID_GEN_RECEIVE_BLOCK_SIZE:
		// Maximum packet size
		value32 = NEO_MAX_PACKET_SIZE_ANNOUNCE;
		break;

	case OID_GEN_TRANSMIT_BUFFER_SPACE:
	case OID_GEN_RECEIVE_BUFFER_SPACE:
		// Buffer size
		value32 = NEO_MAX_PACKET_SIZE_ANNOUNCE * NEO_MAX_PACKET_EXCHANGE;
		break;

	case OID_GEN_LINK_SPEED:
		// Communication speed
		value32 = (UINT)(max_speed / 100);
		break;

	case OID_GEN_VENDOR_ID:
		// Vendor ID
		NeoCopy(&value32, ctx->MacAddress, 3);
		value32 &= 0xFFFFFF00;
		value32 |= 0x01;
		break;

	case OID_GEN_VENDOR_DESCRIPTION:
		// Hardware ID
		buf = ctx->HardwarePrintableID;
		size = (UINT)strlen(ctx->HardwarePrintableID) + 1;
		break;

	case OID_GEN_DRIVER_VERSION:
		// Driver version
		value16 = ((USHORT)NEO_NDIS_MAJOR_VERSION << 8) | NEO_NDIS_MINOR_VERSION;
		buf = &value16;
		size = sizeof(USHORT);
		break;

	case OID_GEN_VENDOR_DRIVER_VERSION:
		// Vendor driver version
		value16 = ((USHORT)NEO_NDIS_MAJOR_VERSION << 8) | NEO_NDIS_MINOR_VERSION;
		buf = &value16;
		size = sizeof(USHORT);
		break;

	case OID_802_3_PERMANENT_ADDRESS:
	case OID_802_3_CURRENT_ADDRESS:
		// MAC address
		buf = ctx->MacAddress;
		size = NEO_MAC_ADDRESS_SIZE;
		break;

	case OID_802_3_MAXIMUM_LIST_SIZE:
		// Number of multicast
		value32 = NEO_MAX_MULTICASE;
		break;

	case OID_GEN_MAXIMUM_SEND_PACKETS:
		// Number of packets that can be sent at a time
		value32 = NEO_MAX_PACKET_EXCHANGE;
		break;

	case OID_GEN_XMIT_OK:
		// Number of packets sent
		value32 = ctx->Status.NumPacketSend;
		break;

	case OID_GEN_RCV_OK:
		// Number of received packets
		value32 = ctx->Status.NumPacketRecv;
		break;

	case OID_GEN_XMIT_ERROR:
		// Number of transmission error packets
		value32 = ctx->Status.NumPacketSendError;
		break;

	case OID_GEN_RCV_ERROR:
		// Number of error packets received
		value32 = ctx->Status.NumPacketRecvError;
		break;

	case OID_GEN_RCV_NO_BUFFER:
		// Number of reception buffer shortage occurrences
		value32 = ctx->Status.NumPacketRecvNoBuffer;
		break;

	case OID_802_3_RCV_ERROR_ALIGNMENT:
		// Number of errors
		value32 = 0;
		break;

	case OID_GEN_MEDIA_CONNECT_STATUS:
		// Cable connection state
		NeoCheckConnectState();
		if (keep_link == false)
		{
			value32 = ctx->Connected ? NdisMediaStateConnected : NdisMediaStateDisconnected;
		}
		else
		{
			value32 = NdisMediaStateConnected;
		}
		break;

	case OID_802_3_XMIT_ONE_COLLISION:
	case OID_802_3_XMIT_MORE_COLLISIONS:
		// Number of collisions
		value32 = 0;
		break;

	case OID_GEN_CURRENT_PACKET_FILTER:
		// Current settings of the packet filter
		value32 = ctx->CurrentPacketFilter;
		break;

/*	case OID_GEN_PROTOCOL_OPTIONS:
		// Current value of the protocol option
		value32 = ctx->CurrentProtocolOptions;
		break;*/

	case OID_GEN_STATISTICS:
		// Statistics (NDIS 6.0)
		NeoZero(&stat, sizeof(stat));
		buf = &stat;
		size = sizeof(stat);

		stat.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
		stat.Header.Revision = NDIS_STATISTICS_INFO_REVISION_1;
		stat.Header.Size = NDIS_SIZEOF_STATISTICS_INFO_REVISION_1;
		stat.SupportedStatistics =
			NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_RCV |
			NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_RCV |
			NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_RCV |
			NDIS_STATISTICS_FLAGS_VALID_BYTES_RCV |
			NDIS_STATISTICS_FLAGS_VALID_RCV_DISCARDS |
			NDIS_STATISTICS_FLAGS_VALID_RCV_ERROR |
			NDIS_STATISTICS_FLAGS_VALID_DIRECTED_FRAMES_XMIT |
			NDIS_STATISTICS_FLAGS_VALID_MULTICAST_FRAMES_XMIT |
			NDIS_STATISTICS_FLAGS_VALID_BROADCAST_FRAMES_XMIT |
			NDIS_STATISTICS_FLAGS_VALID_BYTES_XMIT |
			NDIS_STATISTICS_FLAGS_VALID_XMIT_ERROR |
			NDIS_STATISTICS_FLAGS_VALID_XMIT_DISCARDS |
			NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_RCV |
			NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_RCV |
			NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_RCV |
			NDIS_STATISTICS_FLAGS_VALID_DIRECTED_BYTES_XMIT |
			NDIS_STATISTICS_FLAGS_VALID_MULTICAST_BYTES_XMIT |
			NDIS_STATISTICS_FLAGS_VALID_BROADCAST_BYTES_XMIT;

		stat.ifInErrors = ctx->Status.Int64NumRecvError;
		stat.ifHCInOctets = ctx->Status.Int64BytesRecvTotal;
		stat.ifHCInUcastPkts = ctx->Status.Int64NumRecvUnicast;
		stat.ifHCInBroadcastPkts = ctx->Status.Int64NumRecvBroadcast;
		stat.ifHCOutOctets = ctx->Status.Int64BytesSendTotal;
		stat.ifHCOutUcastPkts = ctx->Status.Int64NumSendUnicast;
		stat.ifHCOutBroadcastPkts = ctx->Status.Int64NumSendBroadcast;
		stat.ifOutErrors = ctx->Status.Int64NumSendError;
		stat.ifHCInUcastOctets = ctx->Status.Int64BytesRecvUnicast;
		stat.ifHCInBroadcastOctets = ctx->Status.Int64BytesRecvBroadcast;
		stat.ifHCOutUcastOctets = ctx->Status.Int64BytesSendUnicast;
		stat.ifHCOutBroadcastOctets = ctx->Status.Int64BytesSendBroadcast;
		break;

	case OID_GEN_INTERRUPT_MODERATION:
		// Interrupt Moderation (NDIS 6.0)
		NeoZero(&intp, sizeof(intp));
		buf = &intp;
		size = sizeof(intp);

		intp.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
		intp.Header.Revision = NDIS_INTERRUPT_MODERATION_PARAMETERS_REVISION_1;
		intp.Header.Size = NDIS_SIZEOF_INTERRUPT_MODERATION_PARAMETERS_REVISION_1;
		intp.InterruptModeration = NdisInterruptModerationNotSupported;
		break;

	default:
		// Unknown OID
		*BytesWritten = 0;
		return NDIS_STATUS_INVALID_OID;
	}

	if (size > InformationBufferLength)
	{
		// Undersize
		*BytesNeeded = size;
		*BytesWritten = 0;
		return NDIS_STATUS_INVALID_LENGTH;
	}

	// Data copy
	NeoCopy(InformationBuffer, buf, size);
	*BytesWritten = size;

	return NDIS_STATUS_SUCCESS;
}

// Set the cable connection state
void NeoSetConnectState(BOOL connected)
{
	if (ctx == NULL)
	{
		return;
	}
	ctx->Connected = connected;
	NeoCheckConnectState();
}

// Check the cable connection state
void NeoCheckConnectState()
{
	NDIS_STATUS_INDICATION t;
	NDIS_LINK_STATE state;
	if (ctx == NULL || ctx->NdisMiniport == NULL)
	{
		return;
	}

	NeoZero(&t, sizeof(t));
	t.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION;
	t.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1;
	t.Header.Size = NDIS_SIZEOF_STATUS_INDICATION_REVISION_1;

	t.SourceHandle = ctx->NdisMiniport;

	NeoZero(&state, sizeof(state));
	state.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
	state.Header.Revision = NDIS_LINK_STATE_REVISION_1;
	state.Header.Size = NDIS_SIZEOF_LINK_STATE_REVISION_1;

	state.MediaDuplexState = NdisPauseFunctionsSendAndReceive;
	state.XmitLinkSpeed = state.RcvLinkSpeed = max_speed;
	state.PauseFunctions = NdisPauseFunctionsUnsupported;

	t.StatusCode = NDIS_STATUS_LINK_STATE;
	t.StatusBuffer = &state;
	t.StatusBufferSize = sizeof(NDIS_LINK_STATE);

	if (keep_link == false)
	{
		if (ctx->ConnectedOld != ctx->Connected || ctx->ConnectedForce)
		{
			ctx->ConnectedForce = FALSE;
			ctx->ConnectedOld = ctx->Connected;
			if (ctx->Halting == FALSE)
			{
				state.MediaConnectState = ctx->Connected ? MediaConnectStateConnected : MediaConnectStateDisconnected;
				NdisMIndicateStatusEx(ctx->NdisMiniport, &t);
			}
		}
	}
	else
	{
		if (ctx->ConnectedForce)
		{
			ctx->ConnectedForce = false;

			if (ctx->Halting == FALSE)
			{
				state.MediaConnectState = MediaConnectStateConnected;
				NdisMIndicateStatusEx(ctx->NdisMiniport, &t);
			}
		}
	}
}

// Information setting handler of adapter
NDIS_STATUS NeoNdisSet(
					NDIS_HANDLE MiniportAdapterContext,
					NDIS_OID Oid,
					void *InformationBuffer,
					ULONG InformationBufferLength,
					ULONG *BytesRead,
					ULONG *BytesNeeded)
{
	if (ctx == NULL)
	{
		return STATUS_UNSUCCESSFUL;
	}

	// Initialization
	*BytesRead = 0;
	*BytesNeeded = 0;

	// Branch processing
	switch (Oid)
	{
	case OID_GEN_CURRENT_PACKET_FILTER:
		/* Packet filter */
		if (InformationBufferLength != 4)
		{
			*BytesNeeded = 4;
			return NDIS_STATUS_INVALID_LENGTH;
		}
		*BytesRead = 4;
		ctx->CurrentPacketFilter = *((UINT *)InformationBuffer);
		return NDIS_STATUS_SUCCESS;

//	case OID_GEN_PROTOCOL_OPTIONS:
		/* Current protocol option value */
/*		if (InformationBufferLength != 4)
		{
			*BytesNeeded = 4;
			return NDIS_STATUS_INVALID_LENGTH;
		}
		*BytesRead = 4;
		ctx->CurrentProtocolOptions = *((UINT *)InformationBuffer);
		return NDIS_STATUS_SUCCESS;*/

	case OID_GEN_CURRENT_LOOKAHEAD:
		/* Look ahead */
		if (InformationBufferLength != 4)
		{
			*BytesNeeded = 4;
			return NDIS_STATUS_INVALID_LENGTH;
		}
		*BytesRead = 4;
		return NDIS_STATUS_SUCCESS;

	case OID_GEN_LINK_PARAMETERS:
		// NDIS 6.0 Link setting
		*BytesRead = InformationBufferLength;
		return NDIS_STATUS_SUCCESS;

	case OID_802_3_MULTICAST_LIST:
		// Multicast list
		*BytesRead = InformationBufferLength;

		return NDIS_STATUS_SUCCESS;

	case OID_PNP_SET_POWER:
	case OID_PNP_QUERY_POWER:
		// Power events
		*BytesRead = InformationBufferLength;

		return NDIS_STATUS_SUCCESS;
	}

	return NDIS_STATUS_INVALID_OID;
}

// Set status values of NET_BUFFER_LISTs
void NeoNdisSetNetBufferListsStatus(NET_BUFFER_LIST *nbl, UINT status)
{
	if (nbl == NULL)
	{
		return;
	}

	while (nbl != NULL)
	{
		NET_BUFFER_LIST_STATUS(nbl) = status;

		nbl = NET_BUFFER_LIST_NEXT_NBL(nbl);
	}
}

// Packet send handler
void NeoNdisSendNetBufferLists(NDIS_HANDLE MiniportAdapterContext,
							   NET_BUFFER_LIST *NetBufferLists,
							   NDIS_PORT_NUMBER PortNumber,
							   ULONG SendFlags)
{
	bool is_dispatch_level = SendFlags & NDIS_SEND_FLAGS_DISPATCH_LEVEL;
	UINT send_complete_flags = 0;
	if (ctx == NULL)
	{
		return;
	}

	if (is_dispatch_level)
	{
		send_complete_flags |= NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL;
	}

	InterlockedIncrement(&ctx->NumCurrentDispatch);

	// Update the connection state
	NeoCheckConnectState();

	if (ctx->Halting != FALSE || ctx->Opened == FALSE || ctx->Paused)
	{
		UINT status = NDIS_STATUS_FAILURE;

		if (ctx->Paused)
		{
			status = NDIS_STATUS_PAUSED;
		}
		else if (ctx->Halting)
		{
			status = NDIS_STATUS_FAILURE;
		}
		else if (ctx->Opened == false && keep_link)
		{
			status = NDIS_STATUS_SUCCESS;
		}

		NeoNdisSetNetBufferListsStatus(NetBufferLists, status);

		InterlockedDecrement(&ctx->NumCurrentDispatch);

		NdisMSendNetBufferListsComplete(ctx->NdisMiniport, NetBufferLists, send_complete_flags);

		return;
	}

	// Operation of the packet queue
	NeoLockPacketQueue();
	{
		NET_BUFFER_LIST *nbl;

		if (ctx->Halting != FALSE || ctx->Opened == FALSE || ctx->Paused)
		{
			UINT status = NDIS_STATUS_FAILURE;

			if (ctx->Paused)
			{
				status = NDIS_STATUS_PAUSED;
			}
			else if (ctx->Halting)
			{
				status = NDIS_STATUS_FAILURE;
			}
			else if (ctx->Opened == false && keep_link)
			{
				status = NDIS_STATUS_SUCCESS;
			}

			NeoUnlockPacketQueue();

			NeoNdisSetNetBufferListsStatus(NetBufferLists, status);

			InterlockedDecrement(&ctx->NumCurrentDispatch);

			NdisMSendNetBufferListsComplete(ctx->NdisMiniport, NetBufferLists, send_complete_flags);

			return;
		}

		nbl = NetBufferLists;

		while (nbl != NULL)
		{
			NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl);

			NET_BUFFER_LIST_STATUS(nbl) = NDIS_STATUS_SUCCESS;

			while (nb != NULL)
			{
				UINT size = NET_BUFFER_DATA_LENGTH(nb);

				if (size >= NEO_MIN_PACKET_SIZE && size <= NEO_MAX_PACKET_SIZE)
				{
					UCHAR *buf = NeoMalloc(size);
					void *ptr;

					ptr = NdisGetDataBuffer(nb, size, buf, 1, 0);

					if (ptr == NULL)
					{
						ctx->Status.NumPacketSendError++;
						ctx->Status.Int64NumSendError++;
						NeoFree(buf);
					}
					else
					{
						if (ptr != buf)
						{
							NeoCopy(buf, ptr, size);
						}

						NeoInsertQueue(buf, size);
						ctx->Status.NumPacketSend++;

						if (buf[0] & 0x40)
						{
							ctx->Status.Int64NumSendBroadcast++;
							ctx->Status.Int64BytesSendBroadcast += (UINT64)size;
						}
						else
						{
							ctx->Status.Int64NumSendUnicast++;
							ctx->Status.Int64BytesSendUnicast += (UINT64)size;
						}

						ctx->Status.Int64BytesSendTotal += (UINT64)size;
					}
				}
				else
				{
					ctx->Status.NumPacketSendError++;
					ctx->Status.Int64NumSendError++;
				}

				nb = NET_BUFFER_NEXT_NB(nb);
			}

			nbl = NET_BUFFER_LIST_NEXT_NBL(nbl);
		}

		// Reception event
		NeoSet(ctx->Event);
	}
	NeoUnlockPacketQueue();

	// Notify the transmission completion
	InterlockedDecrement(&ctx->NumCurrentDispatch);
	NdisMSendNetBufferListsComplete(ctx->NdisMiniport, NetBufferLists, send_complete_flags);
}

// Initialize the packet array
void NeoInitPacketArray()
{
	UINT i;
	// Create a packet buffer
	for (i = 0;i < NEO_MAX_PACKET_EXCHANGE;i++)
	{
		ctx->PacketBuffer[i] = NeoNewPacketBuffer();
	}
}

// Release the packet array
void NeoFreePacketArray()
{
	UINT i;
	for (i = 0;i < NEO_MAX_PACKET_EXCHANGE;i++)
	{
		NeoFreePacketBuffer(ctx->PacketBuffer[i]);
		ctx->PacketBuffer[i] = NULL;
	}
}

// Release the packet buffer
void NeoFreePacketBuffer(PACKET_BUFFER *p)
{
	// Validate arguments
	if (p == NULL)
	{
		return;
	}

	// Release the NET_BUFFER_LIST
	NdisFreeNetBufferList(p->NetBufferList);
	// Release the NET_BUFFER_LIST pool
	NdisFreeNetBufferListPool(p->NetBufferListPool);
	// Release the memory
	NeoFree(p);
}

// Create a packet buffer
PACKET_BUFFER *NeoNewPacketBuffer()
{
	PACKET_BUFFER *p;
	NET_BUFFER_LIST_POOL_PARAMETERS p1;

	// Memory allocation
	p = NeoZeroMalloc(sizeof(PACKET_BUFFER));

	// Create a NET_BUFFER_LIST pool
	NeoZero(&p1, sizeof(p1));
	p1.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
	p1.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
	p1.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
	p1.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT;
	p1.fAllocateNetBuffer = TRUE;
	p1.DataSize = NEO_MAX_PACKET_SIZE;
	p1.PoolTag = 'SETH';
	p->NetBufferListPool = NdisAllocateNetBufferListPool(NULL, &p1);

	// Create a NET_BUFFER_LIST
	p->NetBufferList = NdisAllocateNetBufferList(p->NetBufferListPool, 0, 0);

	return p;
}

// Reset the event
void NeoReset(NEO_EVENT *event)
{
	// Validate arguments
	if (event == NULL)
	{
		return;
	}

	KeResetEvent(event->event);
}

// Set the event
void NeoSet(NEO_EVENT *event)
{
	// Validate arguments
	if (event == NULL)
	{
		return;
	}

	KeSetEvent(event->event, 0, FALSE);
}

// Release the event
void NeoFreeEvent(NEO_EVENT *event)
{
	// Validate arguments
	if (event == NULL)
	{
		return;
	}

	ZwClose(event->event_handle);

	// Release the memory
	NeoFree(event);
}

// Create a new event
NEO_EVENT *NeoNewEvent(char *name)
{
	UNICODE *unicode_name;
	NEO_EVENT *event;
	// Validate arguments
	if (name == NULL)
	{
		return NULL;
	}

	// Convert the name to Unicode
	unicode_name = NewUnicode(name);
	if (unicode_name == NULL)
	{
		return NULL;
	}

	// Memory allocation
	event = NeoZeroMalloc(sizeof(NEO_EVENT));
	if (event == NULL)
	{
		FreeUnicode(unicode_name);
		return NULL;
	}

	// Create an Event
	event->event = IoCreateNotificationEvent(GetUnicode(unicode_name), &event->event_handle);
	if (event->event == NULL)
	{
		NeoFree(event);
		FreeUnicode(unicode_name);
		return NULL;
	}

	// Initialize the event
	KeInitializeEvent(event->event, NotificationEvent, FALSE);
	KeClearEvent(event->event);

	// Release a string
	FreeUnicode(unicode_name);

	return event;
}

// Get the Unicode string
NDIS_STRING *GetUnicode(UNICODE *u)
{
	// Validate arguments
	if (u == NULL)
	{
		return NULL;
	}

	return &u->String;
}

// Release the Unicode strings
void FreeUnicode(UNICODE *u)
{
	// Validate arguments
	if (u == NULL)
	{
		return;
	}

	// Release a string
	NdisFreeString(u->String);

	// Release the memory
	NeoFree(u);
}

// Create a new Unicode string
UNICODE *NewUnicode(char *str)
{
	UNICODE *u;
	// Validate arguments
	if (str == NULL)
	{
		return NULL;
	}

	// Memory allocation
	u = NeoZeroMalloc(sizeof(UNICODE));
	if (u == NULL)
	{
		return NULL;
	}

	// String initialization
	NdisInitializeString(&u->String, str);

	return u;
}

// Release the lock
void NeoFreeLock(NEO_LOCK *lock)
{
	NDIS_SPIN_LOCK *spin_lock;
	// Validate arguments
	if (lock == NULL)
	{
		return;
	}

	spin_lock = &lock->spin_lock;
	NdisFreeSpinLock(spin_lock);

	// Release the memory
	NeoFree(lock);
}

// Unlock
void NeoUnlock(NEO_LOCK *lock)
{
	NDIS_SPIN_LOCK *spin_lock;
	// Validate arguments
	if (lock == NULL)
	{
		return;
	}

	spin_lock = &lock->spin_lock;
	NdisReleaseSpinLock(spin_lock);
}

// Lock
void NeoLock(NEO_LOCK *lock)
{
	NDIS_SPIN_LOCK *spin_lock;
	// Validate arguments
	if (lock == NULL)
	{
		return;
	}

	spin_lock = &lock->spin_lock;
	NdisAcquireSpinLock(spin_lock);
}

// Creating a new lock
NEO_LOCK *NeoNewLock()
{
	NDIS_SPIN_LOCK *spin_lock;

	// Memory allocation
	NEO_LOCK *lock = NeoZeroMalloc(sizeof(NEO_LOCK));
	if (lock == NULL)
	{
		return NULL;
	}

	// Initialize spin lock
	spin_lock = &lock->spin_lock;

	NdisAllocateSpinLock(spin_lock);

	return lock;
}

// Memory copy
void NeoCopy(void *dst, void *src, UINT size)
{
	// Validate arguments
	if (dst == NULL || src == NULL || size == 0)
	{
		return;
	}

	// Copy
	NdisMoveMemory(dst, src, size);
}

// Memory clear
void NeoZero(void *dst, UINT size)
{
	// Validate arguments
	if (dst == NULL || size == 0)
	{
		return;
	}

	// Clear
	NdisZeroMemory(dst, size);
}

// Clear to zero by memory allocation
void *NeoZeroMalloc(UINT size)
{
	void *p = NeoMalloc(size);
	if (p == NULL)
	{
		// Memory allocation failure
		return NULL;
	}
	// Clear to zero
	NeoZero(p, size);
	return p;
}

// Memory allocation
void *NeoMalloc(UINT size)
{
	NDIS_STATUS r;
	void *p;
	if (size == 0)
	{
		size = 1;
	}

	// Allocate the non-paged memory
	r = NdisAllocateMemoryWithTag(&p, size, 'SETH');

	if (NG(r))
	{
		return NULL;
	}
	return p;
}

// Release the memory
void NeoFree(void *p)
{
	// Validate arguments
	if (p == NULL)
	{
		return;
	}

	// Release the memory
	NdisFreeMemory(p, 0, 0);
}


