/*	
 *	util.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"

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

typedef struct _NAME_TABLE {
	int code;
	const char* name;
} NAME_TABLE;

static const char*
_lookup_name_table(int code, const NAME_TABLE* name_table, int table_size)
{
	static char tmp[16];
	//
	int i;
	const char* name;

	for(i = 0; i < table_size; i++) {
		if(name_table[i].code == code) {
			return name_table[i].name;
		}
	}

	_snprintf(tmp, sizeof tmp, "$%x", code);
	return tmp;
}

#define lookup_name_table(code, name_table) \
	_lookup_name_table((code), (name_table), sizeof (name_table) / sizeof *(name_table))

/**/

static const NAME_TABLE oid_name_table[] = {
/* General Operational Characteristics */
{ OID_GEN_SUPPORTED_LIST,           "OID_GEN_SUPPORTED_LIST"           },
{ OID_GEN_HARDWARE_STATUS,          "OID_GEN_HARDWARE_STATUS"          },
{ OID_GEN_MEDIA_SUPPORTED,          "OID_GEN_MEDIA_SUPPORTED"          },
{ OID_GEN_MEDIA_IN_USE,             "OID_GEN_MEDIA_IN_USE"             },
{ OID_GEN_MAXIMUM_LOOKAHEAD,        "OID_GEN_MAXIMUM_LOOKAHEAD"        },
{ OID_GEN_MAXIMUM_FRAME_SIZE,       "OID_GEN_MAXIMUM_FRAME_SIZE"       },
{ OID_GEN_LINK_SPEED,               "OID_GEN_LINK_SPEED"               },
{ OID_GEN_TRANSMIT_BUFFER_SPACE,    "OID_GEN_TRANSMIT_BUFFER_SPACE"    },
{ OID_GEN_RECEIVE_BUFFER_SPACE,     "OID_GEN_RECEIVE_BUFFER_SPACE"     },
{ OID_GEN_TRANSMIT_BLOCK_SIZE,      "OID_GEN_TRANSMIT_BLOCK_SIZE"      },
{ OID_GEN_RECEIVE_BLOCK_SIZE,       "OID_GEN_RECEIVE_BLOCK_SIZE"       },
{ OID_GEN_VENDOR_ID,                "OID_GEN_VENDOR_ID"                },
{ OID_GEN_VENDOR_DESCRIPTION,       "OID_GEN_VENDOR_DESCRIPTION"       },
{ OID_GEN_VENDOR_DRIVER_VERSION,    "OID_GEN_VENDOR_DRIVER_VERSION"    },
{ OID_GEN_CURRENT_PACKET_FILTER,    "OID_GEN_CURRENT_PACKET_FILTER"    },
{ OID_GEN_CURRENT_LOOKAHEAD,        "OID_GEN_CURRENT_LOOKAHEAD"        },
{ OID_GEN_DRIVER_VERSION,           "OID_GEN_DRIVER_VERSION"           },
{ OID_GEN_MAXIMUM_TOTAL_SIZE,       "OID_GEN_MAXIMUM_TOTAL_SIZE"       },
{ OID_GEN_PROTOCOL_OPTIONS,         "OID_GEN_PROTOCOL_OPTIONS"         },
{ OID_GEN_MAC_OPTIONS,              "OID_GEN_MAC_OPTIONS"              },
{ OID_GEN_MEDIA_CONNECT_STATUS,     "OID_GEN_MEDIA_CONNECT_STATUS"     },
{ OID_GEN_MAXIMUM_SEND_PACKETS,     "OID_GEN_MAXIMUM_SEND_PACKETS"     },
{ OID_GEN_SUPPORTED_GUIDS,          "OID_GEN_SUPPORTED_GUIDS"          },
{ OID_GEN_NETWORK_LAYER_ADDRESSES,  "OID_GEN_NETWORK_LAYER_ADDRESSES"  },
{ OID_GEN_TRANSPORT_HEADER_OFFSET,  "OID_GEN_TRANSPORT_HEADER_OFFSET"  },
{ OID_GEN_PHYSICAL_MEDIUM,          "OID_GEN_PHYSICAL_MEDIUM"          },
{ OID_GEN_MACHINE_NAME,             "OID_GEN_MACHINE_NAME"             },
{ OID_GEN_VLAN_ID,                  "OID_GEN_VLAN_ID"                  },
{ OID_GEN_RNDIS_CONFIG_PARAMETER,   "OID_GEN_RNDIS_CONFIG_PARAMETER"   },
/* General Statistics */
{ OID_GEN_XMIT_OK,                  "OID_GEN_XMIT_OK"                  },
{ OID_GEN_RCV_OK,                   "OID_GEN_RCV_OK"                   },
{ OID_GEN_XMIT_ERROR,               "OID_GEN_XMIT_ERROR"               },
{ OID_GEN_RCV_ERROR,                "OID_GEN_RCV_ERROR"                },
{ OID_GEN_RCV_NO_BUFFER,            "OID_GEN_RCV_NO_BUFFER"            },
{ OID_GEN_DIRECTED_BYTES_XMIT,      "OID_GEN_DIRECTED_BYTES_XMIT"      },
{ OID_GEN_DIRECTED_FRAMES_XMIT,     "OID_GEN_DIRECTED_FRAMES_XMIT"     },
{ OID_GEN_MULTICAST_BYTES_XMIT,     "OID_GEN_MULTICAST_BYTES_XMIT"     },
{ OID_GEN_MULTICAST_FRAMES_XMIT,    "OID_GEN_MULTICAST_FRAMES_XMIT"    },
{ OID_GEN_BROADCAST_BYTES_XMIT,     "OID_GEN_BROADCAST_BYTES_XMIT"     },
{ OID_GEN_BROADCAST_FRAMES_XMIT,    "OID_GEN_BROADCAST_FRAMES_XMIT"    },
{ OID_GEN_DIRECTED_BYTES_RCV,       "OID_GEN_DIRECTED_BYTES_RCV"       },
{ OID_GEN_DIRECTED_FRAMES_RCV,      "OID_GEN_DIRECTED_FRAMES_RCV"      },
{ OID_GEN_MULTICAST_BYTES_RCV,      "OID_GEN_MULTICAST_BYTES_RCV"      },
{ OID_GEN_MULTICAST_FRAMES_RCV,     "OID_GEN_MULTICAST_FRAMES_RCV"     },
{ OID_GEN_BROADCAST_BYTES_RCV,      "OID_GEN_BROADCAST_BYTES_RCV"      },
{ OID_GEN_BROADCAST_FRAMES_RCV,     "OID_GEN_BROADCAST_FRAMES_RCV"     },
{ OID_GEN_RCV_CRC_ERROR,            "OID_GEN_RCV_CRC_ERROR"            },
{ OID_GEN_TRANSMIT_QUEUE_LENGTH,    "OID_GEN_TRANSMIT_QUEUE_LENGTH"    },
/* Ethernet Operational Characteristics */
{ OID_802_3_PERMANENT_ADDRESS,      "OID_802_3_PERMANENT_ADDRESS"      },
{ OID_802_3_CURRENT_ADDRESS,        "OID_802_3_CURRENT_ADDRESS"        },
{ OID_802_3_MULTICAST_LIST,         "OID_802_3_MULTICAST_LIST"         },
{ OID_802_3_MAXIMUM_LIST_SIZE,      "OID_802_3_MAXIMUM_LIST_SIZE"      },
{ OID_802_3_MAC_OPTIONS,            "OID_802_3_MAC_OPTIONS"            },
/* Ethernet Statistics */
{ OID_802_3_RCV_ERROR_ALIGNMENT,    "OID_802_3_RCV_ERROR_ALIGNMENT"    },
{ OID_802_3_XMIT_ONE_COLLISION,     "OID_802_3_XMIT_ONE_COLLISION"     },
{ OID_802_3_XMIT_MORE_COLLISIONS,   "OID_802_3_XMIT_MORE_COLLISIONS"   },
{ OID_802_3_XMIT_DEFERRED,          "OID_802_3_XMIT_DEFERRED"          },
{ OID_802_3_XMIT_MAX_COLLISIONS,    "OID_802_3_XMIT_MAX_COLLISIONS"    },
{ OID_802_3_RCV_OVERRUN,            "OID_802_3_RCV_OVERRUN"            },
{ OID_802_3_XMIT_UNDERRUN,          "OID_802_3_XMIT_UNDERRUN"          },
{ OID_802_3_XMIT_HEARTBEAT_FAILURE, "OID_802_3_XMIT_HEARTBEAT_FAILURE" },
{ OID_802_3_XMIT_TIMES_CRS_LOST,    "OID_802_3_XMIT_TIMES_CRS_LOST"    },
{ OID_802_3_XMIT_LATE_COLLISIONS,   "OID_802_3_XMIT_LATE_COLLISIONS"   },
};

