Figure 5.3 shows how TDI clients set up and submit TDI_XXX IOCTL requests to their underlying TDI transports.
Figure 5.3 Preparing an IOCTL IRP and Making a Request
A kernel-mode client prepares its own IOCTL IRPs for communication with the underlying transport driver, using one of the TdiBuildXxx macros summarized in Chapter 4. The client can either obtain an IRP from a still higher layer of the network or call TdiBuildInternalDeviceControlIrp to allocate an IRP for itself.
A subsequent call to a TdiBuildXxx macro sets the appropriate TDI_XXX code in the MinorFunctionCode of the IRP and sets the MajorFunctionCode to IRP_MJ_INTERNAL_DEVICE_CONTROL for the underlying transport driver, as well as setting the client-supplied and IOCTL-specific parameters to each TdiBuildXxx macro in the IRP. For example, to set up the TDI_ASSOCIATE_ADDRESS request, mentioned in the preceding section, the client passes both a pointer to the file object that represents its connection endpoint and the handle to the file object that represents its transport address when it uses TdiBuildAccept.
When the IRP is set up, the client calls IoCallDriver to submit its IOCTL request to its underlying transport. The transport's TdiDispatchInternalDeviceControl routine receives the client-supplied IRP from IoCallDriver and usually forwards the client's request to an internal driver function for further processing. The transport completes the requested operation, sets the I/O status block in the IRP with the results of the operation, and calls TdiCompleteRequest (or IoCompleteRequest) with the IRP when the client's request has been satisfied.
If the client supplied the entry point of its IoCompletion routine when it called the TdiBuildXxx macro, the client's IoCompletion routine is called when the transport completes the requested operation and calls Io/TdiCompleteRequest with the client-supplied TDI_XXX IOCTL request.