#include <nt.h>
#include <ntddscsi.h>
#include <ntdddisk.h>
#include <ntddcdrm.h>
#include <ntrtl.h>
#include <nturtl.h>

#include <windows.h>
#include <stdio.h>
#include <process.h>
#include <memory.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>

#define _NTSCSI_USER_MODE_

#include <scsi.h>

VOID
GetDriverName(
    IN ULONG PortNumber
    )
{
    UNICODE_STRING name;
    UNICODE_STRING unicodeString;
    ANSI_STRING ansiString;
    HANDLE key;
    HANDLE portKey;
    OBJECT_ATTRIBUTES objectAttributes;
    NTSTATUS status;
    UCHAR buffer[64];
    ULONG length;
    PKEY_VALUE_FULL_INFORMATION keyData = (PKEY_VALUE_FULL_INFORMATION)buffer;

    printf("\nSCSI PORT %d\n", PortNumber);

    //
    // Obtain handle to SCSI path in device map.
    //

    RtlInitUnicodeString(&name,
                         L"\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi");

    //
    // Initialize the object for the key.
    //

    InitializeObjectAttributes(&objectAttributes,
                               &name,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               (PSECURITY_DESCRIPTOR) NULL);

    //
    // Open the key.
    //

    status = NtOpenKey(&key,
                       KEY_READ,
                       &objectAttributes);

    if (!NT_SUCCESS(status)) {
        return;
    }

    //
    // Create Scsi port name.
    //

    sprintf(buffer,
            "Scsi Port %d",
            PortNumber);

    RtlInitString(&ansiString, buffer);

    status = RtlAnsiStringToUnicodeString(&unicodeString,
                                          &ansiString,
                                          TRUE);

    if (!NT_SUCCESS(status)) {
        return;
    }

    InitializeObjectAttributes( &objectAttributes,
                                &unicodeString,
                                OBJ_CASE_INSENSITIVE,
                                key,
                                (PSECURITY_DESCRIPTOR) NULL );

    status = NtOpenKey(&portKey,
                       KEY_READ,
                       &objectAttributes);

    if (!NT_SUCCESS(status)) {
        return;
    }

    RtlInitUnicodeString(&name,
                         L"Driver");

    status = NtQueryValueKey(portKey,
                             &name,
                             KeyValueFullInformation,
                             keyData,
                             64,
                             &length);

    if (!NT_SUCCESS(status)) {
        return;
    }

    printf("Driver name: %S\n",
           (PUCHAR)keyData + keyData->DataOffset);

    RtlInitUnicodeString(&name,
                         L"Interrupt");

    status = NtQueryValueKey(portKey,
                             &name,
                             KeyValueFullInformation,
                             keyData,
                             64,
                             &length);

    if (!NT_SUCCESS(status)) {
        return;
    }

    printf("IRQ %d ",
           *((PUCHAR)keyData + keyData->DataOffset));

    RtlInitUnicodeString(&name,
                         L"IOAddress");

    status = NtQueryValueKey(portKey,
                             &name,
                             KeyValueFullInformation,
                             keyData,
                             64,
                             &length);

    if (!NT_SUCCESS(status)) {
        printf("\n");
        return;
    }

    printf("IO Address %x\n",
           *((PULONG)keyData + keyData->DataOffset/4));

    return;
}