const char*
oid_name(int code)
{
	return lookup_name_table(code, oid_name_table);
}

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

PKTHREAD
create_thread(void (*start_routine)(void*), void* start_context)
{
	HANDLE thread_handle = NULL;
	PKTHREAD thread_object = NULL;
	//
	NTSTATUS status;

	/* スレッドを作成し、スレッドハンドルを取得します。
	 * * PsCreateSystemThread()が成功した場合、スレッドハンドルがスレッド実体への参照を保持しています。
	 */
	status = PsCreateSystemThread(
		&thread_handle,		/* ThreadHandle */
		THREAD_ALL_ACCESS,	/* DesiredAccess */
		NULL,			/* ObjectAttributes */
		NULL,			/* ProcessHandle */
		NULL,			/* ClientId */
		start_routine,		/* StartRoutine */
		start_context);		/* StartContext */
	if(!NT_SUCCESS(status)) {
		KdPrint(("PsCreateSystemThread failed\n"));
		goto L_EXIT;
	}

	/* スレッドハンドルから、スレッドオブジェクトを取得します。
	 * * ObReferenceObjectByHandle()が成功した場合、スレッドハンドルとスレッドオブジェクトがスレッド実体への参照を保持しています。
	 */
	status = ObReferenceObjectByHandle(
		thread_handle,		/* Handle */
		THREAD_ALL_ACCESS,	/* DesiredAccess */
		NULL,			/* ObjectType */
		KernelMode,		/* AccessMode */
		(void*)&thread_object,	/* Object */
		NULL);			/* HandleInformation */
	if(!NT_SUCCESS(status)) {
		KdPrint(("ObReferenceObjectByHandle failed\n"));
		goto L_EXIT;
	}

L_EXIT:	/*-------------------------------------------------------------------*/

	/* PsCreateSystemThread()が成功していたら、ObReferenceObjectByHandle()の成否にかかわらず、スレッドハンドルを開放します。
	 * * ObReferenceObjectByHandle()が成功していた場合、スレッドオブジェクトがスレッド実体への参照を保持しています。
	 *   これが、通常の動作です。
	 * * ObReferenceObjectByHandle()が失敗していた場合、スレッド実体への参照が無く、スレッドは宙に浮いた状態になっています。
	 *   せめて、スレッドを強制終了させたいところですが、カーネルモードにはスレッドを強制終了するAPIが無いため、不可能です。
	 *   スレッドが動作したまま、ドライバがアンロードされると、おそらくメモリ保護エラーでシステムが停止するでしょう。
	 *   実際には、ObReferenceObjectByHandle()が失敗することはまず無いはずなので、このような問題は発生しないかも知れません。
	 *   しかし、もしもObReferenceObjectByHandle()が失敗した場合、上述の問題が発生することを留意しておいてください。(未解決)
	 */
	if(thread_handle) {
		ZwClose(thread_handle);
	}

	return thread_object;
}

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

