/*	
 *	ioctl.c
 *
 *	PceCom - P/ECE USBシリアルポートデバイスドライバ
 *	Copyright (C) 2005 Naoyuki Sawa
 *
 *	* Wed Aug 03 21:57:00 JST 2005 Naoyuki Sawa
 *	- 1st リリース。
 */
#include "app.h"

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

static void wait_on_mask_cancel_routine(DEVICE_OBJECT* device_object, IRP* irp);

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

static const int SettableParams[] = {
	SERIAL_SP_PARITY        ,
	SERIAL_SP_BAUD          ,
	SERIAL_SP_DATABITS      ,
	SERIAL_SP_STOPBITS      ,
	SERIAL_SP_HANDSHAKING   ,
	SERIAL_SP_PARITY_CHECK  ,
	SERIAL_SP_CARRIER_DETECT,
	0
};
static const int SettableData[] = {
	SERIAL_DATABITS_5  ,
	SERIAL_DATABITS_6  ,
	SERIAL_DATABITS_7  ,
	SERIAL_DATABITS_8  ,
	SERIAL_DATABITS_16 ,
	SERIAL_DATABITS_16X,
	0
};
static const int SettableStopParity[] = {
	SERIAL_STOPBITS_10 ,
	SERIAL_STOPBITS_15 ,
	SERIAL_STOPBITS_20 ,
	SERIAL_PARITY_NONE ,
	SERIAL_PARITY_ODD  ,
	SERIAL_PARITY_EVEN ,
	SERIAL_PARITY_MARK ,
	SERIAL_PARITY_SPACE,
	0
};
static const int SettableBaud[] = {
	SERIAL_BAUD_075   ,
	SERIAL_BAUD_110   ,
	SERIAL_BAUD_134_5 ,
	SERIAL_BAUD_150   ,
	SERIAL_BAUD_300   ,
	SERIAL_BAUD_600   ,
	SERIAL_BAUD_1200  ,
	SERIAL_BAUD_1800  ,
	SERIAL_BAUD_2400  ,
	SERIAL_BAUD_4800  ,
	SERIAL_BAUD_7200  ,
	SERIAL_BAUD_9600  ,
	SERIAL_BAUD_14400 ,
	SERIAL_BAUD_19200 ,
	SERIAL_BAUD_38400 ,
	SERIAL_BAUD_56K   ,
	SERIAL_BAUD_128K  ,
	SERIAL_BAUD_115200,
	SERIAL_BAUD_57600 ,
	SERIAL_BAUD_USER  ,
	0
};
static const int ProvCapabilities[] = {
	SERIAL_PCF_DTRDSR       ,
	SERIAL_PCF_RTSCTS       ,
	SERIAL_PCF_CD           ,
	SERIAL_PCF_PARITY_CHECK ,
	SERIAL_PCF_XONXOFF      ,
	SERIAL_PCF_SETXCHAR     ,
	SERIAL_PCF_TOTALTIMEOUTS,
	SERIAL_PCF_INTTIMEOUTS  ,
	SERIAL_PCF_SPECIALCHARS ,
	SERIAL_PCF_16BITMODE    ,
	0
};
static int join_bits(const int* bits) {
	int v = 0;
	while(*bits) {
		v |= *bits++;
	}
	return v;
}

NTSTATUS
ioctl_init(DEVICE_OBJECT* device_object)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;

	KdPrint(("ioctl_init\n"));

ENTER_CS;

	/* IOCTL_SERIAL_GET_PROPERTIESの準備をしておきます。 */
	memset(&device_extension->properties, 0, sizeof device_extension->properties);
	device_extension->properties.PacketLength	= sizeof(SERIAL_COMMPROP);
	device_extension->properties.PacketVersion	= 2;
	device_extension->properties.ServiceMask	= SERIAL_SP_SERIALCOMM;
	device_extension->properties.ProvSubType	= SERIAL_SP_RS232;
	device_extension->properties.MaxTxQueue		= 0;
	device_extension->properties.MaxRxQueue		= 0;
	device_extension->properties.CurrentTxQueue	= 0;
	device_extension->properties.CurrentRxQueue	= 0;
	device_extension->properties.SettableParams	= join_bits(SettableParams);
	device_extension->properties.SettableData	= (unsigned short)join_bits(SettableData);
	device_extension->properties.SettableStopParity	= (unsigned short)join_bits(SettableStopParity);
	device_extension->properties.SettableBaud	= join_bits(SettableBaud);
	device_extension->properties.MaxBaud		= SERIAL_BAUD_USER;
	device_extension->properties.ProvCapabilities	= join_bits(ProvCapabilities);

