/*	
 *	rdwr.c
 *
 *	PceCom - P/ECE USBシリアルポートデバイスドライバ
 *	Copyright (C) 2005 Naoyuki Sawa
 *
 *	* Sat Jul 23 17:10:00 JST 2005 Naoyuki Sawa
 *	- 1st リリース。
 */
#include "app.h"

#define VERBOSE

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

static void rdwr_cancel_routine(DEVICE_OBJECT* device_object, IRP* irp);
static void read_thread_routine(void* context);
static void write_thread_routine(void* context);
static void polling_thread_routine(void* context);

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

NTSTATUS
init_rdwr(DEVICE_OBJECT* device_object)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	RDWR* rdwr = &device_extension->rdwr;
	//
	NTSTATUS status;

	KdPrint(("init_rdwr\n"));

	KeInitializeEvent(&rdwr->terminate_event, NotificationEvent, FALSE);

	/* Readスレッドを開始します。 */
	rdwr->read_capacity = device_extension->read_pipe.MaximumTransferSize;
	rdwr->read_buffer = ExAllocatePool(NonPagedPool, rdwr->read_capacity);
	if(!rdwr->read_buffer) {
		KdPrint(("ExAllocatePool failed\n"));
		return STATUS_UNSUCCESSFUL;
	}
	InitializeListHead(&rdwr->read_irp_list);
	KeInitializeEvent(&rdwr->read_start_event, SynchronizationEvent, FALSE);
	KeInitializeEvent(&rdwr->read_pulse_event, SynchronizationEvent, FALSE);
	rdwr->read_thread = create_thread(read_thread_routine, device_object);
	if(!rdwr->read_thread) {
		KdPrint(("create_thread failed\n"));
		return STATUS_UNSUCCESSFUL;
	}

	/* Writeスレッドを開始します。 */
	rdwr->write_capacity = device_extension->write_pipe.MaximumTransferSize;
	rdwr->write_buffer = ExAllocatePool(NonPagedPool, rdwr->write_capacity);
	if(!rdwr->write_buffer) {
		KdPrint(("ExAllocatePool failed\n"));
		return STATUS_UNSUCCESSFUL;
	}
	InitializeListHead(&rdwr->write_irp_list);
	KeInitializeEvent(&rdwr->write_start_event, SynchronizationEvent, FALSE);
	KeInitializeEvent(&rdwr->write_pulse_event, SynchronizationEvent, FALSE);
	rdwr->write_thread = create_thread(write_thread_routine, device_object);
	if(!rdwr->write_thread) {
		KdPrint(("create_thread failed\n"));
		return STATUS_UNSUCCESSFUL;
	}

	/* ポーリングスレッドを開始します。 */
	rdwr->polling_thread = create_thread(polling_thread_routine, device_object);
	if(!rdwr->polling_thread) {
		KdPrint(("create_thread failed\n"));
		return STATUS_UNSUCCESSFUL;
	}

	return STATUS_SUCCESS;
}

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

void
exit_rdwr(DEVICE_OBJECT* device_object)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	RDWR* rdwr = &device_extension->rdwr;

	KdPrint(("exit_rdwr\n"));

	/* 全スレッドへの停止要求を発行します。 */
	KeSetEvent(&rdwr->terminate_event, IO_NO_INCREMENT, FALSE);

	/* ポーリングスレッドを終了します。 */
	if(rdwr->polling_thread) {
		delete_thread(rdwr->polling_thread);
	}

	/* Writeスレッドを終了します。 */
	if(rdwr->write_thread) {
		delete_thread(rdwr->write_thread);
	}
	if(rdwr->write_buffer) {
		ExFreePool(rdwr->write_buffer);
	}

	/* Readスレッドを終了します。 */
	if(rdwr->read_thread) {
		delete_thread(rdwr->read_thread);
	}
	if(rdwr->read_buffer) {
		ExFreePool(rdwr->read_buffer);
	}
}

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

NTSTATUS
dispatch_read(DEVICE_OBJECT* device_object, IRP* irp)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	RDWR* rdwr = &device_extension->rdwr;
	//
	NTSTATUS status;

#ifdef VERBOSE
	KdPrint(("dispatch_read\n"));