int __cdecl
main( int argc, char **argv )
{
    BYTE buffer[32];
    HANDLE volumeHandle;
    STRING string;
    UNICODE_STRING unicodeString;
    OBJECT_ATTRIBUTES  objectAttributes;
    NTSTATUS ntStatus;
    IO_STATUS_BLOCK statusBlock;
    ULONG portNumber = 0;
    PSCSI_ADAPTER_BUS_INFO  adapterInfo;
    PSCSI_BUS_DATA busData;
    PSCSI_INQUIRY_DATA inquiryData;
    UCHAR prevDeviceInquiryData[INQUIRYDATABUFFERSIZE];
    PINQUIRYDATA deviceInquiryData;
    ULONG bytesTransferred, i, j;
    ULONG deviceNumber;
    BOOLEAN newDisk = FALSE;
    BOOLEAN newCdrom = FALSE;
    UCHAR prevPathId;
    UCHAR prevTargetId;
    UCHAR prevLun;
    BOOLEAN prevDeviceClaimed;
    BOOLEAN listAdapters = FALSE;
    BOOLEAN allAdapters = TRUE;
    UCHAR lunExtra;

    if(argc == 2) {
        if(argv[1][0] == '*') {
            listAdapters = TRUE;
        } else {
            portNumber = atoi(argv[1]);
            allAdapters = FALSE;
        }
    }

    printf("\nWindows NT SCSI Bus Rescan Version 1.1\n");

    if(listAdapters) {
        printf("[only listing adapters]\n");
    } else if(allAdapters) {
        printf("[scanning all adapters]\n");
    } else {
        printf("[scanning adapter %d only]\n", portNumber);
    }

    while (TRUE) {

        memset( buffer, 0, sizeof( buffer ) );
        sprintf( buffer,
                 "\\\\.\\Scsi%d:",
                 portNumber);

        //
        // Open the volume with the DOS name.
        //

        volumeHandle = CreateFile( buffer,
                                   GENERIC_READ,
                                   FILE_SHARE_READ | FILE_SHARE_WRITE,
                                   NULL,
                                   OPEN_EXISTING,
                                   0,
                                   0 );

        if( volumeHandle == INVALID_HANDLE_VALUE ) {
            break;
        }

        if(listAdapters) {
            GetDriverName(portNumber);
            portNumber++;
            CloseHandle(volumeHandle);
            continue;
        }

        //
        // Issue rescan device control.
        //

        if( !DeviceIoControl( volumeHandle,
                              IOCTL_SCSI_RESCAN_BUS,
                              NULL,
                              0,
                              NULL,
                              0,
                              &bytesTransferred,
                              NULL ) ) {

            printf( "Rescan SCSI port %d failed [Error %d].\n", portNumber, GetLastError() );
            CloseHandle( volumeHandle );
            exit(4);
        }

        //
        // Get a big chuck of memory to store the SCSI bus data.
        //

        adapterInfo = malloc( 0x1000 );

        if (adapterInfo == NULL) {
            printf( "Can't allocate memory for bus data\n" );
            CloseHandle( volumeHandle );
            exit(4);
        }

        //
        // Issue device control to get configuration information.
        //

        if (!DeviceIoControl( volumeHandle,
                              IOCTL_SCSI_GET_INQUIRY_DATA,
                              NULL,
                              0,
                              adapterInfo,
                              0x1000,
                              &bytesTransferred,
                              NULL)) {

            printf( "Get SCSI bus data failed [Error %d].\n", GetLastError() );
            CloseHandle( volumeHandle );
            exit(4);
        }

        GetDriverName(portNumber);

        //
        // Display devices on buses.
        //

        for (i=0; i < adapterInfo->NumberOfBuses; i++) {

            busData = &adapterInfo->BusData[i];
            printf( "\nBus  TID  LUN  In use  Type        Vendor                 FW Rev  Advanced SCSI\n" );
            printf( "===============================================================================\n" );
            printf("%2d   %2d   %2d     %2d    Initiator",
                   i,
                   busData->InitiatorBusId & 0x7,
                   0,
                   1);

            inquiryData =
                (PSCSI_INQUIRY_DATA)((PUCHAR)adapterInfo + busData->InquiryDataOffset);

            memset(&prevDeviceInquiryData, 0, INQUIRYDATABUFFERSIZE);
            prevPathId = 0xFF;
            prevTargetId = 0xFF;
            prevLun = 0xFF;
            prevDeviceClaimed = 0xFF;
            for (j=0; j<busData->NumberOfLogicalUnits; j++) {

                int k;

                //
                // Make sure VendorId string is null terminated.
                //

                deviceInquiryData = (PINQUIRYDATA)&inquiryData->InquiryData[0];

                deviceInquiryData->VendorSpecific[0] = '\0';
                if (prevPathId != inquiryData->PathId ||
                    prevTargetId != inquiryData->TargetId ||
                    prevLun != (inquiryData->Lun-1) ||
                    prevDeviceClaimed != inquiryData->DeviceClaimed ||
                    memcmp( &prevDeviceInquiryData, deviceInquiryData, INQUIRYDATABUFFERSIZE)
                   ) {
                    lunExtra = 0;
                    printf("\n%2d   %2d   %2d     %2d    ",
                            inquiryData->PathId,
                            inquiryData->TargetId,
                            inquiryData->Lun,
                            inquiryData->DeviceClaimed);
                } else {
                    lunExtra += 1;
                    printf("\r%2d   %2d   %2d-%1d   %2d    ",
                            inquiryData->PathId,
                            inquiryData->TargetId,
                            inquiryData->Lun-lunExtra,
                            inquiryData->Lun,
                            inquiryData->DeviceClaimed);
                    }

                prevPathId = inquiryData->PathId;
                prevTargetId = inquiryData->TargetId;
                prevLun = inquiryData->Lun;
                prevDeviceClaimed = inquiryData->DeviceClaimed;
                memmove( &prevDeviceInquiryData, deviceInquiryData, INQUIRYDATABUFFERSIZE);

                //
                // Determine the perpherial type.
                //

                switch (deviceInquiryData->DeviceType) {
                case DIRECT_ACCESS_DEVICE:
                    if (!inquiryData->DeviceClaimed) {
                        newDisk = TRUE;
                    }
                    printf("Disk Drive ");
                    break;

                case SEQUENTIAL_ACCESS_DEVICE:
                    printf("Tape Drive ");
                    break;

                case PRINTER_DEVICE:
                    printf("Printer    ");
                    break;

                case WRITE_ONCE_READ_MULTIPLE_DEVICE:
                    printf("Worm Drive ");
                    break;

                case READ_ONLY_DIRECT_ACCESS_DEVICE:
                    if (!inquiryData->DeviceClaimed) {
                        newCdrom = TRUE;
                    }
                    printf("CdRom Drive");
                    break;

                case SCANNER_DEVICE:
                    printf("Scanner    ");
                    break;

                case OPTICAL_DEVICE:
                    if (!inquiryData->DeviceClaimed) {
                        newDisk = TRUE;
                    }
                    printf("OpticalDisk");
                    break;

                case MEDIUM_CHANGER:
                    printf("MediumChanger");
                    break;

                case COMMUNICATION_DEVICE:
                    printf("Communication");
                    break;

                default:
                    printf("OtherPeripheral");
                }

                //
                // Display product information.
                //

                printf(" %s", deviceInquiryData->VendorId);

                //
                // Display SCSI capabilities.
                //

                printf("   ");
                if (deviceInquiryData->Synchronous) {
                    printf(" SN");
                }

                if (deviceInquiryData->CommandQueue) {
                    printf(" CQ");
                }

                if (deviceInquiryData->Wide16Bit) {
                    printf(" W16");
                }

                if (deviceInquiryData->Wide32Bit) {
                    printf(" W32");
                }

                if (deviceInquiryData->SoftReset) {
                    printf(" SR");
                }

                if (deviceInquiryData->LinkedCommands) {
                    printf(" LC");
                }

                if (deviceInquiryData->RelativeAddressing) {
                    printf(" RA");
                }

                if (deviceInquiryData->DeviceTypeQualifier != DEVICE_QUALIFIER_ACTIVE) {
                    printf(" DQ%d", deviceInquiryData->DeviceTypeQualifier);
                }

                printf("\n                        [ ");
                for(k = 0; k < 8; k++) {
                    printf("%02x ", ((PUCHAR) deviceInquiryData)[k]);
                }
                printf("]");

                //
                // Get next device data.
                //

                inquiryData =
                    (PSCSI_INQUIRY_DATA)((PUCHAR)adapterInfo + inquiryData->NextInquiryDataOffset);
            }

            printf("\n");
        }

        free (adapterInfo);

        if(allAdapters) {
            CloseHandle( volumeHandle );
            portNumber++;
        } else {
            break;
        }
    }

    if (newDisk) {

        //
        // Send IOCTL_DISK_FIND_NEW_DEVICES commands to each existing disk.
        //

        deviceNumber = 0;
        while (TRUE) {

            memset(buffer, 0, sizeof(buffer));
            sprintf(buffer,
                    "\\Device\\Harddisk%d\\Partition0",
                    deviceNumber);

            RtlInitString(&string,
                          buffer);

            ntStatus = RtlAnsiStringToUnicodeString(&unicodeString,
                                                    &string,
                                                    TRUE);

            if (!NT_SUCCESS(ntStatus)) {
                continue;
            }

            InitializeObjectAttributes(&objectAttributes,
                                       &unicodeString,
                                       0,
                                       NULL,
                                       NULL);

            ntStatus = NtOpenFile(&volumeHandle,
                                  FILE_READ_DATA  |
                                  FILE_WRITE_DATA |
                                  SYNCHRONIZE,
                                  &objectAttributes,
                                  &statusBlock,
                                  FILE_SHARE_READ  |
                                  FILE_SHARE_WRITE,
                                  FILE_SYNCHRONOUS_IO_ALERT);

            if (!NT_SUCCESS(ntStatus)) {
                break;
            }

            //
            // Issue find device device control.
            //

            if (DeviceIoControl( volumeHandle,
                                 IOCTL_DISK_FIND_NEW_DEVICES,
                                 NULL,
                                 0,
                                 NULL,
                                 0,
                                 &bytesTransferred,
                                 NULL ) ) {

                printf( "Found new disk (%d)\n", deviceNumber );
            }

            CloseHandle( volumeHandle );
            deviceNumber++;
        }
    }

    if (newCdrom) {

        //
        // Send IOCTL_CDROM_FIND_NEW_DEVICES commands to each existing cdrom.
        //

        deviceNumber = 0;
        while (TRUE) {

            memset(buffer, 0, sizeof(buffer));
            sprintf(buffer,
                    "\\Device\\Cdrom%d",
                    deviceNumber);

            RtlInitString(&string,
                          buffer);

            ntStatus = RtlAnsiStringToUnicodeString(&unicodeString,
                                                    &string,
                                                    TRUE);

            if (!NT_SUCCESS(ntStatus)) {
                continue;
            }

            InitializeObjectAttributes(&objectAttributes,
                                       &unicodeString,
                                       0,
                                       NULL,
                                       NULL);

            ntStatus = NtOpenFile(&volumeHandle,
                                  FILE_READ_DATA  |
                                  FILE_WRITE_DATA |
                                  SYNCHRONIZE,
                                  &objectAttributes,
                                  &statusBlock,
                                  FILE_SHARE_READ  |
                                  FILE_SHARE_WRITE,
                                  FILE_SYNCHRONOUS_IO_ALERT);

            if (!NT_SUCCESS(ntStatus)) {
                break;
            }

            //
            // Issue find device device control.
            //

            if (DeviceIoControl( volumeHandle,
                                 IOCTL_CDROM_FIND_NEW_DEVICES,
                                 NULL,
                                 0,
                                 NULL,
                                 0,
                                 &bytesTransferred,
                                 NULL ) ) {

                printf( "Found new cdrom (%d)\n", deviceNumber );
            }

            CloseHandle( volumeHandle );
            deviceNumber++;
        }
    }

    return(0);
}