void
delete_thread(PKTHREAD thread_object)
{
	NTSTATUS status;

	/* 確実にスレッドが停止するのを待ちます。
	 * あらかじめ呼び出し側にて、スレッドへの停止要求を発行しておいてください。
	 */
	status = KeWaitForSingleObject(thread_object, Executive, KernelMode, FALSE, NULL);
	if(!NT_SUCCESS(status)) {
		KdPrint(("KeWaitForSingleObject failed\n"));
		/* 強行(^^; */
	}

	/* スレッドオブジェクトを開放します。 */
	ObDereferenceObject(thread_object);
}

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

NTSTATUS
call_usbd(DEVICE_OBJECT* usbd, URB* urb)
{
	NTSTATUS status;
	KEVENT event;
	IRP* irp;
	IO_STACK_LOCATION* stack_location;
	IO_STATUS_BLOCK status_block;

	/* 完了を待つためのイベントを初期化します。 */
	KeInitializeEvent(&event, NotificationEvent, FALSE); /* SynchronizationEventでも可 */

	/* 新しいIRPを作成します。
	 * I/OマネージャがIRPを解放するので、ドライバが明示的に解放する必要はありません。
	 */
	irp = IoBuildDeviceIoControlRequest(
		IOCTL_INTERNAL_USB_SUBMIT_URB,	/* IoControlCode */
		usbd,				/* DeviceObject */
		NULL,				/* InputBuffer */
		0,				/* InputBufferLength */
		NULL,				/* OutputBuffer */
		0,				/* OutputBufferLength */
		TRUE,				/* InternalDeviceIoControl */
		&event,				/* Event */
		&status_block);			/* IoStatusBlock */
	if(!irp) {
		return STATUS_UNSUCCESSFUL;
	}

	/* IRPのパラメータとして、URBを格納します。 */
	stack_location = IoGetNextIrpStackLocation(irp); /* IoGetCurrentIrpStackLocation()ではない!! */
	stack_location->Parameters.Others.Argument1 = urb;

	/* IOリクエストを発行し、必要に応じて完了を待ちます。
	 * * call_driver()と違って、下位ドライバがSTATUS_PENDINGを返した場合のみ、完了イベントを待ちます。
	 *   独自の完了ルーチンを提供していないので、あらゆる完了シナリオでI/Oマネージャがイベントをシグナル状態に
	 *   するかどうかについて、確信を持つことはできないからです。(『Microsoft WDM プログラミング』p.566より)
	 */
	status = IoCallDriver(usbd, irp);
	if(status == STATUS_PENDING) {
		KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
	}

	/* IRPは、I/Oマネージャによって、完了と同時にもう破棄されています。
	 * 結果は、IRPのIO_STATUS_BLOCKではなく、呼び出し側提供のIO_STATUS_BLOCKに格納されています。
	 */
	return status_block.Status; /* "return status"や"return irp->IoStatus.Status"ではない!! */
}

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