#endif /*VERBOSE*/

	/* このIRPのリムーブロックを獲得します。 */
	status = IoAcquireRemoveLock(&device_extension->remove_lock, irp);
	if(!NT_SUCCESS(status)) {
		KdPrint(("IoAcquireRemoveLock failed\n"));
		return complete_request(irp, status, 0);
	}

	/* IRPをキューへ追加した瞬間に所有権が無くなるので、先にマークしておきます。(必須!!) */
	IoMarkIrpPending(irp);

ENTER_CS;
	/* IRPがキューの中にある間、キャンセルルーチンを設定します。 */
	IoSetCancelRoutine(irp, rdwr_cancel_routine);

	/* IRPをキューへ追加します。 */
	InsertTailList(&rdwr->read_irp_list, &irp->Tail.Overlay.ListEntry);
LEAVE_CS;

	/* このイベントはヒントに過ぎないので、キューへの追加とアトミックでなくて大丈夫です。 */
	KeSetEvent(&rdwr->read_start_event, IO_NO_INCREMENT, FALSE);

	return STATUS_PENDING;
}

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

NTSTATUS
dispatch_write(DEVICE_OBJECT* device_object, IRP* irp)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	RDWR* rdwr = &device_extension->rdwr;
	//
	NTSTATUS status;

#ifdef VERBOSE
	KdPrint(("dispatch_write\n"));
#endif /*VERBOSE*/

	/* このIRPのリムーブロックを獲得します。 */
	status = IoAcquireRemoveLock(&device_extension->remove_lock, irp);
	if(!NT_SUCCESS(status)) {
		KdPrint(("IoAcquireRemoveLock failed\n"));
		return complete_request(irp, status, 0);
	}

	/* IRPをキューへ追加した瞬間に所有権が無くなるので、先にマークしておきます。(必須!!) */
	IoMarkIrpPending(irp);

ENTER_CS;
	/* IRPがキューの中にある間、キャンセルルーチンを設定します。 */
	IoSetCancelRoutine(irp, rdwr_cancel_routine);

	/* IRPをキューへ追加します。 */
	InsertTailList(&rdwr->write_irp_list, &irp->Tail.Overlay.ListEntry);
LEAVE_CS;

	/* このイベントはヒントに過ぎないので、キューへの追加とアトミックでなくて大丈夫です。 */
	KeSetEvent(&rdwr->write_start_event, IO_NO_INCREMENT, FALSE);

	return STATUS_PENDING;
}

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

static void
rdwr_cancel_routine(DEVICE_OBJECT* device_object, IRP* irp)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;

	///* IRPをキューから取り除きます。
	// * * RemoveHeadList()は、ListEntryからチェインをたどるので、ListHeadを指定する必要がありません。
	// *   従って、Read/Write両方で同じキャンセルルーチンを共用できます。
	// */
	//RemoveHeadList(&irp->Tail.Overlay.ListEntry);
	//------------------------------------------------------------------------------------------------
	//↓2005/07/30 Naoyuki Sawa 修正
	//↓キャンセル要求のあったIRPでなく、誤って現在のIRPキューの先頭をキャンセルしてしまっていました。
	//------------------------------------------------------------------------------------------------
	/* IRPをキューから取り除きます。
	 * * RemoveEntryList()は、ListEntryからチェインをたどるので、ListEntryを指定する必要がありません。
	 *   従って、Read/Write両方で同じキャンセルルーチンを共用できます。
	 */
	RemoveEntryList(&irp->Tail.Overlay.ListEntry);

	/* キャンセルルーチンはキャンセルスピンロックを獲得した状態で呼ばれます。(仕様)
	 * キャンセルスピンロックの保護対象はIRPキューなので、もう解放しても大丈夫です。
	 */
	IoReleaseCancelSpinLock(irp->CancelIrql);

	/* IRPを完了させます。 */
	IoReleaseRemoveLock(&device_extension->remove_lock, irp);
	complete_request(irp, STATUS_CANCELLED, 0);
}

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