LEAVE_CS;

	return STATUS_SUCCESS;
}

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

void
ioctl_exit(DEVICE_OBJECT* device_object)
{
	KdPrint(("ioctl_exit\n"));

	///* WaitOnMask IRPが残っていたら、完了させます。 */
	//if(device_extension->wait_on_mask_irp) {
	//	complete_wait_on_mask(device_extension->wait_on_mask_irp, 0);
	//}
	//→不要？
}

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

static NTSTATUS
ioctl_serial_set_queue_size(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_QUEUE_SIZE* queue_size)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	//
	NTSTATUS status;

	KdPrint(("InSize  = %d\n", queue_size->InSize ));
	KdPrint(("OutSize = %d\n", queue_size->OutSize));

	status = rx_resize(device_object, queue_size->InSize);
	if(!NT_SUCCESS(status)) {
		KdPrint(("rx_resize failed\n"));
		goto L_EXIT;
	}
	device_extension->queue_size.InSize = queue_size->InSize; /* 1word書き込みなので、排他制御不要 */

	status = tx_resize(device_object, queue_size->OutSize);
	if(!NT_SUCCESS(status)) {
		KdPrint(("tx_resize failed\n"));
		goto L_EXIT;
	}
	device_extension->queue_size.OutSize = queue_size->OutSize; /* 1word書き込みなので、排他制御不要 */

L_EXIT:
	return status;
}

static NTSTATUS
ioctl_serial_get_properties(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_COMMPROP* properties)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	*properties = device_extension->properties;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_get_commstatus(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_STATUS* comm_status)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	*comm_status = device_extension->comm_status;
	comm_status->AmountInInQueue  = device_extension->rx_length;
	comm_status->AmountInOutQueue = device_extension->tx_length;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_get_modemstatus(DEVICE_OBJECT* device_object, IRP* irp, int* modem_status)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	*modem_status = device_extension->modem_status;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_set_dtr(DEVICE_OBJECT* device_object, IRP* irp, void* junk)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	int comm_event = 0;
ENTER_CS;
	if(!(device_extension->dtr_rts & SERIAL_DTR_STATE)) {
		device_extension->dtr_rts |= SERIAL_DTR_STATE;
		comm_event |= SERIAL_EV_DSR; /* DTR=DSRと仮定 */
	}
LEAVE_CS;
	assert_comm_event(device_object, comm_event);
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_clr_dtr(DEVICE_OBJECT* device_object, IRP* irp, void* junk)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	int comm_event = 0;
ENTER_CS;
	if(device_extension->dtr_rts & SERIAL_DTR_STATE) {
		device_extension->dtr_rts &= ~SERIAL_DTR_STATE;
		comm_event |= SERIAL_EV_DSR; /* DTR=DSRと仮定 */
	}
LEAVE_CS;
	assert_comm_event(device_object, comm_event);
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_set_rts(DEVICE_OBJECT* device_object, IRP* irp, void* junk)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	int comm_event = 0;
ENTER_CS;
	if(!(device_extension->dtr_rts & SERIAL_RTS_STATE)) {
		device_extension->dtr_rts |= SERIAL_RTS_STATE;
		comm_event |= SERIAL_EV_CTS; /* RTS=CTSと仮定 */
	}
LEAVE_CS;
	assert_comm_event(device_object, comm_event);
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_clr_rts(DEVICE_OBJECT* device_object, IRP* irp, void* junk)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	int comm_event = 0;
ENTER_CS;
	if(device_extension->dtr_rts & SERIAL_RTS_STATE) {
		device_extension->dtr_rts &= ~SERIAL_RTS_STATE;
		comm_event |= SERIAL_EV_CTS; /* RTS=CTSと仮定 */
	}
