(转)驱动开发之五 --- TDI之六 【译文】
http://hi.baidu.com/combojiang/item/d0287ca509180ddf5bf19132
同样的使用TDI_RECEIVE来完成数据接收。然而我们却没有用它来实现。实际上,如果你注意到,你可以创建回调来通知你数据或者其他事件什么时候到达。这就是我们所做的。我实现了一个API包装函数来创建任意的事件句柄。如下:
NTSTATUS TdiFuncs_SetEventHandler(PFILE_OBJECT pfoTdiFileObject,
LONG InEventType, PVOID InEventHandler, PVOID InEventContext)
{
NTSTATUS NtStatus = STATUS_INSUFFICIENT_RESOURCES;
PIRP pIrp;
IO_STATUS_BLOCK IoStatusBlock = {0};
PDEVICE_OBJECT pTdiDevice;
LARGE_INTEGER TimeOut = {0};
UINT NumberOfSeconds = 60*3;
TDI_COMPLETION_CONTEXT TdiCompletionContext;
KeInitializeEvent(&TdiCompletionContext.kCompleteEvent,
NotificationEvent, FALSE);
/*
* The TDI Device Object is required to send these
* requests to the TDI Driver.
*/
pTdiDevice = IoGetRelatedDeviceObject(pfoTdiFileObject);
/*
* Step 1: Build the IRP. TDI defines several macros and functions
* that can quickly create IRP's, etc. for variuos purposes.
* While this can be done manually it's easiest to use the macros.
*
*/
pIrp = TdiBuildInternalDeviceControlIrp(TDI_SET_EVENT_HANDLER,
pTdiDevice, pfoConnection, &TdiCompletionContext.kCompleteEvent,
&IoStatusBlock);
if(pIrp)
{
/*
* Step 2: Set the IRP Parameters
*/
TdiBuildSetEventHandler(pIrp, pTdiDevice, pfoTdiFileObject,
NULL, NULL, InEventType, InEventHandler, InEventContext);
NtStatus = IoCallDriver(pTdiDevice, pIrp);
/*
* If the status returned is STATUS_PENDING this means that
* the IRP will not be completed synchronously and the driver has
* queued the IRP for later processing. This is fine but we do not
* want to return this thread, we are a synchronous call so we want
* to wait until it has completed. The EVENT that we provided
* will be set when the IRP completes.
*/
if(NtStatus == STATUS_PENDING)
{
KeWaitForSingleObject(&TdiCompletionContext.kCompleteEvent,
Executive, KernelMode, FALSE, NULL);
/*
* Find the Status of the completed IRP
*/
NtStatus = IoStatusBlock.Status;
}
}
return NtStatus;
}
使用这个函数实现回调的代码如下:
Collapse
NtStatus = TdiFuncs_SetEventHandler(
pTdiExampleContext->TdiHandle.pfoTransport,
TDI_EVENT_RECEIVE,
TdiExample_ClientEventReceive,
(PVOID)pTdiExampleContext);
...
NTSTATUS TdiExample_ClientEventReceive(PVOID TdiEventContext,
CONNECTION_CONTEXT ConnectionContext,
ULONG ReceiveFlags,
ULONG BytesIndicated,
ULONG BytesAvailable,
ULONG *BytesTaken,
PVOID Tsdu,
PIRP *IoRequestPacket)
{
NTSTATUS NtStatus = STATUS_SUCCESS;
UINT uiDataRead = 0;
PTDI_EXAMPLE_CONTEXT pTdiExampleContext =
(PTDI_EXAMPLE_CONTEXT)TdiEventContext;
PIRP pIrp;
DbgPrint("TdiExample_ClientEventReceive 0x%0x, %i, %i\n",
ReceiveFlags, BytesIndicated, BytesAvailable);
*BytesTaken = BytesAvailable;
/*
* This implementation is extremely simple. We do not queue
* data if we do not have an IRP to put it there. We also
* assume we always get the full data packet sent every recieve.
* These are Bells and Whistles that can easily be added to
* any implementation but would help to make the implementation
* more complex and harder to follow the underlying idea. Since
* those essentially are common-sense add ons they are ignored and
* the general implementation of how to Queue IRP's and
* recieve data are implemented.
*
*/
pIrp = HandleIrp_RemoveNextIrp(pTdiExampleContext->pReadIrpListHead);
if(pIrp)
{
PIO_STACK_LOCATION pIoStackLocation =
IoGetCurrentIrpStackLocation(pIrp);
uiDataRead =
BytesAvailable > pIoStackLocation->Parameters.Read.Length ?
pIoStackLocation->Parameters.Read.Length : BytesAvailable;
pIrp->Tail.Overlay.DriverContext[0] = NULL;
RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer, Tsdu, uiDataRead);
pIrp->IoStatus.Status = NtStatus;
pIrp->IoStatus.Information = uiDataRead;
IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
}
/*
* The I/O Request can be used to recieve the rest of the data.
* We are not using it in this example however and will actually
* be assuming that we always get all the data.
*
*/
*IoRequestPacket = NULL;
return NtStatus;
}
不要对HandleIrp_RemoveNextIrp产生恐惧,我们会在本篇后面部分讲怎样排队IRP请求。