void
abort_read(DEVICE_OBJECT* device_object)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	RDWR* rdwr = &device_extension->rdwr;
	//
	LIST_ENTRY irp_list;
	LIST_ENTRY* list_entry;
	IRP* irp;

	InitializeListHead(&irp_list);

ENTER_CS;

	while(!IsListEmpty(&rdwr->read_irp_list)) {
		list_entry = RemoveHeadList(&rdwr->read_irp_list);
		irp = CONTAINING_RECORD(list_entry, IRP, Tail.Overlay.ListEntry);
		IoSetCancelRoutine(irp, NULL); /* キューから取り外したので、キャンセルルーチンを解除 */
		InsertTailList(&irp_list, list_entry);
	}
	if(rdwr->read_current_irp) {
		rdwr->read_current_irp->Cancel = TRUE;
	}

LEAVE_CS;

	while(!IsListEmpty(&irp_list)) {
		list_entry = RemoveHeadList(&irp_list);
		irp = CONTAINING_RECORD(list_entry, IRP, Tail.Overlay.ListEntry);
		IoReleaseRemoveLock(&device_extension->remove_lock, irp);
		complete_request(irp, STATUS_CANCELLED, 0);
	}
}

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

void
abort_write(DEVICE_OBJECT* device_object)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	RDWR* rdwr = &device_extension->rdwr;
	//
	LIST_ENTRY irp_list;
	LIST_ENTRY* list_entry;
	IRP* irp;

	InitializeListHead(&irp_list);

ENTER_CS;

	while(!IsListEmpty(&rdwr->write_irp_list)) {
		list_entry = RemoveHeadList(&rdwr->write_irp_list);
		irp = CONTAINING_RECORD(list_entry, IRP, Tail.Overlay.ListEntry);
		IoSetCancelRoutine(irp, NULL); /* キューから取り外したので、キャンセルルーチンを解除 */
		InsertTailList(&irp_list, list_entry);
	}
	if(rdwr->write_current_irp) {
		rdwr->write_current_irp->Cancel = TRUE;
	}

LEAVE_CS;

	while(!IsListEmpty(&irp_list)) {
		list_entry = RemoveHeadList(&irp_list);
		irp = CONTAINING_RECORD(list_entry, IRP, Tail.Overlay.ListEntry);
		IoReleaseRemoveLock(&device_extension->remove_lock, irp);
		complete_request(irp, STATUS_CANCELLED, 0);
	}
}

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