LEAVE_CS;
	assert_comm_event(device_object, comm_event);
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_get_dtrrts(DEVICE_OBJECT* device_object, IRP* irp, int* dtr_rts)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	*dtr_rts = device_extension->dtr_rts;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_purge(DEVICE_OBJECT* device_object, IRP* irp, int* purge_mask)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;

	/* 受信バッファクリア。 */
	if(*purge_mask & SERIAL_PURGE_RXCLEAR) {
		device_extension->rx_length = 0; /* 1word書き込みなので、排他制御不要 */
	}

	/* 受信IRPキャンセル。 */
	if(*purge_mask & SERIAL_PURGE_RXABORT) {
		rx_abort(device_object);
	}

	/* 送信バッファクリア。 */
	if(*purge_mask & SERIAL_PURGE_TXCLEAR) {
		device_extension->tx_length = 0; /* 1word書き込みなので、排他制御不要 */
	}

	/* 送信IRPキャンセル。 */
	if(*purge_mask & SERIAL_PURGE_TXABORT) {
		tx_abort(device_object);
	}

	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_set_line_control(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_LINE_CONTROL* line_control)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	device_extension->line_control = *line_control;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_get_line_control(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_LINE_CONTROL* line_control)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	*line_control = device_extension->line_control;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_set_baud_rate(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_BAUD_RATE* baud_rate)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	device_extension->baud_rate = *baud_rate;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_get_baud_rate(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_BAUD_RATE* baud_rate)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	*baud_rate = device_extension->baud_rate;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_set_handflow(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_HANDFLOW* handflow)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	device_extension->handflow = *handflow;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_get_handflow(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_HANDFLOW* handflow)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	*handflow = device_extension->handflow;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_set_timeouts(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_TIMEOUTS* timeouts)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	KdPrint(("ReadIntervalTimeout         = %d\n", timeouts->ReadIntervalTimeout        ));
	KdPrint(("ReadTotalTimeoutMultiplier  = %d\n", timeouts->ReadTotalTimeoutMultiplier ));
	KdPrint(("ReadTotalTimeoutConstant    = %d\n", timeouts->ReadTotalTimeoutConstant   ));
	KdPrint(("WriteTotalTimeoutMultiplier = %d\n", timeouts->WriteTotalTimeoutMultiplier));
	KdPrint(("WriteTotalTimeoutConstant   = %d\n", timeouts->WriteTotalTimeoutConstant  ));
ENTER_CS;
	device_extension->timeouts = *timeouts;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_get_timeouts(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_TIMEOUTS* timeouts)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	*timeouts = device_extension->timeouts;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_set_chars(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_CHARS* chars)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	KdPrint(("EofChar   = $%02x\n", chars->EofChar  ));
	KdPrint(("ErrorChar = $%02x\n", chars->ErrorChar));
	KdPrint(("BreakChar = $%02x\n", chars->BreakChar));
	KdPrint(("EventChar = $%02x\n", chars->EventChar));
	KdPrint(("XonChar   = $%02x\n", chars->XonChar  ));
	KdPrint(("XoffChar  = $%02x\n", chars->XoffChar ));
ENTER_CS;
	device_extension->chars = *chars;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_get_chars(DEVICE_OBJECT* device_object, IRP* irp, SERIAL_CHARS* chars)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	*chars = device_extension->chars;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_get_stats(DEVICE_OBJECT* device_object, IRP* irp, SERIALPERF_STATS* stats)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	*stats = device_extension->stats;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_clear_stats(DEVICE_OBJECT* device_object, IRP* irp, void* junk)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	memset(&device_extension->stats, 0, sizeof device_extension->stats);
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_set_wait_mask(DEVICE_OBJECT* device_object, IRP* irp, int* wait_mask)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	//
	IRP* wait_on_mask_irp;

	if(*wait_mask & SERIAL_EV_RXCHAR)   KdPrint(("SERIAL_EV_RXCHAR   (Any Character received)\n"));
	if(*wait_mask & SERIAL_EV_RXFLAG)   KdPrint(("SERIAL_EV_RXFLAG   (Received certain character)\n"));
	if(*wait_mask & SERIAL_EV_TXEMPTY)  KdPrint(("SERIAL_EV_TXEMPTY  (Transmitt Queue Empty)\n"));
	if(*wait_mask & SERIAL_EV_CTS)      KdPrint(("SERIAL_EV_CTS      (CTS changed state)\n"));
	if(*wait_mask & SERIAL_EV_DSR)      KdPrint(("SERIAL_EV_DSR      (DSR changed state)\n"));
	if(*wait_mask & SERIAL_EV_RLSD)     KdPrint(("SERIAL_EV_RLSD     (RLSD changed state)\n"));
	if(*wait_mask & SERIAL_EV_BREAK)    KdPrint(("SERIAL_EV_BREAK    (BREAK received)\n"));
	if(*wait_mask & SERIAL_EV_ERR)      KdPrint(("SERIAL_EV_ERR      (Line status error occurred)\n"));
	if(*wait_mask & SERIAL_EV_RING)     KdPrint(("SERIAL_EV_RING     (Ring signal detected)\n"));
	if(*wait_mask & SERIAL_EV_PERR)     KdPrint(("SERIAL_EV_PERR     (Printer error occured)\n"));
	if(*wait_mask & SERIAL_EV_RX80FULL) KdPrint(("SERIAL_EV_RX80FULL (Receive buffer is 80 percent full)\n"));
	if(*wait_mask & SERIAL_EV_EVENT1)   KdPrint(("SERIAL_EV_EVENT1   (Provider specific event 1)\n"));
	if(*wait_mask & SERIAL_EV_EVENT2)   KdPrint(("SERIAL_EV_EVENT2   (Provider specific event 2)\n"));

