PIRP 
    IoBuildDeviceIoControlRequest(
        IN ULONG  IoControlCode,
        IN PDEVICE_OBJECT  DeviceObject,
        IN PVOID  InputBuffer,        /* optional */
        IN ULONG  InputBufferLength,
        IN OUT PVOID  OutputBuffer,        /* optional */
        IN ULONG  OutputBufferLength,
        IN BOOLEAN  InternalDeviceIoControl,
        IN PKEVENT  Event,
        OUT PIO_STATUS_BLOCK  IoStatusBlock
        );
IoBuildDeviceIoControlRequest allocates and sets up an IRP for a device control request, optionally with an I/O buffer if the I/O control code requires the caller to supply an input or output buffer.
IoBuildDeviceIoControlRequest returns a pointer to an IRP with the next-lower driver’s I/O stack location partially set up from the supplied parameters. The returned pointer is NULL if an IRP cannot be allocated.
An intermediate or highest-level driver can call IoBuildDeviceIoControlRequest to set up IRPs for requests sent to lower-level drivers. The next-lower driver’s I/O stack location is set up with the given IoControlCode at Parameters.DeviceIoControl.IoControlCode. Because the caller can wait on the completion of this driver-allocated IRP by calling KeWaitForSingleObject on the given Event, the caller need not set an IoCompletion routine in the IRP before calling IoCallDriver. When the next-lower driver completes this IRP, the I/O Manager releases it.
Callers of IoBuildDeviceIoControlRequest must be running at IRQL PASSIVE_LEVEL.
IO_STACK_LOCATION, IoAllocateIrp, IoBuildAsynchronousFsdRequest, IoBuildSynchronousFsdRequest, IoCallDriver, IoCompleteRequest, IRP, KeInitializeEvent, KeWaitForSingleObject