USB_CONFIGURATION_DESCRIPTOR*
get_configuration_descriptor(DEVICE_OBJECT* usbd, int i_configuration)
{
	NTSTATUS status;
	URB urb;
	int configuration_descriptor_size;
	USB_CONFIGURATION_DESCRIPTOR* configuration_descriptor;

	/* コンフィギュレーション、インターフェイス、エンドポイントディスクリプタ全体のサイズを取得するために、
	 * まず、コンフィギュレーションディスクリプタのみを取得します。
	 */
	configuration_descriptor_size = sizeof(USB_CONFIGURATION_DESCRIPTOR);
	/*{{同じコード*/
	configuration_descriptor = ExAllocatePool(NonPagedPool, configuration_descriptor_size);
	if(!configuration_descriptor) {
		KdPrint(("ExAllocatePool failed\n"));
		return NULL;
	}
	UsbBuildGetDescriptorRequest(
		&urb,						/* Urb */
		sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),	/* Length */
		USB_CONFIGURATION_DESCRIPTOR_TYPE,		/* DescriptorType */
		(unsigned char)i_configuration,			/* Index */
		0,						/* LanguageId */
		configuration_descriptor,			/* TransferBuffer */
		NULL,						/* TransferBufferMDL */
		configuration_descriptor_size,			/* TransferBufferLength */
		NULL);						/* Link */
	status = call_usbd(usbd, &urb);
	if(!NT_SUCCESS(status)) {
		KdPrint(("call_usbd failed\n"));
		ExFreePool(configuration_descriptor);
		return NULL;
	}
	/*}}同じコード*/
	configuration_descriptor_size = configuration_descriptor->wTotalLength;
	ExFreePool(configuration_descriptor);

	/* コンフィギュレーション、インターフェイス、エンドポイントディスクリプタ全体を取得します。 */
	/*{{同じコード*/
	configuration_descriptor = ExAllocatePool(NonPagedPool, configuration_descriptor_size);
	if(!configuration_descriptor) {
		KdPrint(("ExAllocatePool failed\n"));
		return NULL;
	}
	UsbBuildGetDescriptorRequest(
		&urb,						/* Urb */
		sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST),	/* Length */
		USB_CONFIGURATION_DESCRIPTOR_TYPE,		/* DescriptorType */
		(unsigned char)i_configuration,			/* Index */
		0,						/* LanguageId */
		configuration_descriptor,			/* TransferBuffer */
		NULL,						/* TransferBufferMDL */
		configuration_descriptor_size,			/* TransferBufferLength */
		NULL);						/* Link */
	status = call_usbd(usbd, &urb);
	if(!NT_SUCCESS(status)) {
		KdPrint(("call_usbd failed\n"));
		ExFreePool(configuration_descriptor);
		return NULL;
	}
	/*}}同じコード*/

	return configuration_descriptor;
}

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