ENTER_CS;

	device_extension->comm_event &= *wait_mask;
	device_extension->wait_mask   = *wait_mask;

	/* SetWaitMaskは、WaitOnMask IRPをInformation=0で正常終了させます。(仕様) */
	wait_on_mask_irp = device_extension->wait_on_mask_irp;
	device_extension->wait_on_mask_irp = NULL;
	if(wait_on_mask_irp) {
		IoSetCancelRoutine(wait_on_mask_irp, NULL);
	}

LEAVE_CS;

	/* 必要に応じて、WaitOnMask IRPを完了します。 */
	if(wait_on_mask_irp) {
		complete_wait_on_mask(wait_on_mask_irp, 0);
	}

	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_get_wait_mask(DEVICE_OBJECT* device_object, IRP* irp, int* wait_mask)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
ENTER_CS;
	*wait_mask = device_extension->wait_mask;
LEAVE_CS;
	return STATUS_SUCCESS;
}

static NTSTATUS
ioctl_serial_wait_on_mask(DEVICE_OBJECT* device_object, IRP* irp, int* wait_mask)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	//
	NTSTATUS status;

ENTER_CS;

	/* WAIT_ON_MASKの二重実行や、SET_WAIT_MASK=0の状態で実行すると、エラー扱いとなります。(仕様) */
	if(device_extension->wait_on_mask_irp || !device_extension->wait_mask) {
		KdPrint(("Invalid parameter\n"));
		status = STATUS_INVALID_PARAMETER/*仕様*/;

	/* 既にイベントが発生していたら、すぐにWaitOnMask IRPを完了します。 */
	} else if(device_extension->comm_event) {
		KdPrint(("Immediately complete\n"));
		*wait_mask = device_extension->comm_event;
			     device_extension->comm_event = 0;
		status = STATUS_SUCCESS;

	/* さもなくば、WaitOnMask IRPを保留します。 */
	} else {
		IoMarkIrpPending(irp); /* 忘れないで!! */
		IoSetCancelRoutine(irp, wait_on_mask_cancel_routine);
		device_extension->wait_on_mask_irp = irp;
		status = STATUS_PENDING;
	}

LEAVE_CS;

	return status;
}

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

typedef struct _IOCTL_TABLE {
	int   code;
	void* ioctl;
	int   length; /* + = in(set), - = out(get) */
} IOCTL_TABLE;