static void
read_thread_routine(void* context)
{
	DEVICE_OBJECT* device_object = context;
	//
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	IOCTL* ioctl = &device_extension->ioctl;
	RDWR* rdwr = &device_extension->rdwr;
	//
	KTIMER timeout_timer; /* 要停止 */
	//
	NTSTATUS status;
	LIST_ENTRY* list_entry;
	IRP* irp;
	IO_STACK_LOCATION* stack_location;
	unsigned char* buffer;
	int length;
	int copy_length;
	int total_length;
	int timeout_msec;
	LARGE_INTEGER timeout;
	//
	void* wait_objects[3];
	ASSERT(THREAD_WAIT_OBJECTS >= 3);

	KdPrint(("read_thread_routine start\n"));

	/* タイムアウトタイマを初期化します。 */
	KeInitializeTimer(&timeout_timer);

	for(;;) {
		/* キューから次のIRPを取り出します。 */
		wait_objects[0] = &rdwr->terminate_event;
		wait_objects[1] = &rdwr->read_start_event;
		for(;;) {
ENTER_CS;
			if(!IsListEmpty(&rdwr->read_irp_list)) {
				list_entry = RemoveHeadList(&rdwr->read_irp_list);
				irp = CONTAINING_RECORD(list_entry, IRP, Tail.Overlay.ListEntry);
				IoSetCancelRoutine(irp, NULL); /* キューから取り外したので、キャンセルルーチンを解除 */
				rdwr->read_current_irp = irp;
			} else {
				irp = NULL;
			}
LEAVE_CS;
			if(irp) {
				break;
			}
			status = KeWaitForMultipleObjects(2, wait_objects, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
			if(!NT_SUCCESS(status)) {
				KdPrint(("KeWaitForMultipleObjects failed\n"));
				goto L_EXIT;
			}
			if(status == 0/*Terminate*/) {
				goto L_EXIT;
			}
		}
		stack_location = IoGetCurrentIrpStackLocation(irp);
		buffer = irp->AssociatedIrp.SystemBuffer;
		length = stack_location->Parameters.Read.Length;
		if(!length) {
			rdwr->read_current_irp = NULL;
			IoReleaseRemoveLock(&device_extension->remove_lock, irp);
			complete_request(irp, STATUS_SUCCESS, 0);
			continue;
		}
		total_length = 0;

		/* タイムアウトを計算します。(※仮) */
ENTER_CS;
		if(!ioctl->timeouts.ReadTotalTimeoutMultiplier &&
		   !ioctl->timeouts.ReadTotalTimeoutConstant) {
			if(ioctl->timeouts.ReadIntervalTimeout == -1) {
				timeout_msec =  0; /* ブロックしない */
			} else {
				timeout_msec = -1; /* タイムアウトしない */
			}
		} else {
			timeout_msec = ioctl->timeouts.ReadTotalTimeoutMultiplier * length
				     + ioctl->timeouts.ReadTotalTimeoutConstant;
			if(timeout_msec <= 0) { /* オーバーフロー対策 */
				timeout_msec = INT_MAX;
			}
		}
LEAVE_CS;

		/* 取り出したIRPを処理します。 */
		wait_objects[0] = &rdwr->terminate_event;
		wait_objects[1] = &rdwr->read_pulse_event;
		wait_objects[2] = &timeout_timer;
		if(timeout_msec >= 0) {
			relative_timeout(&timeout, timeout_msec);
			KeSetTimer(&timeout_timer, timeout, NULL); /* 前回のタイマが動作中ならば、暗黙の停止後にリスタートします */
		} else {
			KeCancelTimer(&timeout_timer);
		}
		for(;;) {
			status = KeWaitForMultipleObjects(3, wait_objects, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
			if(!NT_SUCCESS(status)) {
				KdPrint(("KeWaitForMultipleObjects failed\n"));
				rdwr->read_current_irp = NULL;
				IoReleaseRemoveLock(&device_extension->remove_lock, irp);
				complete_request(irp, STATUS_CANCELLED, 0);
				goto L_EXIT;
			}
			if(status == 0/*Terminate*/) {
				KdPrint(("Read terminate\n"));
				rdwr->read_current_irp = NULL;
				IoReleaseRemoveLock(&device_extension->remove_lock, irp);
				complete_request(irp, STATUS_CANCELLED, 0);
				goto L_EXIT;
			}
			if(irp->Cancel) {
				KdPrint(("Read cancel\n"));
				rdwr->read_current_irp = NULL;
				IoReleaseRemoveLock(&device_extension->remove_lock, irp);
				complete_request(irp, STATUS_CANCELLED, 0);
				break;
			}
ENTER_CS;
			/* 受信バッファの有効サイズと、リクエストバッファの空きサイズのうち、小さい方をコピーサイズとします。 */
			copy_length = rdwr->read_length;
			if(copy_length > length) {
				copy_length = length;
			}

			/* 受信バッファからリクエストバッファへコピーします。 */
			memcpy(buffer, rdwr->read_buffer, copy_length);
			memmove(rdwr->read_buffer, &rdwr->read_buffer[copy_length], rdwr->read_length - copy_length);
			rdwr->read_length -= copy_length;
LEAVE_CS;
			/* コピーした分、リクエストバッファのポインタとサイズを調整し、合計コピーサイズを増やします。 */
			buffer += copy_length;
			length -= copy_length;
			total_length += copy_length;

			//if(!length) {
			//	rdwr->read_current_irp = NULL;
			//	IoReleaseRemoveLock(&device_extension->remove_lock, irp);
			//	complete_request(irp, STATUS_SUCCESS, total_length);		/* リクエストバッファ全体に受信完了 */
			//	break;
			//}
			//↓※仮
			if(total_length) {
				rdwr->read_current_irp = NULL;
				IoReleaseRemoveLock(&device_extension->remove_lock, irp);
				complete_request(irp, STATUS_SUCCESS, total_length);		/* 1バイト以上受信したら即完了(仮) */
				break;
			}
			if(status == 2/*Timeout*/) {
				rdwr->read_current_irp = NULL;
				IoReleaseRemoveLock(&device_extension->remove_lock, irp);
				if(total_length) {
					complete_request(irp, STATUS_SUCCESS, total_length);	/* 1バイト以上受信してタイムアウト */
				} else {
					complete_request(irp, STATUS_TIMEOUT, 0);		/* 1バイトも受信できずにタイムアウト */
				}
				break;
			}
		}
	}

L_EXIT:
	/* タイムアウトタイマを確実に停止します。 */
	KeCancelTimer(&timeout_timer);

	/* * Read/Write/ポーリングスレッドのうち、一部のスレッドだけがエラー要因で終了することを防ぎます。
	 *   Read/Write/ポーリングスレッドのうち、ひとつでも終了したら、他のスレッドも確実に終了させます。
	 * - exit_rdwr()から、明示的に全スレッドを停止した場合は、この処理はダミー動作となります。
	 */
	KeSetEvent(&rdwr->terminate_event, IO_NO_INCREMENT, FALSE);

	KdPrint(("read_thread_routine exit\n"));
	PsTerminateSystemThread(STATUS_SUCCESS);
}

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

static void
write_thread_routine(void* context)
{
	DEVICE_OBJECT* device_object = context;
	//
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	IOCTL* ioctl = &device_extension->ioctl;
	RDWR* rdwr = &device_extension->rdwr;
	//
	KTIMER timeout_timer; /* 要停止 */
	//
	NTSTATUS status;
	LIST_ENTRY* list_entry;
	IRP* irp;
	IO_STACK_LOCATION* stack_location;
	unsigned char* buffer;
	int length;
	int copy_length;
	int total_length;
	int timeout_msec;
	LARGE_INTEGER timeout;
	//
	void* wait_objects[3];
	ASSERT(THREAD_WAIT_OBJECTS >= 3);

	KdPrint(("write_thread_routine start\n"));

	/* タイムアウトタイマを初期化します。 */
	KeInitializeTimer(&timeout_timer);

	for(;;) {
		/* キューから次のIRPを取り出します。 */
		wait_objects[0] = &rdwr->terminate_event;
		wait_objects[1] = &rdwr->write_start_event;
		for(;;) {
ENTER_CS;
			if(!IsListEmpty(&rdwr->write_irp_list)) {
				list_entry = RemoveHeadList(&rdwr->write_irp_list);
				irp = CONTAINING_RECORD(list_entry, IRP, Tail.Overlay.ListEntry);
				IoSetCancelRoutine(irp, NULL); /* キューから取り外したので、キャンセルルーチンを解除 */
				rdwr->write_current_irp = irp;
			} else {
				irp = NULL;
			}
LEAVE_CS;
			if(irp) {
				break;
			}
			status = KeWaitForMultipleObjects(2, wait_objects, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
			if(!NT_SUCCESS(status)) {
				KdPrint(("KeWaitForMultipleObjects failed\n"));
				goto L_EXIT;
			}
			if(status == 0/*Terminate*/) {
				goto L_EXIT;
			}
		}
		stack_location = IoGetCurrentIrpStackLocation(irp);
		buffer = irp->AssociatedIrp.SystemBuffer;
		length = stack_location->Parameters.Write.Length;
		if(!length) {
			rdwr->write_current_irp = NULL;
			IoReleaseRemoveLock(&device_extension->remove_lock, irp);
			complete_request(irp, STATUS_SUCCESS, 0);
			continue;
		}
		total_length = 0;

		/* タイムアウトを計算します。(※仮) */
ENTER_CS;
		if(!ioctl->timeouts.WriteTotalTimeoutMultiplier &&
		   !ioctl->timeouts.WriteTotalTimeoutConstant) {
			timeout_msec = -1; /* タイムアウトしない */
		} else {
			timeout_msec = ioctl->timeouts.WriteTotalTimeoutMultiplier * length
				     + ioctl->timeouts.WriteTotalTimeoutConstant;
			if(timeout_msec <= 0) { /* オーバーフロー対策 */
				timeout_msec = INT_MAX;
			}
		}
LEAVE_CS;

		/* 取り出したIRPを処理します。 */
		wait_objects[0] = &rdwr->terminate_event;
		wait_objects[1] = &rdwr->write_pulse_event;
		wait_objects[2] = &timeout_timer;
		if(timeout_msec >= 0) {
			relative_timeout(&timeout, timeout_msec);
			KeSetTimer(&timeout_timer, timeout, NULL); /* 前回のタイマが動作中ならば、暗黙の停止後にリスタートします */
		} else {
			KeCancelTimer(&timeout_timer);
		}
		for(;;) {
			status = KeWaitForMultipleObjects(3, wait_objects, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
			if(!NT_SUCCESS(status)) {
				KdPrint(("KeWaitForMultipleObjects failed\n"));
				rdwr->write_current_irp = NULL;
				IoReleaseRemoveLock(&device_extension->remove_lock, irp);
				complete_request(irp, STATUS_CANCELLED, 0);
				goto L_EXIT;
			}
			if(status == 0/*Terminate*/) {
				KdPrint(("Write terminate\n"));
				rdwr->write_current_irp = NULL;
				IoReleaseRemoveLock(&device_extension->remove_lock, irp);
				complete_request(irp, STATUS_CANCELLED, 0);
				goto L_EXIT;
			}
			if(irp->Cancel) {
				KdPrint(("Write cancel\n"));
				rdwr->write_current_irp = NULL;
				IoReleaseRemoveLock(&device_extension->remove_lock, irp);
				complete_request(irp, STATUS_CANCELLED, 0);
				break;
			}
ENTER_CS;
			/* 送信バッファの空きサイズと、リクエストバッファの有効サイズのうち、小さい方をコピーサイズとします。 */
			copy_length = rdwr->write_capacity - rdwr->write_length;
			if(copy_length > length) {
				copy_length = length;
			}

			/* リクエストバッファから送信バッファへコピーします。 */
			memcpy(&rdwr->write_buffer[rdwr->write_length], buffer, copy_length);
			rdwr->write_length += copy_length;
LEAVE_CS;
			/* コピーした分、リクエストバッファのポインタとサイズを調整し、合計コピーサイズを増やします。 */
			buffer += copy_length;
			length -= copy_length;
			total_length += copy_length;

			if(!length) {
				rdwr->write_current_irp = NULL;
				IoReleaseRemoveLock(&device_extension->remove_lock, irp);
				complete_request(irp, STATUS_SUCCESS, total_length);		/* リクエストバッファ全体を送信完了 */
				break;
			}
			if(status == 2/*Timeout*/) {
				rdwr->write_current_irp = NULL;
				IoReleaseRemoveLock(&device_extension->remove_lock, irp);
				if(total_length) {
					complete_request(irp, STATUS_SUCCESS, total_length);	/* 1バイト以上送信してタイムアウト */
				} else {
					complete_request(irp, STATUS_TIMEOUT, 0);		/* 1バイトも送信できずにタイムアウト */
				}
				break;
			}
		}
	}

L_EXIT:
	/* タイムアウトタイマを確実に停止します。 */
	KeCancelTimer(&timeout_timer);

	/* * Read/Write/ポーリングスレッドのうち、一部のスレッドだけがエラー要因で終了することを防ぎます。
	 *   Read/Write/ポーリングスレッドのうち、ひとつでも終了したら、他のスレッドも確実に終了させます。
	 * - exit_rdwr()から、明示的に全スレッドを停止した場合は、この処理はダミー動作となります。
	 */
	KeSetEvent(&rdwr->terminate_event, IO_NO_INCREMENT, FALSE);

	KdPrint(("write_thread_routine exit\n"));
	PsTerminateSystemThread(STATUS_SUCCESS);
}

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

#define POLLING_INTERVAL	1	/* ミリ秒 */

static void
polling_thread_routine(void* context)
{
	DEVICE_OBJECT* device_object = context;
	//
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	IOCTL* ioctl = &device_extension->ioctl;
	RDWR* rdwr = &device_extension->rdwr;
	//
	KTIMER polling_interval_timer; /* 要停止 */
	unsigned char* buffer/*[MaximumTransferSize]*/ = NULL;
	//
	NTSTATUS status;
	URB urb;
	int length;
	LARGE_INTEGER timeout;
	//
	int event = 0;
	int wait_mask;
	IRP* wait_on_mask_irp;
	//
	void* wait_objects[3];
	ASSERT(THREAD_WAIT_OBJECTS >= 3);

	KdPrint(("polling_thread_routine start\n"));

	/* ポーリングインターバルタイマを開始します。 */
	KeInitializeTimer(&polling_interval_timer);
	relative_timeout(&timeout, POLLING_INTERVAL);
	KeSetTimerEx(&polling_interval_timer, timeout, POLLING_INTERVAL, NULL);

	/* 送受信用の一時バッファを確保します。 */
	buffer = ExAllocatePool(NonPagedPool,
		device_extension->read_pipe.MaximumTransferSize > device_extension->write_pipe.MaximumTransferSize ?
		device_extension->read_pipe.MaximumTransferSize : device_extension->write_pipe.MaximumTransferSize);
	if(!buffer) {
		KdPrint(("ExAllocatePool failed\n"));
		goto L_EXIT;
	}

	wait_objects[0] = &rdwr->terminate_event;
	wait_objects[1] = &polling_interval_timer;
	for(;;) {
		status = KeWaitForMultipleObjects(2, wait_objects, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
		if(!NT_SUCCESS(status)) {
			KdPrint(("KeWaitForMultipleObjects failed\n"));
			goto L_EXIT;
		}
		if(status == 0/*Terminate*/) {
			goto L_EXIT;
		}

		/*-------------------- Read --------------------*/

ENTER_CS;
		/* 受信サイズを決定します。
		 * * 受信バッファはパケットサイズの倍数でなければいけないことに注意してください!!
		 *   一回のトランスファは複数回のトランザクションで構成されますが、個々のトランザクションに注目したときに、
		 *   ホストの受信バッファサイズがデバイスの送信データサイズを下回っていてはいけません。
		 *   ホストの受信バッファサイズが不足すると、USBDへの受信要求が失敗したり、完了しなかったりするみたいです。
		 */
		length = rdwr->read_capacity - rdwr->read_length;			/* 受信バッファの空きサイズ */
		if(length > (int)device_extension->read_pipe.MaximumTransferSize) {
			length = device_extension->read_pipe.MaximumTransferSize;	/* 最大転送サイズを上限とする */
		}
		length &= ~(device_extension->read_pipe.MaximumPacketSize - 1);		/* パケットサイズの倍数とする */
LEAVE_CS;

		if(length) {
			/*{{この間に、受信バッファが読み出されて減ることはあっても、増えることは無いはずです。*/

			/* 受信処理を行います。 */
			UsbBuildInterruptOrBulkTransferRequest(
				&urb,
				sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
				device_extension->read_pipe.PipeHandle,
				buffer,
				NULL,
				length,
				USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_IN,
				NULL);
			status = call_usbd(device_extension->next_device_object, &urb);
			if(!NT_SUCCESS(status)) {
				KdPrint(("call_usbd failed\n"));
				goto L_EXIT;
			}
			length = urb.UrbBulkOrInterruptTransfer.TransferBufferLength;	/* 実際に受信したサイズ */

			/*}}この間に、受信バッファが読み出されて減ることはあっても、増えることは無いはずです。*/
ENTER_CS;
			/* 一時バッファから受信バッファへコピーします。 */
			ASSERT(rdwr->read_length + length <= rdwr->read_capacity);
			memcpy(&rdwr->read_buffer[rdwr->read_length], buffer, length);
			rdwr->read_length += length;

			/* イベントを検出します。 */
			event |= SERIAL_EV_RXCHAR;		/* 任意のキャラクタを受信した */
			if(memchr(buffer, ioctl->chars.EventChar, length)) {
				event |= SERIAL_EV_RXFLAG;	/* イベントキャラクタを受信した */
			}
LEAVE_CS;
		}

		/* 受信スレッドへの定期パルスを発行します。 */
		KeSetEvent(&rdwr->read_pulse_event, IO_NO_INCREMENT, FALSE);

		/*-------------------- Write --------------------*/

ENTER_CS;
		/* 送信サイズを決定します。 */
		length = rdwr->write_length;						/* 送信バッファの有効サイズ */
		if(length > (int)device_extension->write_pipe.MaximumTransferSize) {
			length = device_extension->write_pipe.MaximumTransferSize;	/* 最大転送サイズを上限とする */
		}

		if(length) {
			/* 送信バッファから一時バッファへコピーします。 */
			memcpy(buffer, rdwr->write_buffer, length);
			memmove(rdwr->write_buffer, &rdwr->write_buffer[length], rdwr->write_length - length);
			rdwr->write_length -= length;

			/* イベントを検出します。 */
			if(!rdwr->write_length) {
				event |= SERIAL_EV_TXEMPTY;	/* 送信バッファが空になった */
			}
		}
LEAVE_CS;

		if(length) {
			/* 送信処理を行います。 */
			UsbBuildInterruptOrBulkTransferRequest(
				&urb,
				sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER),
				device_extension->write_pipe.PipeHandle,
				buffer,
				NULL,
				length,
				USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_OUT,
				NULL);
			status = call_usbd(device_extension->next_device_object, &urb);
			if(!NT_SUCCESS(status)) {
				KdPrint(("call_usbd failed\n"));
				goto L_EXIT;
			}

			/* 送信処理が成功した場合、送信サイズが最大転送サイズ以下ならば、全部一度で送信できているはずです。
			 * * シリアル通信などのデータ区切りの無い通信では、要求よりも小さなサイズでの送信成功も有り得ます。
			 *   しかし、USBの場合は、トランスファ単位でのデータの区切りが意味を持っているので、
			 *   バスドライバが勝手に送信サイズを減らして、送信を成功させることは有り得ないはずです。
			 * * …と思うのですけれど、確証が持てないので、念のためフリービルドでもチェックすることにします(^^;
			 */
			if(length != urb.UrbBulkOrInterruptTransfer.TransferBufferLength) {
				KdPrint(("Write length error\n"));
				goto L_EXIT;
			}
		}

		/* 送信スレッドへの定期パルスを発行します。 */
		KeSetEvent(&rdwr->write_pulse_event, IO_NO_INCREMENT, FALSE);

		/*-------------------- Event --------------------*/

ENTER_CS;
		/* 現在のイベントマスクのうち、いずれかのイベントが発生していたら、イベント待ちIRPを取り出します。(あれば) */
		wait_mask = event & ioctl->wait_mask;
		if(wait_mask) {
			wait_on_mask_irp = ioctl->wait_on_mask_irp;
			ioctl->wait_on_mask_irp = NULL;
			if(wait_on_mask_irp) {
				IoSetCancelRoutine(wait_on_mask_irp, NULL); /* 保持しなくなったら、キャンセルルーチンを解除 */
			}
		} else {
			wait_on_mask_irp = NULL;
		}
LEAVE_CS;

		/* 現在のイベントマスクのうち、いずれかのイベントが発生していたら、イベント待ちIRPを完了させます。(あれば) */
		if(wait_on_mask_irp) {
			complete_wait_on_mask(wait_on_mask_irp, wait_mask);
		}

		/* イベント待ちIRPへ完了通知したイベントだけをクリアします。(※仕様不明。要確認) */
		event &= ~wait_mask;
	}

L_EXIT:
	/* 送受信用の一時バッファを開放します。 */
	if(buffer) {
		ExFreePool(buffer);
	}

	/* ポーリングインターバルタイマを停止します。 */
	KeCancelTimer(&polling_interval_timer);

	/* * Read/Write/ポーリングスレッドのうち、一部のスレッドだけがエラー要因で終了することを防ぎます。
	 *   Read/Write/ポーリングスレッドのうち、ひとつでも終了したら、他のスレッドも確実に終了させます。
	 * - exit_rdwr()から、明示的に全スレッドを停止した場合は、この処理はダミー動作となります。
	 */
	KeSetEvent(&rdwr->terminate_event, IO_NO_INCREMENT, FALSE);

	KdPrint(("polling_thread_routine exit\n"));
	PsTerminateSystemThread(STATUS_SUCCESS);
}