/* コンフィギュレーションを選択します。
 */
int
set_configuration(DEVICE_OBJECT* usbd, USBD_PIPE_INFORMATION pipes[/*max_pipes*/], int max_pipes)
{
	int num_pipes = 0;
	//
	URB* urb = NULL;
	USB_CONFIGURATION_DESCRIPTOR* configuration_descriptor = NULL;
	//
	NTSTATUS status;
	USBD_INTERFACE_LIST_ENTRY interface_list[1 + 1/*終端*/];
	USB_INTERFACE_DESCRIPTOR* interface_descriptor;
	USBD_INTERFACE_INFORMATION* interface_information;

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

	/* コンフィギュレーションディスクリプタを取得します。 */
	configuration_descriptor = get_configuration_descriptor(usbd, 0);
	if(!configuration_descriptor) {
		KdPrint(("get_configuration_descriptor failed\n"));
		goto L_EXIT;
	}

	/* SET_CONFIGURATIONの準備。 */
	memset(interface_list, 0, sizeof interface_list); /* 必要!! */
	interface_list[0].InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
		configuration_descriptor,	/* ConfigurationDescriptor */
		configuration_descriptor,	/* StartPosition */
		-1,				/* InterfaceNumber */
		-1,				/* AlternateSetting */
		-1,				/* InterfaceClass */
		-1,				/* InterfaceSubClass */
		-1);				/* InterfaceProtocol */
	if(!interface_list[0].InterfaceDescriptor) {
		KdPrint(("USBD_ParseConfigurationDescriptorEx failed\n"));
		goto L_EXIT;
	}
	urb = USBD_CreateConfigurationRequestEx(configuration_descriptor, interface_list);
	if(!urb) {
		KdPrint(("USBD_CreateConfigurationRequestEx failed\n"));
		goto L_EXIT;
	}

	/* SET_CONFIGURATIONを発行します。 */
	status = call_usbd(usbd, urb);
	if(!NT_SUCCESS(status)) {
		KdPrint(("call_usbd failed\n"));
		goto L_EXIT;
	}
	interface_information = interface_list[0].Interface;

	/* バルクIN/OUTパイプを取得します。 */
	if((int)interface_information->NumberOfPipes > max_pipes) {
		KdPrint(("Number of pipes error\n"));
		goto L_EXIT;
	}
	while(num_pipes < (int)interface_information->NumberOfPipes) {
		pipes[num_pipes] = interface_information->Pipes[num_pipes];
		num_pipes++;
	}

L_EXIT:	/*-------------------------------------------------------------------*/

	if(urb) {
		ExFreePool(urb);
	}
	if(configuration_descriptor) {
		ExFreePool(configuration_descriptor);
	}

	return num_pipes;
}

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

/* コンフィギュレーションを未選択に戻します。
 */
void
clear_configuration(DEVICE_OBJECT* usbd)
{
	URB urb;

	UsbBuildSelectConfigurationRequest(
		&urb,						/* Urb */
		sizeof(struct _URB_SELECT_CONFIGURATION),	/* Length */
		NULL);						/* ConfigurationDescriptor */
	call_usbd(usbd, &urb);
}

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

/* 同期IRPのキャンセル…『Microsoft WDM プログラミング』p.237〜239参照 */
static NTSTATUS
usb_transfer_completion_routine(DEVICE_OBJECT* device_object, IRP* irp, void* context)
{
	KEVENT* event = context;
	KeSetEvent(event, IO_NO_INCREMENT, FALSE);
	return STATUS_MORE_PROCESSING_REQUIRED;
}