static IOCTL_TABLE ioctl_table[] = {
	{	IOCTL_SERIAL_SET_QUEUE_SIZE,	ioctl_serial_set_queue_size,	 (int)sizeof(SERIAL_QUEUE_SIZE)		},
	{	IOCTL_SERIAL_GET_PROPERTIES,	ioctl_serial_get_properties,	-(int)sizeof(SERIAL_COMMPROP)		},
	{	IOCTL_SERIAL_GET_COMMSTATUS,	ioctl_serial_get_commstatus,	-(int)sizeof(SERIAL_STATUS)		},
	{	IOCTL_SERIAL_GET_MODEMSTATUS,	ioctl_serial_get_modemstatus,	-(int)sizeof(int)			},
	{	IOCTL_SERIAL_SET_DTR,		ioctl_serial_set_dtr,		 (int)0					},
	{	IOCTL_SERIAL_CLR_DTR,		ioctl_serial_clr_dtr,		 (int)0					},
	{	IOCTL_SERIAL_SET_RTS,		ioctl_serial_set_rts,		 (int)0					},
	{	IOCTL_SERIAL_CLR_RTS,		ioctl_serial_clr_rts,		 (int)0					},
	{	IOCTL_SERIAL_GET_DTRRTS,	ioctl_serial_get_dtrrts,	-(int)sizeof(int)			},
//	{	IOCTL_SERIAL_SET_XON,		ioctl_serial_set_xon,							},
//	{	IOCTL_SERIAL_SET_XOFF,		ioctl_serial_set_xoff,							},
//	{	IOCTL_SERIAL_XOFF_COUNTER,	ioctl_serial_xoff_counter,						},
	{	IOCTL_SERIAL_PURGE,		ioctl_serial_purge,		 (int)sizeof(int)			},
//	{	IOCTL_SERIAL_RESET_DEVICE,	ioctl_serial_reset_device,						},
//	{	IOCTL_SERIAL_SET_BREAK_ON,	ioctl_serial_set_break_on,						},
//	{	IOCTL_SERIAL_SET_BREAK_OFF,	ioctl_serial_set_break_off,						},
	{	IOCTL_SERIAL_SET_LINE_CONTROL,	ioctl_serial_set_line_control,	 (int)sizeof(SERIAL_LINE_CONTROL)	},
	{	IOCTL_SERIAL_GET_LINE_CONTROL,	ioctl_serial_get_line_control,	-(int)sizeof(SERIAL_LINE_CONTROL)	},
	{	IOCTL_SERIAL_SET_BAUD_RATE,	ioctl_serial_set_baud_rate,	 (int)sizeof(SERIAL_BAUD_RATE)		},
	{	IOCTL_SERIAL_GET_BAUD_RATE,	ioctl_serial_get_baud_rate,	-(int)sizeof(SERIAL_BAUD_RATE)		},
	{	IOCTL_SERIAL_SET_HANDFLOW,	ioctl_serial_set_handflow,	 (int)sizeof(SERIAL_HANDFLOW)		},
	{	IOCTL_SERIAL_GET_HANDFLOW,	ioctl_serial_get_handflow,	-(int)sizeof(SERIAL_HANDFLOW)		},
	{	IOCTL_SERIAL_SET_TIMEOUTS,	ioctl_serial_set_timeouts,	 (int)sizeof(SERIAL_TIMEOUTS)		},
	{	IOCTL_SERIAL_GET_TIMEOUTS,	ioctl_serial_get_timeouts,	-(int)sizeof(SERIAL_TIMEOUTS)		},
	{	IOCTL_SERIAL_SET_CHARS,		ioctl_serial_set_chars,		 (int)sizeof(SERIAL_CHARS)		},
	{	IOCTL_SERIAL_GET_CHARS,		ioctl_serial_get_chars,		-(int)sizeof(SERIAL_CHARS)		},
//	{	IOCTL_SERIAL_IMMEDIATE_CHAR,	ioctl_serial_immediate_char,						},
	{	IOCTL_SERIAL_GET_STATS,		ioctl_serial_get_stats,		-(int)sizeof(SERIALPERF_STATS)		},
	{	IOCTL_SERIAL_CLEAR_STATS,	ioctl_serial_clear_stats,	 (int)0					},
	{	IOCTL_SERIAL_SET_WAIT_MASK,	ioctl_serial_set_wait_mask,	 (int)sizeof(int)			},
	{	IOCTL_SERIAL_GET_WAIT_MASK,	ioctl_serial_get_wait_mask,	-(int)sizeof(int)			},
	{	IOCTL_SERIAL_WAIT_ON_MASK,	ioctl_serial_wait_on_mask,	-(int)sizeof(int)			},
//	{	IOCTL_SERIAL_LSRMST_INSERT,	ioctl_serial_lsrmst_insert,						},
//	{	IOCTL_SERIAL_CONFIG_SIZE,	ioctl_serial_config_size,						},
//	{	IOCTL_SERIAL_GET_COMMCONFIG,	ioctl_serial_get_commconfig,						},
//	{	IOCTL_SERIAL_SET_COMMCONFIG,	ioctl_serial_set_commconfig,						},
//	{	IOCTL_SERIAL_GET_MODEM_CONTROL,	ioctl_serial_get_modem_control,						},
//	{	IOCTL_SERIAL_SET_MODEM_CONTROL,	ioctl_serial_set_modem_control,						},
//	{	IOCTL_SERIAL_SET_FIFO_CONTROL,	ioctl_serial_set_fifo_control,						},
};

