(Please excuse the recycled bits. The 7-month-old is still absorbing most of my blogging time (along with sleeping time, dating time, playing time, cleaning time, working time, etc...))
A question came up on the ntdev mailing list about why there are "so many" ways to send an I/O request to a SCSI driver.
In matter of fact there are two - IRP_MJ_SCSI and IOCTL_SCSI_PASS_THROUGH (and it's direct mapped variant).
IRP_MJ_SCSI == IRP_MJ_INTERNAL_DEVICE_CONTROL. This is easily determined by looking at the headers. No SCSI shouldn't have its own IRP MJ code, and no it shouldn't reuse a number from an existing one. But it's been that way since I joined MS (and I tried to change it once ... not possible without breaking everyone doing storage) and so we all just have to live with it. Someone decided we needed a MJ code for SCSI requests. I suspect IRP_MJ_INTERNAL_DEVICE_CONTROL was reused to avoid the cost of an additional PVOID per driver object (which at the time would have been an issue).
IOCTL_SCSI_EXECUTE_* is set in the stack location for an IRP_MJ_SCSI to (a) allow someone handling IRP_MJ_INTERNAL_DEVICE_CONTROL to differentiate between the two and (b) to provide some indicator of which direction the SRB is transferring data. Its use isn't particularly consistent ... I think of it as more of a debugging aid but I see that some of our drivers (like the RAMDISK driver) use it to decide if the SRB in question is a read/write type command or one of those strange SCSI commands like REQUEST_SENSE which require more complex emulation. I think using the CDB code would have been a much better idea.
IRP_MJ_SCSI comes with an SRB.
IOCTL_SCSI_PASS_THROUGH and IOCTL_SCSI_PASS_THROUGH_DIRECT are to send SCSI requests from user-mode. They come with SCSI_PASS_THROUGH/SCSI_PASS_THROUGH_DIRECT structures so as not to expose SRB to user-mode and so as to avoid validating SRBs which might come from user-mode (besides, how is a user mode component going to provide a value like OriginalIrp).
IOCTL_SCSI_PASS_THROUGH is a buffered operation, while IOCTL_SCSI_PASS_THROUGH_DIRECT direct maps the data transfer. They're separate control codes because they are - I suppose a flag in the PASS_THROUGH structure could have contained the transfer mode ... would that make you happier?
So there are two ways to send a SCSI request. One for kernel-mode components (IRP_MJ_SCSI) and one for user-mode components (IOCTL_SCSI_PASS_THROUGH[_DIRECT]). If you want to convert a pass-through command to a IRP_MJ_SCSI command it's pretty straight-forward to make an SRB and send it down.