NTSTATUS
usb_transfer(DEVICE_OBJECT* usbd, URB* urb, KEVENT* cancel_event)
{
	NTSTATUS status;
	IRP* irp;
	IO_STACK_LOCATION* stack_location;
	KEVENT event;
	IO_STATUS_BLOCK status_block;
	int pending;
	//
	void* wait_objects[THREAD_WAIT_OBJECTS];
	ASSERT(THREAD_WAIT_OBJECTS >= 3);

	/* IRP完了通知イベントを初期化します。 */
	KeInitializeEvent(&event, NotificationEvent, FALSE); /* SynchronizationEventは不可 */

	/* IRPを作成します。(完了時に自動的に解放されるので、明示的な解放は不要です) */
	irp = IoBuildDeviceIoControlRequest(
		IOCTL_INTERNAL_USB_SUBMIT_URB,	/* IoControlCode */
		usbd,				/* DeviceObject */
		NULL,				/* InputBuffer */
		0,				/* InputBufferLength */
		NULL,				/* OutputBuffer */
		0,				/* OutputBufferLength */
		TRUE,				/* InternalDeviceIoControl */
		&event,				/* Event */
		&status_block);			/* IoStatusBlock */
	if(!irp) {
		KdPrint(("IoBuildDeviceIoControlRequest failed\n"));
		status = STATUS_UNSUCCESSFUL;
		goto L_EXIT;
	}
	stack_location = IoGetNextIrpStackLocation(irp); /* IoGetCurrentIrpStackLocation()ではありません!! */
	stack_location->Parameters.Others.Argument1 = urb;

	/* 同期IRPのキャンセル…『Microsoft WDM プログラミング』p.237〜239参照 */
	IoSetCompletionRoutine(irp, usb_transfer_completion_routine, &event, TRUE, TRUE, TRUE);
	pending = IoCallDriver(usbd, irp) == STATUS_PENDING;
	if(pending) {	/* ←無くても可 */
		wait_objects[0] = cancel_event;
		wait_objects[1] = &event;
		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/*cancel_event*/) {
			IoCancelIrp(irp);
		}
		KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); /* usb_transfer_completion_routine()によるEvent set待ち */
	}		/* ←無くても可 */
	KeClearEvent(&event); /* usb_transfer_completion_routine()によるEvent setを解除する */
	IoCompleteRequest(irp, IO_NO_INCREMENT); /* STATUS_MORE_PROCESSING_REQUIREDによって中断しているIRPの完了処理を再開する */
	if(pending) {	/* ←必須!! */
		/* ★重要: IoBuildDeviceIoControlRequest()に指定したEventが、IRPの完了時にシグナル状態になるのは、
		 *         IoCallDriver()からSTATUS_PENDINGが返された場合だけです。
		 *         STATUS_PENDING以外が返された場合は、IRPが完了しても、Eventはシグナル状態になりません!!
		 */
		KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); /* IoBuildDeviceIoControlRequest()によるEvent set待ち */
	}		/* ←必須!! */
	status = status_block.Status;

L_EXIT:	/*-------------------------------------------------------------------*/

	return status;
}

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

#define SET_DEVICE_ADDRESS 0 /* デバイスファームウェアと一致させてください */

NTSTATUS
set_device_address(DEVICE_OBJECT* usbd, const unsigned char address[6])
{
	NTSTATUS status;
	URB urb;

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

	/* ベンダリクエストを構築します。 */
	UsbBuildVendorRequest(
		&urb,							/* Urb */
		URB_FUNCTION_VENDOR_INTERFACE,				/* Function */
		sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST),	/* Length */
		USBD_TRANSFER_DIRECTION_OUT | USBD_SHORT_TRANSFER_OK,	/* TransferFlags */
		0,							/* ReservedBits */
		SET_DEVICE_ADDRESS,					/* Request */
		0,							/* Value */
		0,							/* Index */
		(unsigned char*)address,				/* TransferBuffer */
		NULL,							/* TransferBufferMDL */
		6,							/* TransferBufferLength */
		NULL);							/* Link */

	/* ベンダリクエストを発行します。 */
	status = call_usbd(usbd, &urb);
	if(!NT_SUCCESS(status)) {
		KdPrint(("call_usbd failed\n"));
		/* FALLTHRU */
	}

	return status;
}