NTSTATUS
dispatch_ioctl(DEVICE_OBJECT* device_object, IRP* irp)
{
	IO_STACK_LOCATION* stack_location = IoGetCurrentIrpStackLocation(irp);
	//
	void* system_buffer      = irp->AssociatedIrp.SystemBuffer;
	int ioctl_code           = stack_location->Parameters.DeviceIoControl.IoControlCode;
	int input_buffer_length  = stack_location->Parameters.DeviceIoControl.InputBufferLength;
	int output_buffer_length = stack_location->Parameters.DeviceIoControl.OutputBufferLength;
	//
	int i;
	int length;
	NTSTATUS status;

#ifndef QUIET
	KdPrint(("dispatch_ioctl - %s\n", serial_ioctl_name(ioctl_code)));
#endif /*QUIET*/

	/* IOCTLコードに一致するエントリを検索します。 */
	for(i = 0; i < sizeof ioctl_table / sizeof *ioctl_table; i++) {
		if(ioctl_table[i].code == ioctl_code) {

			/* 入出力を伴うIOCTLならば、バッファサイズを検査します。 */
			length = ioctl_table[i].length;
			if(length < 0) { /* out */
				length = -length; /* 出力IOCTLは、出力バイト数を返す */
				if(output_buffer_length < length) {
					KdPrint(("Buffer too small"));
					return complete_request(irp, STATUS_BUFFER_TOO_SMALL, 0);
				}
			} else if(length > 0) { /* in */
				if(input_buffer_length < length) {
					KdPrint(("Buffer too small"));
					return complete_request(irp, STATUS_BUFFER_TOO_SMALL, 0);
				}
				length = 0; /* 入力IOCTLは、0を返す */
			}

			/* IOCTLを実行します。 */
			status = ((NTSTATUS (*)(DEVICE_OBJECT*, IRP*, void*))ioctl_table[i].ioctl)(device_object, irp, system_buffer);
			if(status != STATUS_PENDING) {
				if(!NT_SUCCESS(status)) {
					length = 0; /* 失敗ならば、0を返す */
				}
				complete_request(irp, status, length);
			}
			return status;
		}
	}

	KdPrint(("### Unsupported IOCTL ###\n"));
	return complete_request(irp, STATUS_INVALID_PARAMETER/*仕様*/, 0);
}

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

void
complete_wait_on_mask(IRP* irp, int wait_mask)
{
#ifndef QUIET
	KdPrint(("complete_wait_on_mask\n"));
#endif /*QUIET*/
	*(int*)irp->AssociatedIrp.SystemBuffer = wait_mask;
	complete_request(irp, STATUS_SUCCESS, sizeof(int));
}

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

void
assert_comm_event(DEVICE_OBJECT* device_object, int comm_event)
{
	DEVICE_EXTENSION* device_extension = device_object->DeviceExtension;
	//
	IRP* wait_on_mask_irp;

ENTER_CS;

	/* 新しいイベントを、イベント履歴に追加します。 */
	device_extension->comm_event |= (comm_event & device_extension->wait_mask);

	/* 有効なイベントが発生したら... */
	if(device_extension->comm_event) {

		/* WaitOnMask IRPを取り出します。(あれば) */
		wait_on_mask_irp = device_extension->wait_on_mask_irp;
		device_extension->wait_on_mask_irp = NULL;
		if(wait_on_mask_irp) {
			IoSetCancelRoutine(wait_on_mask_irp, NULL);

			/* イベント履歴を取り出し、クリアします。 */
			comm_event = device_extension->comm_event;
				     device_extension->comm_event = 0;
		}
	} else {
		wait_on_mask_irp = NULL;
	}

LEAVE_CS;

	/* 必要に応じて、WaitOnMask IRPを完了します。 */
	if(wait_on_mask_irp) {
		complete_wait_on_mask(wait_on_mask_irp, comm_event);
	}
}

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

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

	ASSERT(device_extension->wait_on_mask_irp == irp);
	device_extension->wait_on_mask_irp = NULL;

	/* IoCancelIrp()が獲得したスピンロックを解放します。 */
	IoReleaseCancelSpinLock(irp->CancelIrql);

	/* IRPを完了させます。 */
	complete_request(irp, STATUS_CANCELLED, 0);
}

