xref: /reactos/base/applications/atactl/atactl.cpp (revision 7611cc2b)
1 #include <stdarg.h>
2 #include <windef.h>
3 #include <winbase.h>
4 #include <winreg.h>
5 #include <winioctl.h>
6 #include <stdlib.h>
7 //#include <ntdddisk.h>
8 //#include <ntddscsi.h>
9 #include <ntddscsi.h>
10 #include <atapi.h>
11 #include <bm_devs.h>
12 #include <uata_ctl.h>
13 #include <tools.h>
14 #include <uniata_ver.h>
15 
16 #include "helper.h"
17 
18 #define DEFAULT_REMOVAL_LOCK_TIMEOUT 20
19 
20 #define MOV_DW_SWP(a,b)                                                  \
21 do                                                                       \
22 {                                                                        \
23     *(unsigned short *)&(a) = _byteswap_ushort(*(unsigned short *)&(b)); \
24 }                                                                        \
25 while (0)
26 
27 #define MOV_DD_SWP(a,b)           \
28 {                                 \
29     PFOUR_BYTE _from_, _to_;      \
30     _from_ = ((PFOUR_BYTE)&(b));  \
31     _to_ =   ((PFOUR_BYTE)&(a));  \
32     __asm mov ebx,_from_          \
33     __asm mov eax,[ebx]           \
34     __asm bswap eax               \
35     __asm mov ebx,_to_            \
36     __asm mov [ebx],eax           \
37 }
38 
39 int g_extended = 0;
40 int g_adapter_info = 0;
41 char* g_bb_list = NULL;
42 int gRadix = 16;
43 PADAPTERINFO g_AdapterInfo = NULL;
44 
45 BOOLEAN
46 ata_power_mode(
47     int bus_id,
48     int dev_id,
49     int power_mode
50     );
51 
print_help()52 void print_help() {
53     printf("Usage:\n"
54            "  atactl -<switches> c|s<controller id>:b<bus id>:d<device id>[:l<lun>]\n"
55            "Switches:\n"
56            "  l         (L)ist devices on SCSI and ATA controllers bus(es)\n"
57            "              Note: ATA Pri/Sec controller are usually represented\n"
58            "              as Scsi0/Scsi1 under NT-family OSes\n"
59            "  x         show e(X)tended info\n"
60            "  a         show (A)dapter info\n"
61            "  s         (S)can for new devices on ATA/SATA bus(es) (experimental)\n"
62            "  S         (S)can for new devices on ATA/SATA bus(es) (experimental)\n"
63            "              device, hidden with 'H' can be redetected\n"
64            "  h         (H)ide device on ATA/SATA bus for removal (experimental)\n"
65            "              device can be redetected\n"
66            "  H         (H)ide device on ATA/SATA bus (experimental)\n"
67            "              device can not be redetected until 'h' or 'S' is issued\n"
68            "  m [MODE]  set i/o (M)ode for device or revert to default\n"
69            "              available MODEs are PIO, PIO0-PIO5, DMA, WDMA0-WDMA2,\n"
70            "              UDMA33/44/66/100/133, UDMA0-UDMA5\n"
71            "  d [XXX]   lock ATA/SATA bus for device removal for XXX seconds or\n"
72            "              for %d seconds if no lock timeout specified.\n"
73            "              can be used with -h, -m or standalone.\n"
74            "  D [XXX]   disable device (turn into sleep mode) and lock ATA/SATA bus \n"
75            "              for device removal for XXX seconds or\n"
76            "              for %d seconds if no lock timeout specified.\n"
77            "              can be used with -h, -m or standalone.\n"
78            "  pX        change power state to X, where X is\n"
79            "              0 - active, 1 - idle, 2 - standby, 3 - sleep\n"
80            "  r         (R)eset device\n"
81            "  ba        (A)ssign (B)ad-block list\n"
82            "  bl        get assigned (B)ad-block (L)ist\n"
83            "  br        (R)eset assigned (B)ad-block list\n"
84            "  f         specify (F)ile for bad-block list\n"
85            "  n XXX     block (n)ubmering radix. XXX can be hex or dec\n"
86            "------\n"
87            "Examples:\n"
88            "  atactl -l\n"
89            "    will list all scsi buses and all connected devices\n"
90            "  atactl -m udma0 s2:b1:d1\n"
91            "    will switch device at Scsi2, bus 1, taget_id 1 to UDMA0 mode\n"
92            "  atactl -h -d 30 c1:b0:d0:l0 \n"
93            "    will hide Master (d0:l0) device on secondary (c1:b0) IDE channel\n"
94            "    and lock i/o on this channel for 30 seconds to ensure safety\n"
95            "    of removal process"
96            "------\n"
97            "Device address format:\n"
98            "\n"
99            "s<controller id> number of controller in system. Is assigned during hardware\n"
100            "                   detection. Usually s0/s1 are ATA Pri/Sec.\n"
101            "                   Note, due do NT internal design ATA controllers are represented\n"
102            "                   as SCSI controllers.\n"
103            "b<bus id>        For ATA controllers it is channel number.\n"
104            "                   Note, usually onboard controller is represented as 2 legacy\n"
105            "                   ISA-compatible single-channel controllers (Scsi9/Scsi1). Additional\n"
106            "                   ATA, ATA-RAID and some specific onboard controllers are represented\n"
107            "                   as multichannel controllers.\n"
108            "d<device id>     For ATA controllers d0 is Master, d1 is Slave.\n"
109            "l<lun>           Not used in ATA controller drivers, alway 0\n"
110            "------\n"
111            "Bad-block list format:\n"
112            "\n"
113            "# Comment\n"
114            "; Still one comment\n"
115            "hex: switch to hexadecimal mode\n"
116            "<Bad Area 1 Start LBA, e.g. FD50> <Block count 1, e.g. 60>\n"
117            "<Bad Area 2 Start LBA> <Block count 2>\n"
118            "...\n"
119            "dec: switch to decimal mode\n"
120            "<Bad Area N Start LBA, e.g. 16384> <Block count N, e.g. 48>\n"
121            "...\n"
122            "------\n"
123            "",
124            DEFAULT_REMOVAL_LOCK_TIMEOUT,
125            DEFAULT_REMOVAL_LOCK_TIMEOUT
126            );
127     exit(0);
128 }
129 
130 #define CMD_ATA_LIST  0x01
131 #define CMD_ATA_FIND  0x02
132 #define CMD_ATA_HIDE  0x03
133 #define CMD_ATA_MODE  0x04
134 #define CMD_ATA_RESET 0x05
135 #define CMD_ATA_BBLK  0x06
136 #define CMD_ATA_POWER 0x07
137 
138 HANDLE
ata_open_dev(char * Name)139 ata_open_dev(
140     char* Name
141     )
142 {
143     ULONG i;
144     HANDLE h;
145 
146     for(i=0; i<4; i++) {
147         h = CreateFile(Name,
148                        READ_CONTROL | GENERIC_READ | GENERIC_WRITE ,
149                        ((i & 1) ? 0 : FILE_SHARE_READ) | ((i & 2) ? 0 : FILE_SHARE_WRITE),
150                        NULL,
151                        OPEN_EXISTING,
152                        FILE_ATTRIBUTE_NORMAL,
153                        NULL);
154         if(h && (h != ((HANDLE)(-1))) ) {
155             return h;
156         }
157     }
158 
159     for(i=0; i<4; i++) {
160         h = CreateFile(Name,
161                        GENERIC_READ | GENERIC_WRITE ,
162                        ((i & 1) ? 0 : FILE_SHARE_READ) | ((i & 2) ? 0 : FILE_SHARE_WRITE),
163                        NULL,
164                        OPEN_EXISTING,
165                        FILE_ATTRIBUTE_NORMAL,
166                        NULL);
167         if(h && (h != ((HANDLE)(-1))) ) {
168             return h;
169         }
170     }
171 
172     for(i=0; i<4; i++) {
173         h = CreateFile(Name,
174                           GENERIC_READ,
175                           ((i & 1) ? 0 : FILE_SHARE_READ) | ((i & 2) ? 0 : FILE_SHARE_WRITE),
176                        NULL,
177                        OPEN_EXISTING,
178                        FILE_ATTRIBUTE_NORMAL,
179                        NULL);
180         if(h && (h != ((HANDLE)(-1))) ) {
181             return h;
182         }
183     }
184 
185     for(i=0; i<4; i++) {
186         h = CreateFile(Name,
187                        READ_CONTROL,
188                        ((i & 1) ? 0 : FILE_SHARE_READ) | ((i & 2) ? 0 : FILE_SHARE_WRITE),
189                        NULL,
190                        OPEN_EXISTING,
191                        FILE_ATTRIBUTE_NORMAL,
192                        NULL);
193         if(h && (h != ((HANDLE)(-1))) ) {
194             return h;
195         }
196     }
197 
198     return NULL;
199 } // end ata_open_dev()
200 
201 HANDLE
ata_open_file(char * Name,BOOLEAN create)202 ata_open_file(
203     char* Name,
204     BOOLEAN create
205     )
206 {
207     ULONG i;
208     HANDLE h;
209 
210     if(!Name) {
211         if(create) {
212             return GetStdHandle(STD_OUTPUT_HANDLE);
213         } else {
214             return GetStdHandle(STD_INPUT_HANDLE);
215         }
216     }
217 
218     for(i=0; i<4; i++) {
219         h = CreateFile(Name,
220                        create ? GENERIC_WRITE : GENERIC_READ ,
221                        ((i & 1) ? 0 : FILE_SHARE_READ) | ((i & 2) ? 0 : FILE_SHARE_WRITE),
222                        NULL,
223                        create ? CREATE_NEW : OPEN_EXISTING,
224                        FILE_ATTRIBUTE_NORMAL,
225                        NULL);
226         if(h && (h != ((HANDLE)(-1))) ) {
227             return h;
228         }
229     }
230 
231     return NULL;
232 } // end ata_open_file()
233 
234 void
ata_close_dev(HANDLE h)235 ata_close_dev(
236     HANDLE h
237     )
238 {
239     CloseHandle(h);
240 } // end ata_close_dev()
241 
242 int
ata_send_ioctl(HANDLE h,PSCSI_ADDRESS addr,PCCH Signature,ULONG Ioctl,PVOID inBuffer,ULONG inBufferLength,PVOID outBuffer,ULONG outBufferLength,PULONG returned)243 ata_send_ioctl(
244     HANDLE h,
245     PSCSI_ADDRESS addr,
246     PCCH   Signature,
247     ULONG  Ioctl,
248     PVOID  inBuffer,
249     ULONG  inBufferLength,
250     PVOID  outBuffer,
251     ULONG  outBufferLength,
252     PULONG returned
253     )
254 {
255     ULONG status;
256     PUNIATA_CTL AtaCtl;
257     ULONG data_len = max(inBufferLength, outBufferLength);
258     ULONG len;
259 
260     if(addr) {
261         len = data_len + offsetof(UNIATA_CTL, RawData);
262     } else {
263         len = data_len + sizeof(AtaCtl->hdr);
264     }
265     AtaCtl = (PUNIATA_CTL)GlobalAlloc(GMEM_FIXED, len);
266     AtaCtl->hdr.HeaderLength = sizeof(SRB_IO_CONTROL);
267     if(addr) {
268         AtaCtl->hdr.Length = data_len + offsetof(UNIATA_CTL, RawData) - sizeof(AtaCtl->hdr);
269     } else {
270         AtaCtl->hdr.Length = data_len;
271     }
272 
273     memcpy(&AtaCtl->hdr.Signature, Signature, 8);
274 
275     AtaCtl->hdr.Timeout = 10000;
276     AtaCtl->hdr.ControlCode = Ioctl;
277     AtaCtl->hdr.ReturnCode = 0;
278 
279     if(addr) {
280         AtaCtl->addr = *addr;
281         AtaCtl->addr.Length = sizeof(AtaCtl->addr);
282     }
283 
284     if(outBufferLength) {
285         if(addr) {
286             memset(&AtaCtl->RawData, 0, outBufferLength);
287         } else {
288             memset(&AtaCtl->addr, 0, outBufferLength);
289         }
290     }
291 
292     if(inBuffer && inBufferLength) {
293         if(addr) {
294             memcpy(&AtaCtl->RawData, inBuffer, inBufferLength);
295         } else {
296             memcpy(&AtaCtl->addr, inBuffer, inBufferLength);
297         }
298     }
299 
300     status = DeviceIoControl(h,
301                              IOCTL_SCSI_MINIPORT,
302                              AtaCtl,
303                              len,
304                              AtaCtl,
305                              len,
306                              returned,
307                              FALSE);
308 
309     if(outBuffer && outBufferLength) {
310         if(addr) {
311             memcpy(outBuffer, &AtaCtl->RawData, outBufferLength);
312         } else {
313             memcpy(outBuffer, &AtaCtl->addr, outBufferLength);
314         }
315     }
316     GlobalFree(AtaCtl);
317 
318     if(!status) {
319         status = GetLastError();
320         return FALSE;
321     }
322     return TRUE;
323 } // end ata_send_ioctl()
324 
325 int
ata_send_scsi(HANDLE h,PSCSI_ADDRESS addr,PCDB cdb,UCHAR cdbLength,PVOID Buffer,ULONG BufferLength,BOOLEAN DataIn,PSENSE_DATA senseData,PULONG returned)326 ata_send_scsi(
327     HANDLE h,
328     PSCSI_ADDRESS addr,
329     PCDB   cdb,
330     UCHAR  cdbLength,
331     PVOID  Buffer,
332     ULONG  BufferLength,
333     BOOLEAN DataIn,
334     PSENSE_DATA senseData,
335     PULONG returned
336     )
337 {
338     ULONG status;
339     PSCSI_PASS_THROUGH_WITH_BUFFERS sptwb;
340     //ULONG data_len = BufferLength;
341     ULONG len;
342 
343     len = BufferLength + offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
344 
345     sptwb = (PSCSI_PASS_THROUGH_WITH_BUFFERS)GlobalAlloc(GMEM_FIXED, len);
346     if(!sptwb) {
347         return FALSE;
348     }
349     memset(sptwb, 0, offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf));
350 
351     sptwb->spt.Length = sizeof(SCSI_PASS_THROUGH);
352     sptwb->spt.PathId   = addr->PathId;
353     sptwb->spt.TargetId = addr->TargetId;
354     sptwb->spt.Lun      = addr->Lun;
355     sptwb->spt.CdbLength = cdbLength;
356     sptwb->spt.SenseInfoLength = 24;
357     sptwb->spt.DataIn = Buffer ? (DataIn ? SCSI_IOCTL_DATA_IN : SCSI_IOCTL_DATA_OUT) : 0;
358     sptwb->spt.DataTransferLength = BufferLength;
359     sptwb->spt.TimeOutValue = 10;
360     sptwb->spt.DataBufferOffset =
361        offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf);
362     sptwb->spt.SenseInfoOffset =
363        offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucSenseBuf);
364     memcpy(&sptwb->spt.Cdb, cdb, cdbLength);
365 
366     if(Buffer && !DataIn) {
367         memcpy(&sptwb->ucSenseBuf, Buffer, BufferLength);
368     }
369 
370     status = DeviceIoControl(h,
371                              IOCTL_SCSI_PASS_THROUGH,
372                              sptwb,
373                              (Buffer && !DataIn) ? len : sizeof(SCSI_PASS_THROUGH),
374                              sptwb,
375                              (Buffer && DataIn) ? len : offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf),
376                              returned,
377                              FALSE);
378 
379     if(Buffer && DataIn) {
380         memcpy(Buffer, &sptwb->ucDataBuf, BufferLength);
381     }
382     if(senseData) {
383         memcpy(senseData, &sptwb->ucSenseBuf, sizeof(sptwb->ucSenseBuf));
384     }
385 
386     GlobalFree(sptwb);
387 
388     if(!status) {
389         status = GetLastError();
390         return FALSE;
391     }
392     return TRUE;
393 } // end ata_send_scsi()
394 
395 IO_SCSI_CAPABILITIES g_capabilities;
396 UCHAR g_inquiry_buffer[2048];
397 
398 void
ata_mode_to_str(char * str,int mode)399 ata_mode_to_str(
400     char* str,
401     int mode
402     )
403 {
404     if(mode > ATA_SA600) {
405         sprintf(str, "SATA-600+");
406     } else
407     if(mode >= ATA_SA600) {
408         sprintf(str, "SATA-600");
409     } else
410     if(mode >= ATA_SA300) {
411         sprintf(str, "SATA-300");
412     } else
413     if(mode >= ATA_SA150) {
414         sprintf(str, "SATA-150");
415     } else
416     if(mode >= ATA_UDMA0) {
417         sprintf(str, "UDMA%d", mode-ATA_UDMA0);
418     } else
419     if(mode >= ATA_WDMA0) {
420         sprintf(str, "WDMA%d", mode-ATA_WDMA0);
421     } else
422     if(mode >= ATA_SDMA0) {
423         sprintf(str, "SDMA%d", mode-ATA_SDMA0);
424     } else
425     if(mode >= ATA_PIO0) {
426         sprintf(str, "PIO%d", mode-ATA_PIO0);
427     } else
428     if(mode == ATA_PIO_NRDY) {
429         sprintf(str, "PIO nRDY");
430     } else
431     {
432         sprintf(str, "PIO");
433     }
434 } // end ata_mode_to_str()
435 
436 #define check_atamode_str(str, mode) \
437    (!_stricmp(str, "UDMA" #mode) || \
438     !_stricmp(str, "UDMA-" #mode) || \
439     !_stricmp(str, "ATA-" #mode) || \
440     !_stricmp(str, "ATA#" #mode))
441 
442 int
ata_str_to_mode(char * str)443 ata_str_to_mode(
444     char* str
445     )
446 {
447     int mode;
448     size_t len;
449 
450     if(!_stricmp(str, "SATA600"))
451         return ATA_SA600;
452     if(!_stricmp(str, "SATA300"))
453         return ATA_SA300;
454     if(!_stricmp(str, "SATA150"))
455         return ATA_SA150;
456     if(!_stricmp(str, "SATA"))
457         return ATA_SA150;
458 
459     if(check_atamode_str(str, 16))
460         return ATA_UDMA0;
461     if(check_atamode_str(str, 25))
462         return ATA_UDMA1;
463     if(check_atamode_str(str, 33))
464         return ATA_UDMA2;
465     if(check_atamode_str(str, 44))
466         return ATA_UDMA3;
467     if(check_atamode_str(str, 66))
468         return ATA_UDMA4;
469     if(check_atamode_str(str, 100))
470         return ATA_UDMA5;
471     if(check_atamode_str(str, 122))
472         return ATA_UDMA6;
473 
474     len = strlen(str);
475 
476     if(len >= 4 && !_memicmp(str, "UDMA", 4)) {
477         if(len == 4)
478             return ATA_UDMA0;
479         if(len > 5)
480             return -1;
481         mode = str[4] - '0';
482         if(mode < 0 || mode > 7)
483             return -1;
484         return ATA_UDMA0+mode;
485     }
486     if(len >= 4 && !_memicmp(str, "WDMA", 4)) {
487         if(len == 4)
488             return ATA_WDMA0;
489         if(len > 5)
490             return -1;
491         mode = str[4] - '0';
492         if(mode < 0 || mode > 2)
493             return -1;
494         return ATA_WDMA0+mode;
495     }
496     if(len >= 4 && !_memicmp(str, "SDMA", 4)) {
497         if(len == 4)
498             return ATA_SDMA0;
499         if(len > 5)
500             return -1;
501         mode = str[4] - '0';
502         if(mode < 0 || mode > 2)
503             return -1;
504         return ATA_SDMA0+mode;
505     }
506     if(len == 4 && !_memicmp(str, "DMA", 4)) {
507         return ATA_SDMA0;
508     }
509     if(len >= 3 && !_memicmp(str, "PIO", 3)) {
510         if(len == 3)
511             return ATA_PIO;
512         if(len > 4)
513             return -1;
514         mode = str[3] - '0';
515         if(mode < 0 || mode > 5)
516             return -1;
517         return ATA_PIO0+mode;
518     }
519 
520     return -1;
521 } // end ata_str_to_mode()
522 
523 ULONG
EncodeVendorStr(OUT char * Buffer,IN PUCHAR Str,IN ULONG Length,IN ULONG Xorer)524 EncodeVendorStr(
525    OUT char*  Buffer,
526     IN PUCHAR Str,
527     IN ULONG  Length,
528     IN ULONG  Xorer
529     )
530 {
531     ULONG i,j;
532     UCHAR a;
533 
534     for(i=0, j=0; i<Length; i++, j++) {
535         a = Str[i ^ Xorer];
536         if(!a) {
537             Buffer[j] = 0;
538             return j;
539         } else
540         if(a == ' ') {
541             Buffer[j] = '_';
542         } else
543         if((a == '_') ||
544            (a == '#') ||
545            (a == '\\') ||
546            (a == '\"') ||
547            (a == '\'') ||
548            (a <  ' ') ||
549            (a >= 127)) {
550             Buffer[j] = '#';
551             j++;
552             sprintf(Buffer+j, "%2.2x", a);
553             j++;
554         } else {
555             Buffer[j] = a;
556         }
557     }
558     Buffer[j] = 0;
559     return j;
560 } // end EncodeVendorStr()
561 
562 HKEY
ata_get_bblist_regh(IN PIDENTIFY_DATA ident,OUT char * DevSerial,BOOLEAN read_only)563 ata_get_bblist_regh(
564     IN PIDENTIFY_DATA ident,
565     OUT char* DevSerial,
566     BOOLEAN read_only
567     )
568 {
569     HKEY hKey = NULL;
570     HKEY hKey2 = NULL;
571     ULONG Length;
572     REGSAM access = read_only ? KEY_READ : KEY_ALL_ACCESS;
573 
574     Length = EncodeVendorStr(DevSerial, (PUCHAR)ident->ModelNumber, sizeof(ident->ModelNumber), 0x01);
575     DevSerial[Length] = '-';
576     Length++;
577     Length += EncodeVendorStr(DevSerial+Length, ident->SerialNumber, sizeof(ident->SerialNumber), 0x01);
578 
579     if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\UniATA", NULL, access, &hKey) != ERROR_SUCCESS) {
580         hKey = NULL;
581         goto exit;
582     }
583     if(RegOpenKey(hKey, "Parameters", &hKey2) != ERROR_SUCCESS) {
584         hKey2 = NULL;
585         if(read_only || (RegCreateKey(hKey, "Parameters", &hKey2) != ERROR_SUCCESS)) {
586             hKey2 = NULL;
587             goto exit;
588         }
589     }
590     RegCloseKey(hKey2);
591     if(RegOpenKey(hKey, "Parameters\\BadBlocks", &hKey2) != ERROR_SUCCESS) {
592         hKey2 = NULL;
593         if(read_only || (RegCreateKey(hKey, "Parameters\\BadBlocks", &hKey2) != ERROR_SUCCESS)) {
594             hKey2 = NULL;
595             goto exit;
596         }
597     }
598 
599 exit:
600     if(hKey)
601         RegCloseKey(hKey);
602 
603     return hKey2;
604 } // end ata_get_bblist_regh()
605 
606 IDENTIFY_DATA   g_ident;
607 
608 int
ata_check_unit(HANDLE h,int dev_id)609 ata_check_unit(
610     HANDLE h,    // handle to ScsiXXX:
611     int dev_id
612     )
613 {
614     ULONG status;
615     ULONG returned;
616 
617     PSCSI_ADAPTER_BUS_INFO  adapterInfo;
618     PSCSI_INQUIRY_DATA inquiryData;
619     SCSI_ADDRESS addr;
620     ULONG i, j;
621     int l_dev_id;
622     ULONG len;
623     GETTRANSFERMODE IoMode;
624     PSENDCMDOUTPARAMS pout;
625     PIDENTIFY_DATA   ident;
626     PINQUIRYDATA     scsi_ident;
627     char buff[sizeof(SENDCMDOUTPARAMS)+/*sizeof(IDENTIFY_DATA)*/2048];
628     char mode_str[12];
629     //ULONG bus_id = (dev_id >> 24) & 0xff;
630     BOOLEAN found = FALSE;
631     SENDCMDINPARAMS pin;
632     int io_mode = -1;
633     char SerNum[128];
634     char DevSerial[128];
635     char lun_str[10];
636     HKEY hKey2;
637     ULONGLONG max_lba = -1;
638     USHORT chs[3] = { 0 };
639 
640     if(dev_id != -1) {
641         dev_id &= 0x00ffffff;
642     }
643     if(dev_id == 0x007f7f7f) {
644         return TRUE;
645     }
646 
647     pout = (PSENDCMDOUTPARAMS)buff;
648     ident = (PIDENTIFY_DATA)&(pout->bBuffer);
649 
650     status = DeviceIoControl(h,
651                              IOCTL_SCSI_GET_INQUIRY_DATA,
652                              NULL,
653                              0,
654                              g_inquiry_buffer,
655                              sizeof(g_inquiry_buffer),
656                              &returned,
657                              FALSE);
658 
659     if(!status) {
660         printf("Can't get device info\n");
661         return FALSE;
662     }
663 
664     // Note: adapterInfo->NumberOfBuses is 1 greater than g_AdapterInfo->NumberChannels
665     // because of virtual communication port
666     adapterInfo = (PSCSI_ADAPTER_BUS_INFO)g_inquiry_buffer;
667     for (i = 0; i+1 < adapterInfo->NumberOfBuses; i++) {
668         inquiryData = (PSCSI_INQUIRY_DATA) (g_inquiry_buffer +
669             adapterInfo->BusData[i].InquiryDataOffset);
670 
671         if(g_extended && g_AdapterInfo && g_AdapterInfo->ChanHeaderLengthValid &&
672               g_AdapterInfo->NumberChannels < i) {
673             PCHANINFO ChanInfo;
674 
675             ChanInfo = (PCHANINFO)
676                          (((PCHAR)g_AdapterInfo)+
677                             sizeof(ADAPTERINFO)+
678                             g_AdapterInfo->ChanHeaderLength*i);
679 
680             io_mode = ChanInfo->MaxTransferMode;
681             if(io_mode != -1) {
682                 ata_mode_to_str(mode_str, io_mode);
683             } else {
684                 mode_str[0] = 0;
685             }
686             printf(" b%lu [%s]\n",
687                 i,
688                 mode_str
689                 );
690         }
691 
692         while (adapterInfo->BusData[i].InquiryDataOffset) {
693             /*
694             if(dev_id/adapterInfo->BusData[i].NumberOfLogicalUnits ==
695                inquiryData->TargetId &&
696                dev_id%adapterInfo->BusData[i].NumberOfLogicalUnits ==
697                inquiryData->Lun) {
698                 printf(" %d   %d  %3d    %s    %.28s ",
699                 i,
700                 inquiryData->TargetId,
701                 inquiryData->Lun,
702                 (inquiryData->DeviceClaimed) ? "Y" : "N",
703                 &inquiryData->InquiryData[8]);
704             }*/
705             l_dev_id = (i << 16) | ((ULONG)(inquiryData->TargetId) << 8) | inquiryData->Lun;
706 
707             if(l_dev_id == dev_id || dev_id == -1) {
708 
709                 scsi_ident = (PINQUIRYDATA)&(inquiryData->InquiryData);
710                 if(!memcmp(&(scsi_ident->VendorId[0]), UNIATA_COMM_PORT_VENDOR_STR, 24)) {
711                     // skip communication port
712                     goto next_dev;
713                 }
714 
715                 found = TRUE;
716 
717                 if(inquiryData->Lun) {
718                     sprintf(lun_str, ":l%d", inquiryData->Lun);
719                 } else {
720                     sprintf(lun_str, "  ");
721                 }
722 
723 
724                 /*
725                 for (j = 0; j < 8; j++) {
726                     printf("%02X ", inquiryData->InquiryData[j]);
727                 }
728                 */
729 
730                 addr.Length = sizeof(addr);
731                 addr.PortNumber = -1;
732                 addr.PathId   = inquiryData->PathId;
733                 addr.TargetId = inquiryData->TargetId;
734                 addr.Lun      = inquiryData->Lun;
735                 status = ata_send_ioctl(h, &addr, "-UNIATA-",
736                                         IOCTL_SCSI_MINIPORT_UNIATA_GET_MODE,
737                                         NULL, 0,
738                                         &IoMode, sizeof(IoMode),
739                                         &returned);
740                 if(status) {
741                     //io_mode = min(IoMode.CurrentMode, IoMode.MaxMode);
742                     io_mode = IoMode.PhyMode;
743                     if(!io_mode) {
744                         io_mode = min(max(IoMode.CurrentMode,IoMode.OrigMode),IoMode.MaxMode);
745                     }
746                 } else {
747                     io_mode = -1;
748                 }
749 
750                 memset(&pin, 0, sizeof(pin));
751                 memset(buff, 0, sizeof(buff));
752                 pin.irDriveRegs.bCommandReg = ID_CMD;
753                 // this is valid for IDE/ATA, where only 2 devices can be attached to the bus.
754                 // probably, we shall change this in future to support SATA splitters
755                 pin.bDriveNumber = inquiryData->PathId*2+inquiryData->TargetId;
756 
757                 status = ata_send_ioctl(h, NULL, "SCSIDISK",
758                                         IOCTL_SCSI_MINIPORT_IDENTIFY,
759                                         &pin, sizeof(pin),
760                                         buff, sizeof(buff),
761                                         &returned);
762 
763                 if(!status) {
764                     memset(&pin, 0, sizeof(pin));
765                     memset(buff, 0, sizeof(buff));
766                     pin.irDriveRegs.bCommandReg = ATAPI_ID_CMD;
767                     // this is valid for IDE/ATA, where only 2 devices can be attached to the bus.
768                     // probably, we shall change this in future to support SATA splitters
769                     pin.bDriveNumber = inquiryData->PathId*2+inquiryData->TargetId;
770 
771                     status = ata_send_ioctl(h, NULL, "SCSIDISK",
772                                             IOCTL_SCSI_MINIPORT_IDENTIFY,
773                                             &pin, sizeof(pin),
774                                             buff, sizeof(buff),
775                                             &returned);
776 
777                 }
778 
779                 if(!g_extended) {
780                     printf("  b%lu:d%d%s    %24.24s %4.4s ",
781                         i,
782                         inquiryData->TargetId,
783                         lun_str,
784                         /*(inquiryData->DeviceClaimed) ? "Y" : "N",*/
785                         (g_extended ? (PUCHAR)"" : &scsi_ident->VendorId[0]),
786                         (g_extended ? (PUCHAR)"" : &scsi_ident->ProductRevisionLevel[0])
787                         );
788                 } else {
789                     printf("  b%lu:d%d%s ",
790                         i,
791                         inquiryData->TargetId,
792                         lun_str
793                         );
794                 }
795 
796                 if(status) {
797                     if(io_mode == -1) {
798                         io_mode = ata_cur_mode_from_ident(ident, IDENT_MODE_ACTIVE);
799                     }
800                 }
801                 if(io_mode != -1) {
802                     ata_mode_to_str(mode_str, io_mode);
803                 }
804                 if(!g_extended || !status) {
805                     if(g_extended) {
806                         printf("   %24.24s %4.4s ",
807                             (&inquiryData->InquiryData[8]),
808                             (&inquiryData->InquiryData[8+24])
809                             );
810                     }
811                     if(io_mode != -1) {
812                         printf(" %.12s ", mode_str);
813                     }
814                 }
815                 printf("\n");
816 
817                 if(g_extended) {
818                     if(status) {
819 
820                         BOOLEAN BlockMode_valid = TRUE;
821                         BOOLEAN print_geom = FALSE;
822 
823                         switch(ident->DeviceType) {
824                         case ATAPI_TYPE_DIRECT:
825                             if(ident->Removable) {
826                                 printf("    Floppy        ");
827                             } else {
828                                 printf("    Hard Drive    ");
829                             }
830                             break;
831                         case ATAPI_TYPE_TAPE:
832                             printf("    Tape Drive    ");
833                             break;
834                         case ATAPI_TYPE_CDROM:
835                             printf("    CD/DVD Drive  ");
836                             BlockMode_valid = FALSE;
837                             break;
838                         case ATAPI_TYPE_OPTICAL:
839                             printf("    Optical Drive ");
840                             BlockMode_valid = FALSE;
841                             break;
842                         default:
843                             printf("    Hard Drive    ");
844                             print_geom = TRUE;
845                             //MOV_DD_SWP(max_lba, ident->UserAddressableSectors);
846                             max_lba = ident->UserAddressableSectors;
847                             if(ident->FeaturesSupport.Address48) {
848                                max_lba = ident->UserAddressableSectors48;
849                             }
850                             //MOV_DW_SWP(chs[0], ident->NumberOfCylinders);
851                             //MOV_DW_SWP(chs[1], ident->NumberOfHeads);
852                             //MOV_DW_SWP(chs[2], ident->SectorsPerTrack);
853                             chs[0] = ident->NumberOfCylinders;
854                             chs[1] = ident->NumberOfHeads;
855                             chs[2] = ident->SectorsPerTrack;
856                             if(!max_lba) {
857                                 max_lba = (ULONG)(chs[0])*(ULONG)(chs[1])*(ULONG)(chs[2]);
858                             }
859                         }
860                         if(io_mode != -1) {
861                             printf("           %.12s\n", mode_str);
862                         }
863                         for (j = 0; j < 40; j += 2) {
864                             MOV_DW_SWP(SerNum[j], ((PUCHAR)ident->ModelNumber)[j]);
865                         }
866                         printf("    Mod: %40.40s\n", SerNum);
867                         for (j = 0; j < 8; j += 2) {
868                             MOV_DW_SWP(SerNum[j], ((PUCHAR)ident->FirmwareRevision)[j]);
869                         }
870                         printf("    Rev: %8.8s\n", SerNum);
871                         for (j = 0; j < 20; j += 2) {
872                             MOV_DW_SWP(SerNum[j], ((PUCHAR)ident->SerialNumber)[j]);
873                         }
874                         printf("    S/N: %20.20s\n", SerNum);
875 
876                         if(BlockMode_valid) {
877                             if(ident->MaximumBlockTransfer) {
878                                 printf("    Multi-block mode:        %u block%s\n", ident->MaximumBlockTransfer, ident->MaximumBlockTransfer == 1 ? "" : "s");
879                             } else {
880                                 printf("    Multi-block mode:        N/A\n");
881                             }
882                         }
883                         if(print_geom) {
884                             printf("    C/H/S:                   %u/%u/%u \n", chs[0], chs[1], chs[2]);
885                             printf("    LBA:                     %I64u \n", max_lba);
886                             if(max_lba < 2) {
887                                 printf("    Size:                    %lu kb\n", (ULONG)(max_lba/2));
888                             } else
889                             if(max_lba < 2*1024*1024) {
890                                 printf("    Size:                    %lu Mb\n", (ULONG)(max_lba/2048));
891                             } else
892                             if(max_lba < (ULONG)2*1024*1024*1024) {
893                                 printf("    Size:                    %lu.%lu (%lu) Gb\n", (ULONG)(max_lba/2048/1024),
894                                                                                   (ULONG)(((max_lba/2048)%1024)/10),
895                                                                                   (ULONG)(max_lba*512/1000/1000/1000)
896                                 );
897                             } else {
898                                 printf("    Size:                    %lu.%lu (%lu) Tb\n", (ULONG)(max_lba/2048/1024/1024),
899                                                                                   (ULONG)((max_lba/2048/1024)%1024)/10,
900                                                                                   (ULONG)(max_lba*512/1000/1000/1000)
901                                 );
902                             }
903                         }
904                         len = 0;
905                         if((hKey2 = ata_get_bblist_regh(ident, DevSerial, TRUE))) {
906                             if(RegQueryValueEx(hKey2, DevSerial, NULL, NULL, NULL, &len) == ERROR_SUCCESS) {
907                                 printf("    !!! Assigned bad-block list !!!\n");
908                             }
909                             RegCloseKey(hKey2);
910                         }
911                     } else {
912                         switch(scsi_ident->DeviceType) {
913                         case DIRECT_ACCESS_DEVICE:
914                             if(scsi_ident->RemovableMedia) {
915                                 printf("    Floppy        ");
916                             } else {
917                                 printf("    Hard Drive    ");
918                             }
919                             break;
920                         case SEQUENTIAL_ACCESS_DEVICE:
921                             printf("    Tape Drive    ");
922                             break;
923                         case PRINTER_DEVICE:
924                             printf("    Printer       ");
925                             break;
926                         case PROCESSOR_DEVICE:
927                             printf("    Processor     ");
928                             break;
929                         case WRITE_ONCE_READ_MULTIPLE_DEVICE:
930                             printf("    WORM Drive    ");
931                             break;
932                         case READ_ONLY_DIRECT_ACCESS_DEVICE:
933                             printf("    CDROM Drive   ");
934                             break;
935                         case SCANNER_DEVICE:
936                             printf("    Scanner       ");
937                             break;
938                         case OPTICAL_DEVICE:
939                             printf("    Optical Drive ");
940                             break;
941                         case MEDIUM_CHANGER:
942                             printf("    Changer       ");
943                             break;
944                         case COMMUNICATION_DEVICE:
945                             printf("    Comm. device  ");
946                             break;
947                         }
948                         printf("\n");
949                     }
950                 }
951                 memcpy(&g_ident, ident, sizeof(IDENTIFY_DATA));
952             }
953 next_dev:
954             if (inquiryData->NextInquiryDataOffset == 0) {
955                 break;
956             }
957 
958             inquiryData = (PSCSI_INQUIRY_DATA) (g_inquiry_buffer +
959                 inquiryData->NextInquiryDataOffset);
960         }
961     }
962     if(!found) {
963         printf("  No device(s) found.\n");
964         return FALSE;
965     }
966 
967     return TRUE;
968 } // end ata_check_unit()
969 
970 BOOLEAN
ata_adapter_info(int bus_id,int print_info)971 ata_adapter_info(
972     int bus_id,
973     int print_info
974     )
975 {
976     char dev_name[64];
977     HANDLE h;
978     PADAPTERINFO AdapterInfo;
979     ULONG status;
980     ULONG returned;
981     SCSI_ADDRESS addr;
982     PCI_SLOT_NUMBER       slotData;
983     char mode_str[12];
984     ULONG len;
985 
986     sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id);
987     h = ata_open_dev(dev_name);
988     if(!h)
989         return FALSE;
990     addr.Length     = sizeof(addr);
991     addr.PortNumber = bus_id;
992 
993     len = sizeof(ADAPTERINFO)+sizeof(CHANINFO)*AHCI_MAX_PORT;
994     if(!g_AdapterInfo) {
995         AdapterInfo = (PADAPTERINFO)GlobalAlloc(GMEM_FIXED, len);
996         if(!AdapterInfo) {
997             ata_close_dev(h);
998             return FALSE;
999         }
1000     } else {
1001         AdapterInfo = g_AdapterInfo;
1002     }
1003     memset(AdapterInfo, 0, len);
1004 
1005     status = ata_send_ioctl(h, &addr, "-UNIATA-",
1006                             IOCTL_SCSI_MINIPORT_UNIATA_ADAPTER_INFO,
1007                             AdapterInfo, len,
1008                             AdapterInfo, len,
1009                             &returned);
1010     if(status) {
1011         ata_mode_to_str(mode_str, AdapterInfo->MaxTransferMode);
1012     }
1013     printf("Scsi%d: %s     %s\n", bus_id, status ? "[UniATA]" : "", status ? mode_str : "");
1014     if(print_info) {
1015         if(!status) {
1016             printf("Can't get adapter info\n");
1017         } else {
1018             if(AdapterInfo->AdapterInterfaceType == PCIBus) {
1019                 slotData.u.AsULONG = AdapterInfo->slotNumber;
1020                 printf("  PCI Bus/Dev/Func:   %lu/%lu/%lu%s\n",
1021                     AdapterInfo->SystemIoBusNumber, slotData.u.bits.DeviceNumber, slotData.u.bits.FunctionNumber,
1022                     AdapterInfo->AdapterInterfaceType == AdapterInfo->OrigAdapterInterfaceType ? "" : " (ISA-Bridged)");
1023                 printf("  VendorId/DevId/Rev: %#04x/%#04x/%#02x\n",
1024                     (USHORT)(AdapterInfo->DevID >> 16),
1025                     (USHORT)(AdapterInfo->DevID & 0xffff),
1026                     (UCHAR)(AdapterInfo->RevID));
1027                 if(AdapterInfo->DeviceName[0]) {
1028                     printf("  Name:               %s\n", AdapterInfo->DeviceName);
1029                 }
1030             } else
1031             if(AdapterInfo->AdapterInterfaceType == Isa) {
1032                 printf("  ISA Bus\n");
1033             }
1034             printf("  IRQ: %ld\n", AdapterInfo->BusInterruptLevel);
1035         }
1036     }
1037     ata_close_dev(h);
1038     //GlobalFree(AdapterInfo);
1039     g_AdapterInfo = AdapterInfo;
1040     return status ? TRUE : FALSE;
1041 } // end ata_adapter_info()
1042 
1043 int
ata_check_controller(HANDLE h,PIO_SCSI_CAPABILITIES capabilities)1044 ata_check_controller(
1045     HANDLE h,    // handle to ScsiXXX:
1046     PIO_SCSI_CAPABILITIES capabilities
1047     )
1048 {
1049     ULONG status;
1050     ULONG returned;
1051 
1052     status = DeviceIoControl(h,
1053                              IOCTL_SCSI_GET_CAPABILITIES,
1054                              NULL,
1055                              0,
1056                              capabilities,
1057                              sizeof(IO_SCSI_CAPABILITIES),
1058                              &returned,
1059                              FALSE);
1060     return status;
1061 } // end ata_check_controller()
1062 
1063 BOOLEAN
ata_list(int bus_id,int dev_id)1064 ata_list(
1065     int bus_id,
1066     int dev_id
1067     )
1068 {
1069     char dev_name[64];
1070     HANDLE h;
1071     //BOOLEAN uniata_driven;
1072 
1073     if(bus_id == -1) {
1074         for(bus_id=0; TRUE; bus_id++) {
1075             if(!ata_list(bus_id, dev_id))
1076                 break;
1077         }
1078         return TRUE;
1079     }
1080     /*uniata_driven =*/ ata_adapter_info(bus_id, g_adapter_info);
1081     sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id);
1082     h = ata_open_dev(dev_name);
1083     if(!h)
1084         return FALSE;
1085     if(dev_id == -1) {
1086         ata_check_controller(h, &g_capabilities);
1087         ata_check_unit(h, -1);
1088         ata_close_dev(h);
1089         return TRUE;
1090     }
1091     ata_check_unit(h, dev_id | (bus_id << 24));
1092     ata_close_dev(h);
1093     return TRUE;
1094 } // end ata_list()
1095 
1096 BOOLEAN
ata_mode(int bus_id,int dev_id,int mode)1097 ata_mode(
1098     int bus_id,
1099     int dev_id,
1100     int mode
1101     )
1102 {
1103     char dev_name[64];
1104     HANDLE h;
1105     SETTRANSFERMODE IoMode;
1106     ULONG status;
1107     ULONG returned;
1108     SCSI_ADDRESS addr;
1109 
1110     if(dev_id == -1) {
1111         return FALSE;
1112     }
1113     sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id);
1114     h = ata_open_dev(dev_name);
1115     if(!h)
1116         return FALSE;
1117     addr.Length   = sizeof(addr);
1118     addr.PortNumber = bus_id;
1119     addr.PathId   = (UCHAR)(dev_id >> 16);
1120     addr.TargetId = (UCHAR)(dev_id >> 8);
1121     addr.Lun      = (UCHAR)(dev_id);
1122 
1123     IoMode.MaxMode = mode;
1124     IoMode.ApplyImmediately = FALSE;
1125 //    IoMode.ApplyImmediately = TRUE;
1126     IoMode.OrigMode = mode;
1127 
1128     status = ata_send_ioctl(h, &addr, "-UNIATA-",
1129                             IOCTL_SCSI_MINIPORT_UNIATA_SET_MAX_MODE,
1130                             &IoMode, sizeof(IoMode),
1131                             NULL, 0,
1132                             &returned);
1133     if(!status) {
1134         printf("Can't apply specified transfer mode\n");
1135     } else {
1136         ata_mode_to_str(dev_name, mode);
1137         printf("Transfer rate switched to %s\n", dev_name);
1138     }
1139     ata_close_dev(h);
1140     return status ? TRUE : FALSE;
1141 } // end ata_mode()
1142 
1143 BOOLEAN
ata_reset(int bus_id,int dev_id)1144 ata_reset(
1145     int bus_id,
1146     int dev_id
1147     )
1148 {
1149     char dev_name[64];
1150     HANDLE h;
1151     ULONG status;
1152     ULONG returned;
1153     SCSI_ADDRESS addr;
1154 
1155     if(dev_id == -1) {
1156         return FALSE;
1157     }
1158     sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id);
1159     h = ata_open_dev(dev_name);
1160     if(!h)
1161         return FALSE;
1162     addr.Length   = sizeof(addr);
1163     addr.PortNumber = bus_id;
1164     addr.PathId   = (UCHAR)(dev_id >> 16);
1165     addr.TargetId = (UCHAR)(dev_id >> 8);
1166     addr.Lun      = (UCHAR)(dev_id);
1167 
1168     if(addr.TargetId == 0x7f && addr.Lun == 0x7f) {
1169         addr.TargetId = (UCHAR)0xff;
1170         addr.Lun      = 0;
1171         printf("Resetting channel...\n");
1172     } else {
1173         printf("Resetting device...\n");
1174     }
1175 
1176     status = ata_send_ioctl(h, &addr, "-UNIATA-",
1177                             IOCTL_SCSI_MINIPORT_UNIATA_RESET_DEVICE,
1178                             NULL, 0,
1179                             NULL, 0,
1180                             &returned);
1181     if(!status) {
1182         printf("Reset failed\n");
1183     } else {
1184         printf("Channel reset done\n");
1185     }
1186     ata_close_dev(h);
1187     return TRUE;
1188 } // end ata_reset()
1189 
1190 BOOLEAN
ata_hide(int bus_id,int dev_id,int lock,int persistent_hide,int power_mode)1191 ata_hide(
1192     int bus_id,
1193     int dev_id,
1194     int lock,
1195     int persistent_hide,
1196     int power_mode
1197     )
1198 {
1199     char dev_name[64];
1200     HANDLE h;
1201     ULONG status;
1202     ULONG returned;
1203     SCSI_ADDRESS addr;
1204     ADDREMOVEDEV to;
1205 
1206     if(dev_id == -1) {
1207         return FALSE;
1208     }
1209 
1210     if(power_mode) {
1211         ata_power_mode(bus_id, dev_id, power_mode);
1212     }
1213 
1214     if(lock < 0) {
1215         lock = DEFAULT_REMOVAL_LOCK_TIMEOUT;
1216     }
1217     sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id);
1218     h = ata_open_dev(dev_name);
1219     if(!h)
1220         return FALSE;
1221     addr.Length   = sizeof(addr);
1222     addr.PortNumber = bus_id;
1223     addr.PathId   = (UCHAR)(dev_id >> 16);
1224     addr.TargetId = (UCHAR)(dev_id >> 8);
1225     addr.Lun      = (UCHAR)(dev_id);
1226 
1227     to.WaitForPhysicalLink = lock;
1228     to.Flags = persistent_hide ? UNIATA_REMOVE_FLAGS_HIDE : 0;
1229 
1230     printf("Deleting device.\n");
1231     if(lock) {
1232         printf("ATTENTION: you have %d seconds to disconnect cable\n", lock);
1233     }
1234     status = ata_send_ioctl(h, &addr, "-UNIATA-",
1235                             IOCTL_SCSI_MINIPORT_UNIATA_DELETE_DEVICE,
1236                             &to, sizeof(to),
1237                             NULL, 0,
1238                             &returned);
1239     if(!status) {
1240         printf("Delete failed\n");
1241     } else {
1242         printf("Device is detached\n");
1243     }
1244     ata_close_dev(h);
1245     return status ? TRUE : FALSE;
1246 } // end ata_hide()
1247 
1248 BOOLEAN
ata_scan(int bus_id,int dev_id,int lock,int unhide)1249 ata_scan(
1250     int bus_id,
1251     int dev_id,
1252     int lock,
1253     int unhide
1254     )
1255 {
1256     char dev_name[64];
1257     HANDLE h;
1258     ULONG status;
1259     ULONG returned;
1260     SCSI_ADDRESS addr;
1261     ADDREMOVEDEV to;
1262 
1263     if(dev_id == -1) {
1264         return FALSE;
1265     }
1266     if(lock < 0) {
1267         lock = DEFAULT_REMOVAL_LOCK_TIMEOUT;
1268     }
1269     sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id);
1270     h = ata_open_dev(dev_name);
1271     if(!h)
1272         return FALSE;
1273 
1274     if((UCHAR)(dev_id) != 0xff &&
1275        (UCHAR)(dev_id >> 8) != 0xff) {
1276 
1277         addr.Length   = sizeof(addr);
1278         addr.PortNumber = bus_id;
1279         addr.PathId   = (UCHAR)(dev_id >> 16);
1280         addr.TargetId = 0;
1281         addr.Lun      = 0;
1282 
1283         to.WaitForPhysicalLink = lock;
1284         to.Flags = unhide ? UNIATA_ADD_FLAGS_UNHIDE : 0;
1285 
1286         printf("Scanning bus for new devices.\n");
1287         if(lock) {
1288             printf("You have %d seconds to connect device.\n", lock);
1289         }
1290         status = ata_send_ioctl(h, &addr, "-UNIATA-",
1291                                 IOCTL_SCSI_MINIPORT_UNIATA_FIND_DEVICES,
1292                                 &to, sizeof(to),
1293                                 NULL, 0,
1294                                 &returned);
1295     } else {
1296         status = DeviceIoControl(h,
1297                                  IOCTL_SCSI_RESCAN_BUS,
1298                                  NULL, 0,
1299                                  NULL, 0,
1300                                  &returned,
1301                                  FALSE);
1302     }
1303     ata_close_dev(h);
1304     return status ? TRUE : FALSE;
1305 } // end ata_scan()
1306 
1307 CHAR*
_fgets(CHAR * string,int count,HANDLE stream)1308 _fgets(
1309     CHAR *string,
1310     int count,
1311     HANDLE stream
1312     )
1313 {
1314     CHAR *pointer = string;
1315     ULONG read_bytes;
1316 
1317     CHAR *retval = string;
1318     int ch = 0;
1319 
1320     if (count <= 0)
1321         return(NULL);
1322 
1323     while (--count)
1324     {
1325         if(!ReadFile(stream, &ch, 1, &read_bytes, NULL) ||
1326            !read_bytes)
1327         {
1328             if (pointer == string) {
1329                 retval=NULL;
1330                 goto done;
1331             }
1332             break;
1333         }
1334 
1335         if ((*pointer++ = (CHAR)ch) == '\n') {
1336             break;
1337         }
1338     }
1339 
1340     *pointer = '\0';
1341 
1342 /* Common return */
1343 done:
1344     return(retval);
1345 } // end _fgets()
1346 
1347 BOOLEAN
ata_bblk(int bus_id,int dev_id,int list_bb)1348 ata_bblk(
1349     int bus_id,
1350     int dev_id,
1351     int list_bb
1352     )
1353 {
1354     char dev_name[64];
1355     char tmp[64];
1356     char DevSerial[128];
1357     HANDLE h = NULL;
1358     HANDLE hf = NULL;
1359     ULONG status;
1360     ULONG returned;
1361     SCSI_ADDRESS addr;
1362     ULONG len;
1363     ULONG Length;
1364     BOOLEAN retval = FALSE;
1365     HKEY hKey2 = NULL;
1366     char* bblist = NULL;
1367     LONGLONG tmp_bb_lba;
1368     LONGLONG tmp_bb_len;
1369     char BB_Msg[256];
1370     int radix=gRadix;
1371     int i, j;
1372     ULONG b;
1373 
1374     if(dev_id == -1) {
1375         printf("\nERROR: Target device/bus ID must be specified\n\n");
1376         print_help();
1377         return FALSE;
1378     }
1379     if(((dev_id >> 16) & 0xff) == 0xff) {
1380         printf("\nERROR: Target device bus number (channel) must be specified with b:<bus id>\n\n");
1381         print_help();
1382         return FALSE;
1383     }
1384     if(((dev_id >> 8) & 0xff) == 0xff) {
1385         printf("\nERROR: Target device ID must be specified with d:<device id>\n\n");
1386         print_help();
1387         return FALSE;
1388     }
1389     sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id);
1390     h = ata_open_dev(dev_name);
1391     if(!h) {
1392         if(bus_id == -1) {
1393             printf("Controller number must be specified\n");
1394         } else {
1395             printf("Can't open Controller %d\n", bus_id);
1396         }
1397         return FALSE;
1398     }
1399 
1400     if(list_bb == 0) {
1401         hf = ata_open_file(g_bb_list, FALSE);
1402         if(!hf) {
1403             printf("Can't open bad block list file:\n  %s\n", g_bb_list);
1404             ata_close_dev(h);
1405             return FALSE;
1406         }
1407 
1408         len = GetFileSize(hf, NULL);
1409         if(!len || len == INVALID_FILE_SIZE)
1410             goto exit;
1411         bblist = (char*)GlobalAlloc(GMEM_FIXED, len*8);
1412     }
1413 
1414     if(!ata_check_unit(h, dev_id | (bus_id << 24))) {
1415         goto exit;
1416     }
1417 
1418     hKey2 = ata_get_bblist_regh(&g_ident, DevSerial, list_bb==1);
1419     if(!hKey2) {
1420         printf("Can't open registry key:\n  HKLM\\SYSTEM\\CurrentControlSet\\Services\\UniATA\\Parameters\\BadBlocks\n");
1421         goto exit;
1422     }
1423 
1424     if(list_bb == -1) {
1425         if(RegDeleteValue(hKey2, DevSerial) != ERROR_SUCCESS) {
1426             printf("Can't delete registry value:\n  %s\n", DevSerial);
1427             goto exit;
1428         }
1429 
1430         addr.PortNumber = bus_id;
1431         addr.PathId   = (UCHAR)(dev_id >> 16);
1432         addr.TargetId = (UCHAR)(dev_id >> 8);
1433         addr.Lun      = (UCHAR)(dev_id);
1434 
1435         status = ata_send_ioctl(h, &addr, "-UNIATA-",
1436                                 IOCTL_SCSI_MINIPORT_UNIATA_RESETBB,
1437                                 NULL, 0,
1438                                 NULL, 0,
1439                                 &returned);
1440         if(!status) {
1441             printf("Bad block list shall be cleared after reboot.\n");
1442         } else {
1443             printf("Bad block list cleared\n");
1444         }
1445     } else
1446     if(list_bb == 0) {
1447         LONGLONG* pData = ((LONGLONG*)bblist);
1448         char a;
1449         int k, k0;
1450         Length=0;
1451         i=0;
1452         j=0;
1453         k=0;
1454         while(_fgets(BB_Msg, sizeof(BB_Msg), hf)) {
1455             j++;
1456             BB_Msg[sizeof(BB_Msg)-1] = 0;
1457             k=0;
1458             while((a = BB_Msg[k])) {
1459                 if(a == ' ' || a == '\t' || a == '\r') {
1460                     k++;
1461                     continue;
1462                 }
1463                 break;
1464             }
1465             if(!a || a == ';' || a == '#') {
1466                 continue;
1467             }
1468             if(!strncmp(BB_Msg+k, "hex:", 4)) {
1469                 radix=16;
1470                 continue;
1471             }
1472             if(!strncmp(BB_Msg+k, "dec:", 4)) {
1473                 radix=10;
1474                 continue;
1475             }
1476             k0 = k;
1477             while((a = BB_Msg[k])) {
1478                 if(a == ' ' || a == '\t' || a == '\r') {
1479                     BB_Msg[k] = '\t';
1480                 }
1481                 k++;
1482                 if(a == ';' || a == '#') {
1483                     break;
1484                 }
1485                 if(a >= '0' && a <= '9') {
1486                     continue;
1487                 }
1488                 if(radix == 16 && ((a >= 'A' && a <= 'F') || (a >= 'a' && a <= 'f'))) {
1489                     continue;
1490                 }
1491                 printf("Bad input BB list file:\n  %s\n", g_bb_list);
1492                 printf("Illegal character '%1.1s' in line %d:\n%s\n", BB_Msg+k-1, j, BB_Msg);
1493                 k0=-1;
1494                 break;
1495             }
1496             if(k0 == -1) {
1497                 continue;
1498             }
1499             k = k0;
1500             if(radix == 10) {
1501                 b = sscanf(BB_Msg+k, "%I64u\t%I64u", &tmp_bb_lba, &tmp_bb_len);
1502             } else {
1503                 b = sscanf(BB_Msg+k, "%I64x\t%I64x", &tmp_bb_lba, &tmp_bb_len);
1504             }
1505             if(b == 1) {
1506                 tmp_bb_len = 1;
1507             } else
1508             if(b != 2) {
1509                 printf("Bad input BB list file:\n  %s\n", g_bb_list);
1510                 printf("Can't parse line %d:\n%s\n", j, BB_Msg);
1511                 continue;
1512             }
1513             if(!tmp_bb_len) {
1514                 printf("Bad input BB list file:\n  %s\n", g_bb_list);
1515                 printf("BlockCount evaluated to 0 in line %d:\n%s\n", j, BB_Msg);
1516                 continue;
1517             }
1518             if(tmp_bb_lba < 0) {
1519                 printf("Bad input BB list file:\n  %s\n", g_bb_list);
1520                 printf("Start LBA evaluated to negative in line %d:\n%s\n", j, BB_Msg);
1521                 continue;
1522             }
1523             if(tmp_bb_len < 0) {
1524                 printf("Bad input BB list file:\n  %s\n", g_bb_list);
1525                 printf("BlockCount evaluated to negative in line %d:\n%s\n", j, BB_Msg);
1526                 continue;
1527             }
1528 
1529             if(i &&
1530                 (pData[(i-1)*2+1] == tmp_bb_lba)) {
1531                 pData[(i-1)*2+1]+=tmp_bb_len;
1532             } else {
1533                 pData[i*2+0]=tmp_bb_lba;
1534                 pData[i*2+1]=tmp_bb_lba+tmp_bb_len;
1535                 i++;
1536                 Length += sizeof(LONGLONG)*2;
1537             }
1538         }
1539 
1540         if(RegSetValueEx(hKey2, DevSerial, NULL, REG_BINARY, (const UCHAR*)bblist, Length) != ERROR_SUCCESS) {
1541             printf("Can't set registry value:\n  %s\n", DevSerial);
1542             goto exit;
1543         }
1544 /*
1545         addr.PortNumber = bus_id;
1546         addr.PathId   = (UCHAR)(dev_id >> 16);
1547         addr.TargetId = (UCHAR)(dev_id >> 8);
1548         addr.Lun      = (UCHAR)(dev_id);
1549 
1550         status = ata_send_ioctl(h, &addr, "-UNIATA-",
1551                                 IOCTL_SCSI_MINIPORT_UNIATA_SETBB,
1552                                 NULL, 0,
1553                                 NULL, 0,
1554                                 &returned);
1555 */
1556         printf("Bad block list shall be applied after reboot\n");
1557     } else {
1558         len = 0;
1559         returned = RegQueryValueEx(hKey2, DevSerial, NULL, NULL, NULL, &len);
1560         if(returned == 2) {
1561             printf("No bad block list assigned\n");
1562             goto exit;
1563         } else
1564         if(returned != ERROR_SUCCESS) {
1565             printf("Can't get registry value:\n  %s\n", DevSerial);
1566             goto exit;
1567         }
1568 
1569         hf = ata_open_file(g_bb_list, TRUE);
1570         if(!hf) {
1571             printf("Can't create bad block list file:\n  %s\n", g_bb_list);
1572             goto exit;
1573         }
1574 
1575         bblist = (char*)GlobalAlloc(GMEM_FIXED, len);
1576         if(RegQueryValueEx(hKey2, DevSerial, NULL, NULL, (UCHAR*)bblist, &len) != ERROR_SUCCESS) {
1577             printf("Can't get registry value:\n  %s\n", DevSerial);
1578             goto exit;
1579         }
1580         if(g_bb_list) {
1581             for (j = 0; j < 20; j += 2) {
1582                 MOV_DW_SWP(tmp[j], ((PUCHAR)(&g_ident.ModelNumber))[j]);
1583             }
1584             b = sprintf(BB_Msg, "#model: %20.20s\n", tmp);
1585             WriteFile(hf, BB_Msg, b, &returned, NULL);
1586             for (j = 0; j < 4; j += 2) {
1587                 MOV_DW_SWP(tmp[j], ((PUCHAR)(&g_ident.FirmwareRevision))[j]);
1588             }
1589             b = sprintf(BB_Msg, "#rev:   %4.4s\n",   tmp);
1590             WriteFile(hf, BB_Msg, b, &returned, NULL);
1591             for (j = 0; j < 20; j += 2) {
1592                 MOV_DW_SWP(tmp[j], ((PUCHAR)(&g_ident.SerialNumber))[j]);
1593             }
1594             b = sprintf(BB_Msg, "#s/n:   %20.20s\n", tmp);
1595             WriteFile(hf, BB_Msg, b, &returned, NULL);
1596             b = sprintf(BB_Msg, "#%s\n", DevSerial);
1597             WriteFile(hf, BB_Msg, b, &returned, NULL);
1598             b = sprintf(BB_Msg, "#Starting LBA\tNum. of Blocks\n");
1599             WriteFile(hf, BB_Msg, b, &returned, NULL);
1600             b = sprintf(BB_Msg, "hex:\n");
1601             WriteFile(hf, BB_Msg, b, &returned, NULL);
1602         } else {
1603             b = sprintf(BB_Msg, "Starting LBA\tNum. of Blocks (HEX)\n");
1604             WriteFile(hf, BB_Msg, b, &returned, NULL);
1605         }
1606         i = 0;
1607         while(len >= sizeof(LONGLONG)*2) {
1608             tmp_bb_lba = ((LONGLONG*)bblist)[i*2+0];
1609             tmp_bb_len = ((LONGLONG*)bblist)[i*2+1] - tmp_bb_lba;
1610             b = sprintf(BB_Msg, "%I64u\t%I64u\n", tmp_bb_lba, tmp_bb_len);
1611             WriteFile(hf, BB_Msg, b, &returned, NULL);
1612             i++;
1613             len -= sizeof(LONGLONG)*2;
1614         }
1615     }
1616     retval = TRUE;
1617 exit:
1618     if(hKey2)
1619         RegCloseKey(hKey2);
1620     if(bblist) {
1621         GlobalFree(bblist);
1622     }
1623     ata_close_dev(hf);
1624     ata_close_dev(h);
1625     return retval;
1626 } // end ata_bblk()
1627 
1628 BOOLEAN
ata_power_mode(int bus_id,int dev_id,int power_mode)1629 ata_power_mode(
1630     int bus_id,
1631     int dev_id,
1632     int power_mode
1633     )
1634 {
1635     char dev_name[64];
1636     HANDLE h;
1637     ULONG status;
1638     ULONG returned;
1639     SCSI_ADDRESS addr;
1640     CDB cdb;
1641     SENSE_DATA senseData;
1642 
1643     if(dev_id == -1) {
1644         return FALSE;
1645     }
1646     if(!power_mode) {
1647         return TRUE;
1648     }
1649 
1650     sprintf(dev_name, "\\\\.\\Scsi%d:", bus_id);
1651     h = ata_open_dev(dev_name);
1652     if(!h)
1653         return FALSE;
1654     addr.PortNumber = bus_id;
1655     addr.PathId   = (UCHAR)(dev_id >> 16);
1656     addr.TargetId = (UCHAR)(dev_id >> 8);
1657     addr.Lun      = (UCHAR)(dev_id);
1658 
1659     memset(&cdb, 0, sizeof(cdb));
1660     cdb.START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
1661     cdb.START_STOP.Immediate = 1;
1662     cdb.START_STOP.PowerConditions = power_mode;
1663     cdb.START_STOP.Start = (power_mode != StartStop_Power_Sleep);
1664 
1665     printf("Changing power state to ...\n");
1666 
1667     status = ata_send_scsi(h, &addr, &cdb, 6,
1668                             NULL, 0, FALSE,
1669                             &senseData, &returned);
1670     ata_close_dev(h);
1671     return status ? TRUE : FALSE;
1672 } // end ata_power_mode()
1673 
1674 int
ata_num_to_x_dev(char a)1675 ata_num_to_x_dev(
1676     char a
1677     )
1678 {
1679     if(a >= '0' && a <= '9')
1680         return a-'0';
1681     return -1;
1682 }
1683 
1684 int
main(int argc,char * argv[])1685 main (
1686     int argc,
1687     char* argv[]
1688     )
1689 {
1690     //ULONG Flags = 0;
1691     intptr_t i;
1692     uintptr_t j;
1693     char a;
1694     int bus_id = -1;
1695     int dev_id = -1;
1696     int cmd = 0;
1697     int lock = -1;
1698     int b_dev=-1, d_dev=-1, l_dev=0;
1699     int mode=-1;
1700     int list_bb=0;
1701     int persistent_hide=0;
1702     int power_mode=StartStop_Power_NoChg;
1703 
1704     printf("Console ATA control utility for Windows NT3.51/NT4/2000/XP/2003\n"
1705            "Version 0." UNIATA_VER_STR ", Copyright (c) Alexander A. Telyatnikov, 2003-2012\n"
1706            "Home site: http://alter.org.ua\n");
1707 
1708     for(i=1; i<argc; i++) {
1709         if(!argv[i])
1710             continue;
1711         if((a = argv[i][0]) != '-') {
1712             for(j=0; (a = argv[i][j]); j++) {
1713                 switch(a) {
1714                 case 'a' :
1715                 case 's' :
1716                 case 'c' :
1717                     j++;
1718                     bus_id = ata_num_to_x_dev(argv[i][j]);
1719                     break;
1720                 case 'b' :
1721                     j++;
1722                     b_dev = ata_num_to_x_dev(argv[i][j]);
1723                     break;
1724                 case 'd' :
1725                     j++;
1726                     d_dev = ata_num_to_x_dev(argv[i][j]);
1727                     break;
1728                 case 'l' :
1729                     j++;
1730                     l_dev = ata_num_to_x_dev(argv[i][j]);
1731                     break;
1732                 case ':' :
1733                     break;
1734                 default:
1735                     print_help();
1736                 }
1737             }
1738             continue;
1739         }
1740         j=1;
1741         while(argv[i] && (a = argv[i][j]) && (a != ' ') && (a != '\t')) {
1742             switch(a) {
1743             case 'l' :
1744                 if(cmd || lock>0) {
1745                     print_help();
1746                 }
1747                 cmd = CMD_ATA_LIST;
1748                 break;
1749             case 'x' :
1750                 g_extended = 1;
1751                 break;
1752             case 'a' :
1753                 g_adapter_info = 1;
1754                 break;
1755             case 'S' :
1756                 persistent_hide = 1;
1757             case 's' :
1758                 if(cmd || lock>0) {
1759                     print_help();
1760                 }
1761                 cmd = CMD_ATA_FIND;
1762                 d_dev = 0;
1763                 break;
1764             case 'H' :
1765                 persistent_hide = 1;
1766             case 'h' :
1767                 if(cmd) {
1768                     print_help();
1769                 }
1770                 cmd = CMD_ATA_HIDE;
1771                 d_dev = 0;
1772                 break;
1773             case 'm' :
1774                 if(cmd) {
1775                     print_help();
1776                 }
1777                 cmd = CMD_ATA_MODE;
1778                 i++;
1779                 if(!argv[i]) {
1780                     print_help();
1781                 }
1782                 mode = ata_str_to_mode(argv[i]);
1783                 if(mode == -1) {
1784                     i--;
1785                 } else {
1786                     j = strlen(argv[i])-1;
1787                 }
1788                 break;
1789             case 'r' :
1790                 if(cmd) {
1791                     print_help();
1792                 }
1793                 cmd = CMD_ATA_RESET;
1794                 break;
1795             case 'b' :
1796                 if(cmd) {
1797                     print_help();
1798                 }
1799                 switch(argv[i][j+1]) {
1800                 case 'l':
1801                     list_bb = 1;
1802                     break;
1803                 case 'a':
1804                     list_bb = 0;
1805                     break;
1806                 case 'r':
1807                     list_bb = -1;
1808                     break;
1809                 default:
1810                     j--;
1811                 }
1812                 j++;
1813                 cmd = CMD_ATA_BBLK;
1814                 break;
1815             case 'f' :
1816                 if(cmd != CMD_ATA_BBLK) {
1817                     print_help();
1818                 }
1819                 i++;
1820                 if(!argv[i]) {
1821                     print_help();
1822                 }
1823                 g_bb_list=argv[i];
1824                 j = strlen(argv[i])-1;
1825                 break;
1826             case 'p' :
1827                 if(cmd && (cmd != CMD_ATA_FIND) && (cmd != CMD_ATA_HIDE)) {
1828                     print_help();
1829                 }
1830                 switch(argv[i][j+1]) {
1831                 case '0':
1832                 case 'a':
1833                     // do nothing
1834                     break;
1835                 case '1':
1836                 case 'i':
1837                     power_mode = StartStop_Power_Idle;
1838                     break;
1839                 case '2':
1840                 case 's':
1841                     power_mode = StartStop_Power_Standby;
1842                     break;
1843                 case '3':
1844                 case 'p':
1845                     power_mode = StartStop_Power_Sleep;
1846                     break;
1847                 default:
1848                     j--;
1849                 }
1850                 j++;
1851                 if(power_mode && !cmd) {
1852                     cmd = CMD_ATA_POWER;
1853                 }
1854                 break;
1855             case 'D' :
1856                 power_mode = StartStop_Power_Sleep;
1857                 if(cmd && (cmd != CMD_ATA_HIDE)) {
1858                     print_help();
1859                 }
1860             case 'd' :
1861                 if(cmd && (cmd != CMD_ATA_FIND) && (cmd != CMD_ATA_HIDE) && (cmd != CMD_ATA_POWER)) {
1862                     print_help();
1863                 }
1864                 if(!cmd) {
1865                     cmd = CMD_ATA_HIDE;
1866                 }
1867                 i++;
1868                 if(!argv[i]) {
1869                     print_help();
1870                 }
1871                 if(!sscanf(argv[i], "%d", &lock)) {
1872                     lock = DEFAULT_REMOVAL_LOCK_TIMEOUT;
1873                     i--;
1874                 }
1875                 j = strlen(argv[i])-1;
1876                 break;
1877             case 'n' :
1878                 if(cmd != CMD_ATA_BBLK) {
1879                     print_help();
1880                 }
1881                 i++;
1882                 if(!argv[i]) {
1883                     print_help();
1884                 }
1885                 if(!strcmp(argv[i], "hex") ||
1886                    !strcmp(argv[i], "16")) {
1887                     gRadix = 16;
1888                 } else
1889                 if(!strcmp(argv[i], "dec") ||
1890                    !strcmp(argv[i], "10")) {
1891                     gRadix = 10;
1892                 } else {
1893                     print_help();
1894                 }
1895                 j = strlen(argv[i])-1;
1896                 break;
1897             case '?' :
1898             default:
1899                 print_help();
1900             }
1901             j++;
1902         }
1903     }
1904 
1905     if(g_adapter_info && !cmd) {
1906         cmd = CMD_ATA_LIST;
1907         b_dev = 127;
1908         d_dev = 127;
1909         l_dev = 127;
1910     } else
1911     if((d_dev == -1) && (b_dev != -1)) {
1912         d_dev = 127;
1913         l_dev = 127;
1914     }
1915 
1916     if((d_dev != -1) && (b_dev != -1)) {
1917         dev_id = (b_dev << 16) | (d_dev << 8) | l_dev;
1918     }
1919     if(cmd == CMD_ATA_LIST) {
1920         ata_list(bus_id, dev_id);
1921     } else
1922     if(cmd == CMD_ATA_MODE) {
1923         ata_mode(bus_id, dev_id, mode);
1924     } else
1925     if(cmd == CMD_ATA_RESET) {
1926         ata_reset(bus_id, dev_id);
1927     } else
1928     if(cmd == CMD_ATA_FIND) {
1929         ata_scan(bus_id, dev_id, lock, persistent_hide);
1930     } else
1931     if(cmd == CMD_ATA_HIDE) {
1932         ata_hide(bus_id, dev_id, lock, persistent_hide, power_mode);
1933     } else
1934     if(cmd == CMD_ATA_BBLK) {
1935         ata_bblk(bus_id, dev_id, list_bb);
1936     } else
1937     if(cmd == CMD_ATA_POWER) {
1938         ata_power_mode(bus_id, dev_id, power_mode);
1939     } else {
1940         print_help();
1941     }
1942     exit(0);
1943 }
1944 
1945