#include #include #include #include #include #include //#include //#include #include #include #include #include #include #include #include "helper.h" #define DEFAULT_REMOVAL_LOCK_TIMEOUT 20 #define MOV_DW_SWP(a,b) \ do \ { \ *(unsigned short *)&(a) = _byteswap_ushort(*(unsigned short *)&(b)); \ } \ while (0) #define MOV_DD_SWP(a,b) \ { \ PFOUR_BYTE _from_, _to_; \ _from_ = ((PFOUR_BYTE)&(b)); \ _to_ = ((PFOUR_BYTE)&(a)); \ __asm mov ebx,_from_ \ __asm mov eax,[ebx] \ __asm bswap eax \ __asm mov ebx,_to_ \ __asm mov [ebx],eax \ } int g_extended = 0; int g_adapter_info = 0; char* g_bb_list = NULL; int gRadix = 16; PADAPTERINFO g_AdapterInfo = NULL; BOOLEAN ata_power_mode( int bus_id, int dev_id, int power_mode ); void print_help() { printf("Usage:\n" " atactl - c|s:b:d[:l]\n" "Switches:\n" " l (L)ist devices on SCSI and ATA controllers bus(es)\n" " Note: ATA Pri/Sec controller are usually represented\n" " as Scsi0/Scsi1 under NT-family OSes\n" " x show e(X)tended info\n" " a show (A)dapter info\n" " s (S)can for new devices on ATA/SATA bus(es) (experimental)\n" " S (S)can for new devices on ATA/SATA bus(es) (experimental)\n" " device, hidden with 'H' can be redetected\n" " h (H)ide device on ATA/SATA bus for removal (experimental)\n" " device can be redetected\n" " H (H)ide device on ATA/SATA bus (experimental)\n" " device can not be redetected until 'h' or 'S' is issued\n" " m [MODE] set i/o (M)ode for device or revert to default\n" " available MODEs are PIO, PIO0-PIO5, DMA, WDMA0-WDMA2,\n" " UDMA33/44/66/100/133, UDMA0-UDMA5\n" " d [XXX] lock ATA/SATA bus for device removal for XXX seconds or\n" " for %d seconds if no lock timeout specified.\n" " can be used with -h, -m or standalone.\n" " D [XXX] disable device (turn into sleep mode) and lock ATA/SATA bus \n" " for device removal for XXX seconds or\n" " for %d seconds if no lock timeout specified.\n" " can be used with -h, -m or standalone.\n" " pX change power state to X, where X is\n" " 0 - active, 1 - idle, 2 - standby, 3 - sleep\n" " r (R)eset device\n" " ba (A)ssign (B)ad-block list\n" " bl get assigned (B)ad-block (L)ist\n" " br (R)eset assigned (B)ad-block list\n" " f specify (F)ile for bad-block list\n" " n XXX block (n)ubmering radix. XXX can be hex or dec\n" "------\n" "Examples:\n" " atactl -l\n" " will list all scsi buses and all connected devices\n" " atactl -m udma0 s2:b1:d1\n" " will switch device at Scsi2, bus 1, taget_id 1 to UDMA0 mode\n" " atactl -h -d 30 c1:b0:d0:l0 \n" " will hide Master (d0:l0) device on secondary (c1:b0) IDE channel\n" " and lock i/o on this channel for 30 seconds to ensure safety\n" " of removal process" "------\n" "Device address format:\n" "\n" "s number of controller in system. Is assigned during hardware\n" " detection. Usually s0/s1 are ATA Pri/Sec.\n" " Note, due do NT internal design ATA controllers are represented\n" " as SCSI controllers.\n" "b For ATA controllers it is channel number.\n" " Note, usually onboard controller is represented as 2 legacy\n" " ISA-compatible single-channel controllers (Scsi9/Scsi1). Additional\n" " ATA, ATA-RAID and some specific onboard controllers are represented\n" " as multichannel controllers.\n" "d For ATA controllers d0 is Master, d1 is Slave.\n" "l Not used in ATA controller drivers, alway 0\n" "------\n" "Bad-block list format:\n" "\n" "# Comment\n" "; Still one comment\n" "hex: switch to hexadecimal mode\n" " \n" " \n" "...\n" "dec: switch to decimal mode\n" " \n" "...\n" "------\n" "", DEFAULT_REMOVAL_LOCK_TIMEOUT, DEFAULT_REMOVAL_LOCK_TIMEOUT ); exit(0); } #define CMD_ATA_LIST 0x01 #define CMD_ATA_FIND 0x02 #define CMD_ATA_HIDE 0x03 #define CMD_ATA_MODE 0x04 #define CMD_ATA_RESET 0x05 #define CMD_ATA_BBLK 0x06 #define CMD_ATA_POWER 0x07 HANDLE ata_open_dev( char* Name ) { ULONG i; HANDLE h; for(i=0; i<4; i++) { h = CreateFile(Name, READ_CONTROL | GENERIC_READ | GENERIC_WRITE , ((i & 1) ? 0 : FILE_SHARE_READ) | ((i & 2) ? 0 : FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(h && (h != ((HANDLE)(-1))) ) { return h; } } for(i=0; i<4; i++) { h = CreateFile(Name, GENERIC_READ | GENERIC_WRITE , ((i & 1) ? 0 : FILE_SHARE_READ) | ((i & 2) ? 0 : FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(h && (h != ((HANDLE)(-1))) ) { return h; } } for(i=0; i<4; i++) { h = CreateFile(Name, GENERIC_READ, ((i & 1) ? 0 : FILE_SHARE_READ) | ((i & 2) ? 0 : FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(h && (h != ((HANDLE)(-1))) ) { return h; } } for(i=0; i<4; i++) { h = CreateFile(Name, READ_CONTROL, ((i & 1) ? 0 : FILE_SHARE_READ) | ((i & 2) ? 0 : FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(h && (h != ((HANDLE)(-1))) ) { return h; } } return NULL; } // end ata_open_dev() HANDLE ata_open_file( char* Name, BOOLEAN create ) { ULONG i; HANDLE h; if(!Name) { if(create) { return GetStdHandle(STD_OUTPUT_HANDLE); } else { return GetStdHandle(STD_INPUT_HANDLE); } } for(i=0; i<4; i++) { h = CreateFile(Name, create ? GENERIC_WRITE : GENERIC_READ , ((i & 1) ? 0 : FILE_SHARE_READ) | ((i & 2) ? 0 : FILE_SHARE_WRITE), NULL, create ? CREATE_NEW : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(h && (h != ((HANDLE)(-1))) ) { return h; } } return NULL; } // end ata_open_file() void ata_close_dev( HANDLE h ) { CloseHandle(h); } // end ata_close_dev() int ata_send_ioctl( HANDLE h, PSCSI_ADDRESS addr, PCCH Signature, ULONG Ioctl, PVOID inBuffer, ULONG inBufferLength, PVOID outBuffer, ULONG outBufferLength, PULONG returned ) { ULONG status; PUNIATA_CTL AtaCtl; ULONG data_len = max(inBufferLength, outBufferLength); ULONG len; if(addr) { len = data_len + offsetof(UNIATA_CTL, RawData); } else { len = data_len + sizeof(AtaCtl->hdr); } AtaCtl = (PUNIATA_CTL)GlobalAlloc(GMEM_FIXED, len); AtaCtl->hdr.HeaderLength = sizeof(SRB_IO_CONTROL); if(addr) { AtaCtl->hdr.Length = data_len + offsetof(UNIATA_CTL, RawData) - sizeof(AtaCtl->hdr); } else { AtaCtl->hdr.Length = data_len; } memcpy(&AtaCtl->hdr.Signature, Signature, 8); AtaCtl->hdr.Timeout = 10000; AtaCtl->hdr.ControlCode = Ioctl; AtaCtl->hdr.ReturnCode = 0; if(addr) { AtaCtl->addr = *addr; AtaCtl->addr.Length = sizeof(AtaCtl->addr); } if(outBufferLength) { if(addr) { memset(&AtaCtl->RawData, 0, outBufferLength); } else { memset(&AtaCtl->addr, 0, outBufferLength); } } if(inBuffer && inBufferLength) { if(addr) { memcpy(&AtaCtl->RawData, inBuffer, inBufferLength); } else { memcpy(&AtaCtl->addr, inBuffer, inBufferLength); } } status = DeviceIoControl(h, IOCTL_SCSI_MINIPORT, AtaCtl, len, AtaCtl, len, returned, FALSE); if(outBuffer && outBufferLength) { if(addr) { memcpy(outBuffer, &AtaCtl->RawData, outBufferLength); } else { memcpy(outBuffer, &AtaCtl->addr, outBufferLength); } } GlobalFree(AtaCtl); if(!status) { status = GetLastError(); return FALSE; } return TRUE; } // end ata_send_ioctl() int ata_send_scsi( HANDLE h, PSCSI_ADDRESS addr, PCDB cdb, UCHAR cdbLength, PVOID Buffer, ULONG BufferLength, BOOLEAN DataIn, PSENSE_DATA senseData, PULONG returned ) { ULONG status; PSCSI_PASS_THROUGH_WITH_BUFFERS sptwb; //ULONG data_len = BufferLength; ULONG len; len = BufferLength + offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf); sptwb = (PSCSI_PASS_THROUGH_WITH_BUFFERS)GlobalAlloc(GMEM_FIXED, len); if(!sptwb) { return FALSE; } memset(sptwb, 0, offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf)); sptwb->spt.Length = sizeof(SCSI_PASS_THROUGH); sptwb->spt.PathId = addr->PathId; sptwb->spt.TargetId = addr->TargetId; sptwb->spt.Lun = addr->Lun; sptwb->spt.CdbLength = cdbLength; sptwb->spt.SenseInfoLength = 24; sptwb->spt.DataIn = Buffer ? (DataIn ? SCSI_IOCTL_DATA_IN : SCSI_IOCTL_DATA_OUT) : 0; sptwb->spt.DataTransferLength = BufferLength; sptwb->spt.TimeOutValue = 10; sptwb->spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf); sptwb->spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucSenseBuf); memcpy(&sptwb->spt.Cdb, cdb, cdbLength); if(Buffer && !DataIn) { memcpy(&sptwb->ucSenseBuf, Buffer, BufferLength); } status = DeviceIoControl(h, IOCTL_SCSI_PASS_THROUGH, sptwb, (Buffer && !DataIn) ? len : sizeof(SCSI_PASS_THROUGH), sptwb, (Buffer && DataIn) ? len : offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf), returned, FALSE); if(Buffer && DataIn) { memcpy(Buffer, &sptwb->ucDataBuf, BufferLength); } if(senseData) { memcpy(senseData, &sptwb->ucSenseBuf, sizeof(sptwb->ucSenseBuf)); } GlobalFree(sptwb); if(!status) { status = GetLastError(); return FALSE; } return TRUE; } // end ata_send_scsi() IO_SCSI_CAPABILITIES g_capabilities; UCHAR g_inquiry_buffer[2048]; void ata_mode_to_str( char* str, int mode ) { if(mode > ATA_SA600) { sprintf(str, "SATA-600+"); } else if(mode >= ATA_SA600) { sprintf(str, "SATA-600"); } else if(mode >= ATA_SA300) { sprintf(str, "SATA-300"); } else if(mode >= ATA_SA150) { sprintf(str, "SATA-150"); } else if(mode >= ATA_UDMA0) { sprintf(str, "UDMA%d", mode-ATA_UDMA0); } else if(mode >= ATA_WDMA0) { sprintf(str, "WDMA%d", mode-ATA_WDMA0); } else if(mode >= ATA_SDMA0) { sprintf(str, "SDMA%d", mode-ATA_SDMA0); } else if(mode >= ATA_PIO0) { sprintf(str, "PIO%d", mode-ATA_PIO0); } else if(mode == ATA_PIO_NRDY) { sprintf(str, "PIO nRDY"); } else { sprintf(str, "PIO"); } } // end ata_mode_to_str() #define check_atamode_str(str, mode) \ (!_stricmp(str, "UDMA" #mode) || \ !_stricmp(str, "UDMA-" #mode) || \ !_stricmp(str, "ATA-" #mode) || \ !_stricmp(str, "ATA#" #mode)) int ata_str_to_mode( char* str ) { int mode; size_t len; if(!_stricmp(str, "SATA600")) return ATA_SA600; if(!_stricmp(str, "SATA300")) return ATA_SA300; if(!_stricmp(str, "SATA150")) return ATA_SA150; if(!_stricmp(str, "SATA")) return ATA_SA150; if(check_atamode_str(str, 16)) return ATA_UDMA0; if(check_atamode_str(str, 25)) return ATA_UDMA1; if(check_atamode_str(str, 33)) return ATA_UDMA2; if(check_atamode_str(str, 44)) return ATA_UDMA3; if(check_atamode_str(str, 66)) return ATA_UDMA4; if(check_atamode_str(str, 100)) return ATA_UDMA5; if(check_atamode_str(str, 122)) return ATA_UDMA6; len = strlen(str); if(len >= 4 && !_memicmp(str, "UDMA", 4)) { if(len == 4) return ATA_UDMA0; if(len > 5) return -1; mode = str[4] - '0'; if(mode < 0 || mode > 7) return -1; return ATA_UDMA0+mode; } if(len >= 4 && !_memicmp(str, "WDMA", 4)) { if(len == 4) return ATA_WDMA0; if(len > 5) return -1; mode = str[4] - '0'; if(mode < 0 || mode > 2) return -1; return ATA_WDMA0+mode; } if(len >= 4 && !_memicmp(str, "SDMA", 4)) { if(len == 4) return ATA_SDMA0; if(len > 5) return -1; mode = str[4] - '0'; if(mode < 0 || mode > 2) return -1; return ATA_SDMA0+mode; } if(len == 4 && !_memicmp(str, "DMA", 4)) { return ATA_SDMA0; } if(len >= 3 && !_memicmp(str, "PIO", 3)) { if(len == 3) return ATA_PIO; if(len > 4) return -1; mode = str[3] - '0'; if(mode < 0 || mode > 5) return -1; return ATA_PIO0+mode; } return -1; } // end ata_str_to_mode() ULONG EncodeVendorStr( OUT char* Buffer, IN PUCHAR Str, IN ULONG Length, IN ULONG Xorer ) { ULONG i,j; UCHAR a; for(i=0, j=0; i= 127)) { Buffer[j] = '#'; j++; sprintf(Buffer+j, "%2.2x", a); j++; } else { Buffer[j] = a; } } Buffer[j] = 0; return j; } // end EncodeVendorStr() HKEY ata_get_bblist_regh( IN PIDENTIFY_DATA ident, OUT char* DevSerial, BOOLEAN read_only ) { HKEY hKey = NULL; HKEY hKey2 = NULL; ULONG Length; REGSAM access = read_only ? KEY_READ : KEY_ALL_ACCESS; Length = EncodeVendorStr(DevSerial, (PUCHAR)ident->ModelNumber, sizeof(ident->ModelNumber), 0x01); DevSerial[Length] = '-'; Length++; Length += EncodeVendorStr(DevSerial+Length, ident->SerialNumber, sizeof(ident->SerialNumber), 0x01); if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\UniATA", NULL, access, &hKey) != ERROR_SUCCESS) { hKey = NULL; goto exit; } if(RegOpenKey(hKey, "Parameters", &hKey2) != ERROR_SUCCESS) { hKey2 = NULL; if(read_only || (RegCreateKey(hKey, "Parameters", &hKey2) != ERROR_SUCCESS)) { hKey2 = NULL; goto exit; } } RegCloseKey(hKey2); if(RegOpenKey(hKey, "Parameters\\BadBlocks", &hKey2) != ERROR_SUCCESS) { hKey2 = NULL; if(read_only || (RegCreateKey(hKey, "Parameters\\BadBlocks", &hKey2) != ERROR_SUCCESS)) { hKey2 = NULL; goto exit; } } exit: if(hKey) RegCloseKey(hKey); return hKey2; } // end ata_get_bblist_regh() IDENTIFY_DATA g_ident; int ata_check_unit( HANDLE h, // handle to ScsiXXX: int dev_id ) { ULONG status; ULONG returned; PSCSI_ADAPTER_BUS_INFO adapterInfo; PSCSI_INQUIRY_DATA inquiryData; SCSI_ADDRESS addr; ULONG i, j; int l_dev_id; ULONG len; GETTRANSFERMODE IoMode; PSENDCMDOUTPARAMS pout; PIDENTIFY_DATA ident; PINQUIRYDATA scsi_ident; char buff[sizeof(SENDCMDOUTPARAMS)+/*sizeof(IDENTIFY_DATA)*/2048]; char mode_str[12]; //ULONG bus_id = (dev_id >> 24) & 0xff; BOOLEAN found = FALSE; SENDCMDINPARAMS pin; int io_mode = -1; char SerNum[128]; char DevSerial[128]; char lun_str[10]; HKEY hKey2; ULONGLONG max_lba = -1; USHORT chs[3] = { 0 }; if(dev_id != -1) { dev_id &= 0x00ffffff; } if(dev_id == 0x007f7f7f) { return TRUE; } pout = (PSENDCMDOUTPARAMS)buff; ident = (PIDENTIFY_DATA)&(pout->bBuffer); status = DeviceIoControl(h, IOCTL_SCSI_GET_INQUIRY_DATA, NULL, 0, g_inquiry_buffer, sizeof(g_inquiry_buffer), &returned, FALSE); if(!status) { printf("Can't get device info\n"); return FALSE; } // Note: adapterInfo->NumberOfBuses is 1 greater than g_AdapterInfo->NumberChannels // because of virtual communication port adapterInfo = (PSCSI_ADAPTER_BUS_INFO)g_inquiry_buffer; for (i = 0; i+1 < adapterInfo->NumberOfBuses; i++) { inquiryData = (PSCSI_INQUIRY_DATA) (g_inquiry_buffer + adapterInfo->BusData[i].InquiryDataOffset); if(g_extended && g_AdapterInfo && g_AdapterInfo->ChanHeaderLengthValid && g_AdapterInfo->NumberChannels < i) { PCHANINFO ChanInfo; ChanInfo = (PCHANINFO) (((PCHAR)g_AdapterInfo)+ sizeof(ADAPTERINFO)+ g_AdapterInfo->ChanHeaderLength*i); io_mode = ChanInfo->MaxTransferMode; if(io_mode != -1) { ata_mode_to_str(mode_str, io_mode); } else { mode_str[0] = 0; } printf(" b%lu [%s]\n", i, mode_str ); } while (adapterInfo->BusData[i].InquiryDataOffset) { /* if(dev_id/adapterInfo->BusData[i].NumberOfLogicalUnits == inquiryData->TargetId && dev_id%adapterInfo->BusData[i].NumberOfLogicalUnits == inquiryData->Lun) { printf(" %d %d %3d %s %.28s ", i, inquiryData->TargetId, inquiryData->Lun, (inquiryData->DeviceClaimed) ? "Y" : "N", &inquiryData->InquiryData[8]); }*/ l_dev_id = (i << 16) | ((ULONG)(inquiryData->TargetId) << 8) | inquiryData->Lun; if(l_dev_id == dev_id || dev_id == -1) { scsi_ident = (PINQUIRYDATA)&(inquiryData->InquiryData); if(!memcmp(&(scsi_ident->VendorId[0]), UNIATA_COMM_PORT_VENDOR_STR, 24)) { // skip communication port goto next_dev; } found = TRUE; if(inquiryData->Lun) { sprintf(lun_str, ":l%d", inquiryData->Lun); } else { sprintf(lun_str, " "); } /* for (j = 0; j < 8; j++) { printf("%02X ", inquiryData->InquiryData[j]); } */ addr.Length = sizeof(addr); addr.PortNumber = -1; addr.PathId = inquiryData->PathId; addr.TargetId = inquiryData->TargetId; addr.Lun = inquiryData->Lun; status = ata_send_ioctl(h, &addr, "-UNIATA-", IOCTL_SCSI_MINIPORT_UNIATA_GET_MODE, NULL, 0, &IoMode, sizeof(IoMode), &returned); if(status) { //io_mode = min(IoMode.CurrentMode, IoMode.MaxMode); io_mode = IoMode.PhyMode; if(!io_mode) { io_mode = min(max(IoMode.CurrentMode,IoMode.OrigMode),IoMode.MaxMode); } } else { io_mode = -1; } memset(&pin, 0, sizeof(pin)); memset(buff, 0, sizeof(buff)); pin.irDriveRegs.bCommandReg = ID_CMD; // this is valid for IDE/ATA, where only 2 devices can be attached to the bus. // probably, we shall change this in future to support SATA splitters pin.bDriveNumber = inquiryData->PathId*2+inquiryData->TargetId; status = ata_send_ioctl(h, NULL, "SCSIDISK", IOCTL_SCSI_MINIPORT_IDENTIFY, &pin, sizeof(pin), buff, sizeof(buff), &returned); if(!status) { memset(&pin, 0, sizeof(pin)); memset(buff, 0, sizeof(buff)); pin.irDriveRegs.bCommandReg = ATAPI_ID_CMD; // this is valid for IDE/ATA, where only 2 devices can be attached to the bus. // probably, we shall change this in future to support SATA splitters pin.bDriveNumber = inquiryData->PathId*2+inquiryData->TargetId; status = ata_send_ioctl(h, NULL, "SCSIDISK", IOCTL_SCSI_MINIPORT_IDENTIFY, &pin, sizeof(pin), buff, sizeof(buff), &returned); } if(!g_extended) { printf(" b%lu:d%d%s %24.24s %4.4s ", i, inquiryData->TargetId, lun_str, /*(inquiryData->DeviceClaimed) ? "Y" : "N",*/ (g_extended ? (PUCHAR)"" : &scsi_ident->VendorId[0]), (g_extended ? (PUCHAR)"" : &scsi_ident->ProductRevisionLevel[0]) ); } else { printf(" b%lu:d%d%s ", i, inquiryData->TargetId, lun_str ); } if(status) { if(io_mode == -1) { io_mode = ata_cur_mode_from_ident(ident, IDENT_MODE_ACTIVE); } } if(io_mode != -1) { ata_mode_to_str(mode_str, io_mode); } if(!g_extended || !status) { if(g_extended) { printf(" %24.24s %4.4s ", (&inquiryData->InquiryData[8]), (&inquiryData->InquiryData[8+24]) ); } if(io_mode != -1) { printf(" %.12s ", mode_str); } } printf("\n"); if(g_extended) { if(status) { BOOLEAN BlockMode_valid = TRUE; BOOLEAN print_geom = FALSE; switch(ident->DeviceType) { case ATAPI_TYPE_DIRECT: if(ident->Removable) { printf(" Floppy "); } else { printf(" Hard Drive "); } break; case ATAPI_TYPE_TAPE: printf(" Tape Drive "); break; case ATAPI_TYPE_CDROM: printf(" CD/DVD Drive "); BlockMode_valid = FALSE; break; case ATAPI_TYPE_OPTICAL: printf(" Optical Drive "); BlockMode_valid = FALSE; break; default: printf(" Hard Drive "); print_geom = TRUE; //MOV_DD_SWP(max_lba, ident->UserAddressableSectors); max_lba = ident->UserAddressableSectors; if(ident->FeaturesSupport.Address48) { max_lba = ident->UserAddressableSectors48; } //MOV_DW_SWP(chs[0], ident->NumberOfCylinders); //MOV_DW_SWP(chs[1], ident->NumberOfHeads); //MOV_DW_SWP(chs[2], ident->SectorsPerTrack); chs[0] = ident->NumberOfCylinders; chs[1] = ident->NumberOfHeads; chs[2] = ident->SectorsPerTrack; if(!max_lba) { max_lba = (ULONG)(chs[0])*(ULONG)(chs[1])*(ULONG)(chs[2]); } } if(io_mode != -1) { printf(" %.12s\n", mode_str); } for (j = 0; j < 40; j += 2) { MOV_DW_SWP(SerNum[j], ((PUCHAR)ident->ModelNumber)[j]); } printf(" Mod: %40.40s\n", SerNum); for (j = 0; j < 8; j += 2) { MOV_DW_SWP(SerNum[j], ((PUCHAR)ident->FirmwareRevision)[j]); } printf(" Rev: %8.8s\n", SerNum); for (j = 0; j < 20; j += 2) { MOV_DW_SWP(SerNum[j], ((PUCHAR)ident->SerialNumber)[j]); } printf(" S/N: %20.20s\n", SerNum); if(BlockMode_valid) { if(ident->MaximumBlockTransfer) { printf(" Multi-block mode: %u block%s\n", ident->MaximumBlockTransfer, ident->MaximumBlockTransfer == 1 ? "" : "s"); } else { printf(" Multi-block mode: N/A\n"); } } if(print_geom) { printf(" C/H/S: %u/%u/%u \n", chs[0], chs[1], chs[2]); printf(" LBA: %I64u \n", max_lba); if(max_lba < 2) { printf(" Size: %lu kb\n", (ULONG)(max_lba/2)); } else if(max_lba < 2*1024*1024) { printf(" Size: %lu Mb\n", (ULONG)(max_lba/2048)); } else if(max_lba < (ULONG)2*1024*1024*1024) { printf(" Size: %lu.%lu (%lu) Gb\n", (ULONG)(max_lba/2048/1024), (ULONG)(((max_lba/2048)%1024)/10), (ULONG)(max_lba*512/1000/1000/1000) ); } else { printf(" Size: %lu.%lu (%lu) Tb\n", (ULONG)(max_lba/2048/1024/1024), (ULONG)((max_lba/2048/1024)%1024)/10, (ULONG)(max_lba*512/1000/1000/1000) ); } } len = 0; if((hKey2 = ata_get_bblist_regh(ident, DevSerial, TRUE))) { if(RegQueryValueEx(hKey2, DevSerial, NULL, NULL, NULL, &len) == ERROR_SUCCESS) { printf(" !!! Assigned bad-block list !!!\n"); } RegCloseKey(hKey2); } } else { switch(scsi_ident->DeviceType) { case DIRECT_ACCESS_DEVICE: if(scsi_ident->RemovableMedia) { printf(" Floppy "); } else { printf(" Hard Drive "); } break; case SEQUENTIAL_ACCESS_DEVICE: printf(" Tape Drive "); break; case PRINTER_DEVICE: printf(" Printer "); break; case PROCESSOR_DEVICE: printf(" Processor "); break; case WRITE_ONCE_READ_MULTIPLE_DEVICE: printf(" WORM Drive "); break; case READ_ONLY_DIRECT_ACCESS_DEVICE: printf(" CDROM Drive "); break; case SCANNER_DEVICE: printf(" Scanner "); break; case OPTICAL_DEVICE: printf(" Optical Drive "); break; case MEDIUM_CHANGER: printf(" Changer "); break; case COMMUNICATION_DEVICE: printf(" Comm. device "); break; } printf("\n"); } } memcpy(&g_ident, ident, sizeof(IDENTIFY_DATA)); } next_dev: if (inquiryData->NextInquiryDataOffset == 0) { break; } inquiryData = (PSCSI_INQUIRY_DATA) (g_inquiry_buffer + inquiryData->NextInquiryDataOffset); } } if(!found) { printf(" No device(s) found.\n"); return FALSE; } return TRUE; } // end ata_check_unit() BOOLEAN ata_adapter_info( int bus_id, int print_info ) { char dev_name[64]; HANDLE h; PADAPTERINFO AdapterInfo; ULONG status; ULONG returned; SCSI_ADDRESS addr; PCI_SLOT_NUMBER slotData; char mode_str[12]; ULONG len; sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id); h = ata_open_dev(dev_name); if(!h) return FALSE; addr.Length = sizeof(addr); addr.PortNumber = bus_id; len = sizeof(ADAPTERINFO)+sizeof(CHANINFO)*AHCI_MAX_PORT; if(!g_AdapterInfo) { AdapterInfo = (PADAPTERINFO)GlobalAlloc(GMEM_FIXED, len); if(!AdapterInfo) { ata_close_dev(h); return FALSE; } } else { AdapterInfo = g_AdapterInfo; } memset(AdapterInfo, 0, len); status = ata_send_ioctl(h, &addr, "-UNIATA-", IOCTL_SCSI_MINIPORT_UNIATA_ADAPTER_INFO, AdapterInfo, len, AdapterInfo, len, &returned); if(status) { ata_mode_to_str(mode_str, AdapterInfo->MaxTransferMode); } printf("Scsi%d: %s %s\n", bus_id, status ? "[UniATA]" : "", status ? mode_str : ""); if(print_info) { if(!status) { printf("Can't get adapter info\n"); } else { if(AdapterInfo->AdapterInterfaceType == PCIBus) { slotData.u.AsULONG = AdapterInfo->slotNumber; printf(" PCI Bus/Dev/Func: %lu/%lu/%lu%s\n", AdapterInfo->SystemIoBusNumber, slotData.u.bits.DeviceNumber, slotData.u.bits.FunctionNumber, AdapterInfo->AdapterInterfaceType == AdapterInfo->OrigAdapterInterfaceType ? "" : " (ISA-Bridged)"); printf(" VendorId/DevId/Rev: %#04x/%#04x/%#02x\n", (USHORT)(AdapterInfo->DevID >> 16), (USHORT)(AdapterInfo->DevID & 0xffff), (UCHAR)(AdapterInfo->RevID)); if(AdapterInfo->DeviceName[0]) { printf(" Name: %s\n", AdapterInfo->DeviceName); } } else if(AdapterInfo->AdapterInterfaceType == Isa) { printf(" ISA Bus\n"); } printf(" IRQ: %ld\n", AdapterInfo->BusInterruptLevel); } } ata_close_dev(h); //GlobalFree(AdapterInfo); g_AdapterInfo = AdapterInfo; return status ? TRUE : FALSE; } // end ata_adapter_info() int ata_check_controller( HANDLE h, // handle to ScsiXXX: PIO_SCSI_CAPABILITIES capabilities ) { ULONG status; ULONG returned; status = DeviceIoControl(h, IOCTL_SCSI_GET_CAPABILITIES, NULL, 0, capabilities, sizeof(IO_SCSI_CAPABILITIES), &returned, FALSE); return status; } // end ata_check_controller() BOOLEAN ata_list( int bus_id, int dev_id ) { char dev_name[64]; HANDLE h; //BOOLEAN uniata_driven; if(bus_id == -1) { for(bus_id=0; TRUE; bus_id++) { if(!ata_list(bus_id, dev_id)) break; } return TRUE; } /*uniata_driven =*/ ata_adapter_info(bus_id, g_adapter_info); sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id); h = ata_open_dev(dev_name); if(!h) return FALSE; if(dev_id == -1) { ata_check_controller(h, &g_capabilities); ata_check_unit(h, -1); ata_close_dev(h); return TRUE; } ata_check_unit(h, dev_id | (bus_id << 24)); ata_close_dev(h); return TRUE; } // end ata_list() BOOLEAN ata_mode( int bus_id, int dev_id, int mode ) { char dev_name[64]; HANDLE h; SETTRANSFERMODE IoMode; ULONG status; ULONG returned; SCSI_ADDRESS addr; if(dev_id == -1) { return FALSE; } sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id); h = ata_open_dev(dev_name); if(!h) return FALSE; addr.Length = sizeof(addr); addr.PortNumber = bus_id; addr.PathId = (UCHAR)(dev_id >> 16); addr.TargetId = (UCHAR)(dev_id >> 8); addr.Lun = (UCHAR)(dev_id); IoMode.MaxMode = mode; IoMode.ApplyImmediately = FALSE; // IoMode.ApplyImmediately = TRUE; IoMode.OrigMode = mode; status = ata_send_ioctl(h, &addr, "-UNIATA-", IOCTL_SCSI_MINIPORT_UNIATA_SET_MAX_MODE, &IoMode, sizeof(IoMode), NULL, 0, &returned); if(!status) { printf("Can't apply specified transfer mode\n"); } else { ata_mode_to_str(dev_name, mode); printf("Transfer rate switched to %s\n", dev_name); } ata_close_dev(h); return status ? TRUE : FALSE; } // end ata_mode() BOOLEAN ata_reset( int bus_id, int dev_id ) { char dev_name[64]; HANDLE h; ULONG status; ULONG returned; SCSI_ADDRESS addr; if(dev_id == -1) { return FALSE; } sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id); h = ata_open_dev(dev_name); if(!h) return FALSE; addr.Length = sizeof(addr); addr.PortNumber = bus_id; addr.PathId = (UCHAR)(dev_id >> 16); addr.TargetId = (UCHAR)(dev_id >> 8); addr.Lun = (UCHAR)(dev_id); if(addr.TargetId == 0x7f && addr.Lun == 0x7f) { addr.TargetId = (UCHAR)0xff; addr.Lun = 0; printf("Resetting channel...\n"); } else { printf("Resetting device...\n"); } status = ata_send_ioctl(h, &addr, "-UNIATA-", IOCTL_SCSI_MINIPORT_UNIATA_RESET_DEVICE, NULL, 0, NULL, 0, &returned); if(!status) { printf("Reset failed\n"); } else { printf("Channel reset done\n"); } ata_close_dev(h); return TRUE; } // end ata_reset() BOOLEAN ata_hide( int bus_id, int dev_id, int lock, int persistent_hide, int power_mode ) { char dev_name[64]; HANDLE h; ULONG status; ULONG returned; SCSI_ADDRESS addr; ADDREMOVEDEV to; if(dev_id == -1) { return FALSE; } if(power_mode) { ata_power_mode(bus_id, dev_id, power_mode); } if(lock < 0) { lock = DEFAULT_REMOVAL_LOCK_TIMEOUT; } sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id); h = ata_open_dev(dev_name); if(!h) return FALSE; addr.Length = sizeof(addr); addr.PortNumber = bus_id; addr.PathId = (UCHAR)(dev_id >> 16); addr.TargetId = (UCHAR)(dev_id >> 8); addr.Lun = (UCHAR)(dev_id); to.WaitForPhysicalLink = lock; to.Flags = persistent_hide ? UNIATA_REMOVE_FLAGS_HIDE : 0; printf("Deleting device.\n"); if(lock) { printf("ATTENTION: you have %d seconds to disconnect cable\n", lock); } status = ata_send_ioctl(h, &addr, "-UNIATA-", IOCTL_SCSI_MINIPORT_UNIATA_DELETE_DEVICE, &to, sizeof(to), NULL, 0, &returned); if(!status) { printf("Delete failed\n"); } else { printf("Device is detached\n"); } ata_close_dev(h); return status ? TRUE : FALSE; } // end ata_hide() BOOLEAN ata_scan( int bus_id, int dev_id, int lock, int unhide ) { char dev_name[64]; HANDLE h; ULONG status; ULONG returned; SCSI_ADDRESS addr; ADDREMOVEDEV to; if(dev_id == -1) { return FALSE; } if(lock < 0) { lock = DEFAULT_REMOVAL_LOCK_TIMEOUT; } sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id); h = ata_open_dev(dev_name); if(!h) return FALSE; if((UCHAR)(dev_id) != 0xff && (UCHAR)(dev_id >> 8) != 0xff) { addr.Length = sizeof(addr); addr.PortNumber = bus_id; addr.PathId = (UCHAR)(dev_id >> 16); addr.TargetId = 0; addr.Lun = 0; to.WaitForPhysicalLink = lock; to.Flags = unhide ? UNIATA_ADD_FLAGS_UNHIDE : 0; printf("Scanning bus for new devices.\n"); if(lock) { printf("You have %d seconds to connect device.\n", lock); } status = ata_send_ioctl(h, &addr, "-UNIATA-", IOCTL_SCSI_MINIPORT_UNIATA_FIND_DEVICES, &to, sizeof(to), NULL, 0, &returned); } else { status = DeviceIoControl(h, IOCTL_SCSI_RESCAN_BUS, NULL, 0, NULL, 0, &returned, FALSE); } ata_close_dev(h); return status ? TRUE : FALSE; } // end ata_scan() CHAR* _fgets( CHAR *string, int count, HANDLE stream ) { CHAR *pointer = string; ULONG read_bytes; CHAR *retval = string; int ch = 0; if (count <= 0) return(NULL); while (--count) { if(!ReadFile(stream, &ch, 1, &read_bytes, NULL) || !read_bytes) { if (pointer == string) { retval=NULL; goto done; } break; } if ((*pointer++ = (CHAR)ch) == '\n') { break; } } *pointer = '\0'; /* Common return */ done: return(retval); } // end _fgets() BOOLEAN ata_bblk( int bus_id, int dev_id, int list_bb ) { char dev_name[64]; char tmp[64]; char DevSerial[128]; HANDLE h = NULL; HANDLE hf = NULL; ULONG status; ULONG returned; SCSI_ADDRESS addr; ULONG len; ULONG Length; BOOLEAN retval = FALSE; HKEY hKey2 = NULL; char* bblist = NULL; LONGLONG tmp_bb_lba; LONGLONG tmp_bb_len; char BB_Msg[256]; int radix=gRadix; int i, j; ULONG b; if(dev_id == -1) { printf("\nERROR: Target device/bus ID must be specified\n\n"); print_help(); return FALSE; } if(((dev_id >> 16) & 0xff) == 0xff) { printf("\nERROR: Target device bus number (channel) must be specified with b:\n\n"); print_help(); return FALSE; } if(((dev_id >> 8) & 0xff) == 0xff) { printf("\nERROR: Target device ID must be specified with d:\n\n"); print_help(); return FALSE; } sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id); h = ata_open_dev(dev_name); if(!h) { if(bus_id == -1) { printf("Controller number must be specified\n"); } else { printf("Can't open Controller %d\n", bus_id); } return FALSE; } if(list_bb == 0) { hf = ata_open_file(g_bb_list, FALSE); if(!hf) { printf("Can't open bad block list file:\n %s\n", g_bb_list); ata_close_dev(h); return FALSE; } len = GetFileSize(hf, NULL); if(!len || len == INVALID_FILE_SIZE) goto exit; bblist = (char*)GlobalAlloc(GMEM_FIXED, len*8); } if(!ata_check_unit(h, dev_id | (bus_id << 24))) { goto exit; } hKey2 = ata_get_bblist_regh(&g_ident, DevSerial, list_bb==1); if(!hKey2) { printf("Can't open registry key:\n HKLM\\SYSTEM\\CurrentControlSet\\Services\\UniATA\\Parameters\\BadBlocks\n"); goto exit; } if(list_bb == -1) { if(RegDeleteValue(hKey2, DevSerial) != ERROR_SUCCESS) { printf("Can't delete registry value:\n %s\n", DevSerial); goto exit; } addr.PortNumber = bus_id; addr.PathId = (UCHAR)(dev_id >> 16); addr.TargetId = (UCHAR)(dev_id >> 8); addr.Lun = (UCHAR)(dev_id); status = ata_send_ioctl(h, &addr, "-UNIATA-", IOCTL_SCSI_MINIPORT_UNIATA_RESETBB, NULL, 0, NULL, 0, &returned); if(!status) { printf("Bad block list shall be cleared after reboot.\n"); } else { printf("Bad block list cleared\n"); } } else if(list_bb == 0) { LONGLONG* pData = ((LONGLONG*)bblist); char a; int k, k0; Length=0; i=0; j=0; k=0; while(_fgets(BB_Msg, sizeof(BB_Msg), hf)) { j++; BB_Msg[sizeof(BB_Msg)-1] = 0; k=0; while((a = BB_Msg[k])) { if(a == ' ' || a == '\t' || a == '\r') { k++; continue; } break; } if(!a || a == ';' || a == '#') { continue; } if(!strncmp(BB_Msg+k, "hex:", 4)) { radix=16; continue; } if(!strncmp(BB_Msg+k, "dec:", 4)) { radix=10; continue; } k0 = k; while((a = BB_Msg[k])) { if(a == ' ' || a == '\t' || a == '\r') { BB_Msg[k] = '\t'; } k++; if(a == ';' || a == '#') { break; } if(a >= '0' && a <= '9') { continue; } if(radix == 16 && ((a >= 'A' && a <= 'F') || (a >= 'a' && a <= 'f'))) { continue; } printf("Bad input BB list file:\n %s\n", g_bb_list); printf("Illegal character '%1.1s' in line %d:\n%s\n", BB_Msg+k-1, j, BB_Msg); k0=-1; break; } if(k0 == -1) { continue; } k = k0; if(radix == 10) { b = sscanf(BB_Msg+k, "%I64u\t%I64u", &tmp_bb_lba, &tmp_bb_len); } else { b = sscanf(BB_Msg+k, "%I64x\t%I64x", &tmp_bb_lba, &tmp_bb_len); } if(b == 1) { tmp_bb_len = 1; } else if(b != 2) { printf("Bad input BB list file:\n %s\n", g_bb_list); printf("Can't parse line %d:\n%s\n", j, BB_Msg); continue; } if(!tmp_bb_len) { printf("Bad input BB list file:\n %s\n", g_bb_list); printf("BlockCount evaluated to 0 in line %d:\n%s\n", j, BB_Msg); continue; } if(tmp_bb_lba < 0) { printf("Bad input BB list file:\n %s\n", g_bb_list); printf("Start LBA evaluated to negative in line %d:\n%s\n", j, BB_Msg); continue; } if(tmp_bb_len < 0) { printf("Bad input BB list file:\n %s\n", g_bb_list); printf("BlockCount evaluated to negative in line %d:\n%s\n", j, BB_Msg); continue; } if(i && (pData[(i-1)*2+1] == tmp_bb_lba)) { pData[(i-1)*2+1]+=tmp_bb_len; } else { pData[i*2+0]=tmp_bb_lba; pData[i*2+1]=tmp_bb_lba+tmp_bb_len; i++; Length += sizeof(LONGLONG)*2; } } if(RegSetValueEx(hKey2, DevSerial, NULL, REG_BINARY, (const UCHAR*)bblist, Length) != ERROR_SUCCESS) { printf("Can't set registry value:\n %s\n", DevSerial); goto exit; } /* addr.PortNumber = bus_id; addr.PathId = (UCHAR)(dev_id >> 16); addr.TargetId = (UCHAR)(dev_id >> 8); addr.Lun = (UCHAR)(dev_id); status = ata_send_ioctl(h, &addr, "-UNIATA-", IOCTL_SCSI_MINIPORT_UNIATA_SETBB, NULL, 0, NULL, 0, &returned); */ printf("Bad block list shall be applied after reboot\n"); } else { len = 0; returned = RegQueryValueEx(hKey2, DevSerial, NULL, NULL, NULL, &len); if(returned == 2) { printf("No bad block list assigned\n"); goto exit; } else if(returned != ERROR_SUCCESS) { printf("Can't get registry value:\n %s\n", DevSerial); goto exit; } hf = ata_open_file(g_bb_list, TRUE); if(!hf) { printf("Can't create bad block list file:\n %s\n", g_bb_list); goto exit; } bblist = (char*)GlobalAlloc(GMEM_FIXED, len); if(RegQueryValueEx(hKey2, DevSerial, NULL, NULL, (UCHAR*)bblist, &len) != ERROR_SUCCESS) { printf("Can't get registry value:\n %s\n", DevSerial); goto exit; } if(g_bb_list) { for (j = 0; j < 20; j += 2) { MOV_DW_SWP(tmp[j], ((PUCHAR)(&g_ident.ModelNumber))[j]); } b = sprintf(BB_Msg, "#model: %20.20s\n", tmp); WriteFile(hf, BB_Msg, b, &returned, NULL); for (j = 0; j < 4; j += 2) { MOV_DW_SWP(tmp[j], ((PUCHAR)(&g_ident.FirmwareRevision))[j]); } b = sprintf(BB_Msg, "#rev: %4.4s\n", tmp); WriteFile(hf, BB_Msg, b, &returned, NULL); for (j = 0; j < 20; j += 2) { MOV_DW_SWP(tmp[j], ((PUCHAR)(&g_ident.SerialNumber))[j]); } b = sprintf(BB_Msg, "#s/n: %20.20s\n", tmp); WriteFile(hf, BB_Msg, b, &returned, NULL); b = sprintf(BB_Msg, "#%s\n", DevSerial); WriteFile(hf, BB_Msg, b, &returned, NULL); b = sprintf(BB_Msg, "#Starting LBA\tNum. of Blocks\n"); WriteFile(hf, BB_Msg, b, &returned, NULL); b = sprintf(BB_Msg, "hex:\n"); WriteFile(hf, BB_Msg, b, &returned, NULL); } else { b = sprintf(BB_Msg, "Starting LBA\tNum. of Blocks (HEX)\n"); WriteFile(hf, BB_Msg, b, &returned, NULL); } i = 0; while(len >= sizeof(LONGLONG)*2) { tmp_bb_lba = ((LONGLONG*)bblist)[i*2+0]; tmp_bb_len = ((LONGLONG*)bblist)[i*2+1] - tmp_bb_lba; b = sprintf(BB_Msg, "%I64u\t%I64u\n", tmp_bb_lba, tmp_bb_len); WriteFile(hf, BB_Msg, b, &returned, NULL); i++; len -= sizeof(LONGLONG)*2; } } retval = TRUE; exit: if(hKey2) RegCloseKey(hKey2); if(bblist) { GlobalFree(bblist); } ata_close_dev(hf); ata_close_dev(h); return retval; } // end ata_bblk() BOOLEAN ata_power_mode( int bus_id, int dev_id, int power_mode ) { char dev_name[64]; HANDLE h; ULONG status; ULONG returned; SCSI_ADDRESS addr; CDB cdb; SENSE_DATA senseData; if(dev_id == -1) { return FALSE; } if(!power_mode) { return TRUE; } sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id); h = ata_open_dev(dev_name); if(!h) return FALSE; addr.PortNumber = bus_id; addr.PathId = (UCHAR)(dev_id >> 16); addr.TargetId = (UCHAR)(dev_id >> 8); addr.Lun = (UCHAR)(dev_id); memset(&cdb, 0, sizeof(cdb)); cdb.START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; cdb.START_STOP.Immediate = 1; cdb.START_STOP.PowerConditions = power_mode; cdb.START_STOP.Start = (power_mode != StartStop_Power_Sleep); printf("Changing power state to ...\n"); status = ata_send_scsi(h, &addr, &cdb, 6, NULL, 0, FALSE, &senseData, &returned); ata_close_dev(h); return status ? TRUE : FALSE; } // end ata_power_mode() int ata_num_to_x_dev( char a ) { if(a >= '0' && a <= '9') return a-'0'; return -1; } int main ( int argc, char* argv[] ) { //ULONG Flags = 0; intptr_t i; uintptr_t j; char a; int bus_id = -1; int dev_id = -1; int cmd = 0; int lock = -1; int b_dev=-1, d_dev=-1, l_dev=0; int mode=-1; int list_bb=0; int persistent_hide=0; int power_mode=StartStop_Power_NoChg; printf("Console ATA control utility for Windows NT3.51/NT4/2000/XP/2003\n" "Version 0." UNIATA_VER_STR ", Copyright (c) Alexander A. Telyatnikov, 2003-2012\n" "Home site: http://alter.org.ua\n"); for(i=1; i0) { print_help(); } cmd = CMD_ATA_LIST; break; case 'x' : g_extended = 1; break; case 'a' : g_adapter_info = 1; break; case 'S' : persistent_hide = 1; case 's' : if(cmd || lock>0) { print_help(); } cmd = CMD_ATA_FIND; d_dev = 0; break; case 'H' : persistent_hide = 1; case 'h' : if(cmd) { print_help(); } cmd = CMD_ATA_HIDE; d_dev = 0; break; case 'm' : if(cmd) { print_help(); } cmd = CMD_ATA_MODE; i++; if(!argv[i]) { print_help(); } mode = ata_str_to_mode(argv[i]); if(mode == -1) { i--; } else { j = strlen(argv[i])-1; } break; case 'r' : if(cmd) { print_help(); } cmd = CMD_ATA_RESET; break; case 'b' : if(cmd) { print_help(); } switch(argv[i][j+1]) { case 'l': list_bb = 1; break; case 'a': list_bb = 0; break; case 'r': list_bb = -1; break; default: j--; } j++; cmd = CMD_ATA_BBLK; break; case 'f' : if(cmd != CMD_ATA_BBLK) { print_help(); } i++; if(!argv[i]) { print_help(); } g_bb_list=argv[i]; j = strlen(argv[i])-1; break; case 'p' : if(cmd && (cmd != CMD_ATA_FIND) && (cmd != CMD_ATA_HIDE)) { print_help(); } switch(argv[i][j+1]) { case '0': case 'a': // do nothing break; case '1': case 'i': power_mode = StartStop_Power_Idle; break; case '2': case 's': power_mode = StartStop_Power_Standby; break; case '3': case 'p': power_mode = StartStop_Power_Sleep; break; default: j--; } j++; if(power_mode && !cmd) { cmd = CMD_ATA_POWER; } break; case 'D' : power_mode = StartStop_Power_Sleep; if(cmd && (cmd != CMD_ATA_HIDE)) { print_help(); } case 'd' : if(cmd && (cmd != CMD_ATA_FIND) && (cmd != CMD_ATA_HIDE) && (cmd != CMD_ATA_POWER)) { print_help(); } if(!cmd) { cmd = CMD_ATA_HIDE; } i++; if(!argv[i]) { print_help(); } if(!sscanf(argv[i], "%d", &lock)) { lock = DEFAULT_REMOVAL_LOCK_TIMEOUT; i--; } j = strlen(argv[i])-1; break; case 'n' : if(cmd != CMD_ATA_BBLK) { print_help(); } i++; if(!argv[i]) { print_help(); } if(!strcmp(argv[i], "hex") || !strcmp(argv[i], "16")) { gRadix = 16; } else if(!strcmp(argv[i], "dec") || !strcmp(argv[i], "10")) { gRadix = 10; } else { print_help(); } j = strlen(argv[i])-1; break; case '?' : default: print_help(); } j++; } } if(g_adapter_info && !cmd) { cmd = CMD_ATA_LIST; b_dev = 127; d_dev = 127; l_dev = 127; } else if((d_dev == -1) && (b_dev != -1)) { d_dev = 127; l_dev = 127; } if((d_dev != -1) && (b_dev != -1)) { dev_id = (b_dev << 16) | (d_dev << 8) | l_dev; } if(cmd == CMD_ATA_LIST) { ata_list(bus_id, dev_id); } else if(cmd == CMD_ATA_MODE) { ata_mode(bus_id, dev_id, mode); } else if(cmd == CMD_ATA_RESET) { ata_reset(bus_id, dev_id); } else if(cmd == CMD_ATA_FIND) { ata_scan(bus_id, dev_id, lock, persistent_hide); } else if(cmd == CMD_ATA_HIDE) { ata_hide(bus_id, dev_id, lock, persistent_hide, power_mode); } else if(cmd == CMD_ATA_BBLK) { ata_bblk(bus_id, dev_id, list_bb); } else if(cmd == CMD_ATA_POWER) { ata_power_mode(bus_id, dev_id, power_mode); } else { print_help(); } exit(0); }