4 * Home page of code is: http://www.smartmontools.org
6 * Copyright (C) 2004-8 Yuri Dario
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
13 * You should have received a copy of the GNU General Public License
14 * (for example COPYING); if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 * Thanks to Daniela Engert for providing sample code for SMART ioctl access.
24 // These are needed to define prototypes for the functions defined below
34 // This is to include whatever prototypes you define in os_generic.h
37 // Needed by '-V' option (CVS versioning) of smartd/smartctl
38 const char *os_XXXX_c_cvsid
="$Id: os_os2.cpp 4431 2017-08-08 19:38:15Z chrfranke $" \
39 ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID
;
41 // global handle to device driver
44 // print examples for smartctl. You should modify this function so
45 // that the device paths are sensible for your OS, and to eliminate
46 // unsupported commands (eg, 3ware controllers).
47 void print_smartctl_examples(){
48 printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
49 #ifdef HAVE_GETOPT_LONG
51 " smartctl -a hd0 (Prints all SMART information)\n\n"
52 " smartctl --smart=on --offlineauto=on --saveauto=on hd0\n"
53 " (Enables SMART on first disk)\n\n"
54 " smartctl -t long hd0 (Executes extended disk self-test)\n\n"
55 " smartctl --attributes --log=selftest --quietmode=errorsonly hd0\n"
56 " (Prints Self-Test & Attribute errors)\n"
60 " smartctl -a hd0 (Prints all SMART on first disk with DANIS506)\n"
61 " smartctl -a ahci0 (Prints all SMART on first disk with OS2AHCI)\n"
62 " smartctl -s on -o on -S on hd0 (Enables SMART on first disk)\n"
63 " smartctl -t long hd0 (Executes extended disk self-test)\n"
64 " smartctl -A -l selftest -q errorsonly hd0\n"
65 " (Prints Self-Test & Attribute errors)\n"
71 static const char * skipdev(const char * s
)
73 return (!strncmp(s
, "/dev/", 5) ? s
+ 5 : s
);
76 // tries to guess device type given the name (a path). See utility.h
78 int guess_device_type (const char* dev_name
) {
80 //printf( "dev_name %s\n", dev_name);
81 dev_name
= skipdev(dev_name
);
82 if (!strncmp(dev_name
, "hd", 2) || !strncmp(dev_name
, "ahci", 4))
83 return CONTROLLER_ATA
;
84 return CONTROLLER_UNKNOWN
;
87 // makes a list of ATA or SCSI devices for the DEVICESCAN directive of
88 // smartd. Returns number N of devices, or -1 if out of
89 // memory. Allocates N+1 arrays: one of N pointers (devlist); the
90 // other N arrays each contain null-terminated character strings. In
91 // the case N==0, no arrays are allocated because the array of 0
92 // pointers has zero length, equivalent to calling malloc(0).
94 int make_device_names (char*** devlist
, const char* name
) {
98 const int max_dev
= 32; // scan only first 32 devices
100 // SCSI is not supported
101 if (strcmp (name
, "ATA") != 0)
107 HFILE danisDev
, ahciDev
;
108 bool is_danis
= 0, is_ahci
= 0;
110 rc
= DosOpen ((const char unsigned *)danisdev
, &danisDev
, &ActionTaken
, 0, FILE_SYSTEM
,
111 OPEN_ACTION_OPEN_IF_EXISTS
, OPEN_SHARE_DENYNONE
|
112 OPEN_FLAGS_NOINHERIT
| OPEN_ACCESS_READONLY
, NULL
);
116 rc
= DosOpen ((const char unsigned *)ahcidev
, &ahciDev
, &ActionTaken
, 0, FILE_SYSTEM
,
117 OPEN_ACTION_OPEN_IF_EXISTS
, OPEN_SHARE_DENYNONE
|
118 OPEN_FLAGS_NOINHERIT
| OPEN_ACCESS_READONLY
, NULL
);
122 // Count the devices.
125 DSKSP_CommandParameters Parms
;
128 struct ata_identify_device Id
;
130 for(int i
= 0; i
< max_dev
; i
++) {
132 Parms
.byPhysicalUnit
= i
;
133 rc
= DosDevIOCtl (ahciDev
, DSKSP_CAT_GENERIC
, DSKSP_GET_INQUIRY_DATA
,
134 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)&Id
, IDLen
, &IDLen
);
138 Parms
.byPhysicalUnit
= i
+ 0x80;
139 rc
= DosDevIOCtl (danisDev
, DSKSP_CAT_GENERIC
, DSKSP_GET_INQUIRY_DATA
,
140 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)&Id
, IDLen
, &IDLen
);
144 *devlist
= (char**)calloc (result
, sizeof (char *));
150 for(int i
= 0; i
< max_dev
; i
++) {
152 Parms
.byPhysicalUnit
= i
;
153 rc
= DosDevIOCtl (ahciDev
, DSKSP_CAT_GENERIC
, DSKSP_GET_INQUIRY_DATA
,
154 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)&Id
, IDLen
, &IDLen
);
156 asprintf(&(*devlist
)[index
], "ahci%d", i
);
157 if (! (*devlist
)[index
])
163 Parms
.byPhysicalUnit
= i
+ 0x80;
164 rc
= DosDevIOCtl (danisDev
, DSKSP_CAT_GENERIC
, DSKSP_GET_INQUIRY_DATA
,
165 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)&Id
, IDLen
, &IDLen
);
167 asprintf(&(*devlist
)[index
], "hd%d", i
);
168 if (! (*devlist
)[index
])
186 for (index
= 0; index
< result
; index
++)
187 if ((*devlist
)[index
])
188 free ((*devlist
)[index
]);
200 // Like open(). Return non-negative integer handle, only used by the
201 // functions below. type=="ATA" or "SCSI". If you need to store
202 // extra information about your devices, create a private internal
203 // array within this file (see os_freebsd.cpp for an example). If you
204 // can not open the device (permission denied, does not exist, etc)
205 // set errno as open() does and return <0.
206 int deviceopen(const char *pathname
, char * /* type */ ){
212 char * activedev
= NULL
;
214 pathname
= skipdev(pathname
);
216 if(strlen(pathname
) > strlen(danispref
)
217 && strncmp(pathname
, danispref
, strlen(danispref
)) == 0) {
218 fd
= strtol(pathname
+ strlen(danispref
), NULL
, 10) + 0x80;
219 activedev
= (char *)danisdev
;
222 if(strlen(pathname
) > strlen(ahcipref
)
223 && strncmp(pathname
, ahcipref
, strlen(ahcipref
)) == 0) {
224 fd
= strtol(pathname
+ strlen(ahcipref
), NULL
, 10);
225 activedev
= (char *)ahcidev
;
229 pout("Error: please specify hdX or ahciX device name\n");
232 //printf( "deviceopen pathname %s\n", pathname);
233 rc
= DosOpen ((const char unsigned *)activedev
, &hDevice
, &ActionTaken
, 0, FILE_SYSTEM
,
234 OPEN_ACTION_OPEN_IF_EXISTS
, OPEN_SHARE_DENYNONE
|
235 OPEN_FLAGS_NOINHERIT
| OPEN_ACCESS_READONLY
, NULL
);
238 snprintf(errmsg
,256,"Smartctl open driver %s failed (%lu)", activedev
, rc
);
247 // Like close(). Acts only on integer handles returned by
248 // deviceopen() above.
249 int deviceclose(int /* fd */){
258 // OS/2 direct ioctl interface to IBMS506$/OS2AHCI$
260 static int dani_ioctl( int device
, void* arg
)
262 unsigned char* buff
= (unsigned char*) arg
;
264 DSKSP_CommandParameters Parms
;
266 ULONG DLen
= 512; //sizeof (*buf);
269 // printf( "device %d, request 0x%x, arg[0] 0x%x, arg[2] 0x%x\n", device, request, buff[0], buff[2]);
271 Parms
.byPhysicalUnit
= device
;
273 case ATA_IDENTIFY_DEVICE
:
274 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_GENERIC
, DSKSP_GET_INQUIRY_DATA
,
275 (PVOID
)&Parms
, PLen
, &PLen
, (UCHAR
*)arg
+4, DLen
, &DLen
);
278 printf ("DANIS506 ATA DSKSP_GET_INQUIRY_DATA failed (%lu)\n", rc
);
284 case ATA_SMART_STATUS
:
285 DLen
= sizeof(value
);
286 // OS/2 already checks CL/CH in IBM1S506 code!! see s506rte.c (ddk)
287 // value: -1=not supported, 0=ok, 1=failing
288 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_GETSTATUS
,
289 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)&value
, DLen
, &DLen
);
292 printf ("DANIS506 ATA GET SMART_STATUS failed (%lu)\n", rc
);
295 buff
[4] = (unsigned char)value
;
297 case ATA_SMART_READ_VALUES
:
298 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_GET_ATTRIBUTES
,
299 (PVOID
)&Parms
, PLen
, &PLen
, (UCHAR
*)arg
+4, DLen
, &DLen
);
302 printf ("DANIS506 ATA GET DSKSP_SMART_GET_ATTRIBUTES failed (%lu)\n", rc
);
306 case ATA_SMART_READ_THRESHOLDS
:
307 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_GET_THRESHOLDS
,
308 (PVOID
)&Parms
, PLen
, &PLen
, (UCHAR
*)arg
+4, DLen
, &DLen
);
311 printf ("DANIS506 ATA GET DSKSP_SMART_GET_THRESHOLDS failed (%lu)\n", rc
);
315 case ATA_SMART_READ_LOG_SECTOR
:
316 buff
[4] = buff
[1]; // copy select field
317 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_GET_LOG
,
318 (PVOID
)&Parms
, PLen
, &PLen
, (UCHAR
*)arg
+4, DLen
, &DLen
);
321 printf ("DANIS506 ATA GET DSKSP_SMART_GET_LOG failed (%lu)\n", rc
);
325 case ATA_SMART_ENABLE
:
326 buff
[0] = 1; // enable
328 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_ONOFF
,
329 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)buff
, DLen
, &DLen
);
331 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc
);
335 case ATA_SMART_DISABLE
:
336 buff
[0] = 0; // disable
338 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_ONOFF
,
339 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)buff
, DLen
, &DLen
);
341 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc
);
346 case ATA_SMART_AUTO_OFFLINE
:
347 buff
[0] = buff
[3]; // select field
349 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_AUTO_OFFLINE
,
350 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)buff
, DLen
, &DLen
);
352 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc
);
357 case ATA_SMART_AUTOSAVE
:
358 buff
[0] = buff
[3]; // select field
360 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_AUTOSAVE_ONOFF
,
361 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)buff
, DLen
, &DLen
);
363 printf ("DANIS506 ATA DSKSP_SMART_AUTOSAVE_ONOFF failed (%lu)\n", rc
);
367 case ATA_SMART_IMMEDIATE_OFFLINE
:
368 buff
[0] = buff
[1]; // select field
370 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_EXEC_OFFLINE
,
371 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)buff
, DLen
, &DLen
);
373 printf ("DANIS506 ATA GET DSKSP_SMART_EXEC_OFFLINE failed (%lu)\n", rc
);
379 fprintf( stderr
, "device %d, arg[0] 0x%x, arg[2] 0x%x\n", device
, buff
[0], buff
[2]);
380 fprintf( stderr
, "unknown ioctl\n");
385 //case WIN_PIDENTIFY:
388 fprintf( stderr
, "unknown ioctl\n");
397 // Interface to ATA devices. See os_linux.cpp for the cannonical example.
398 // DETAILED DESCRIPTION OF ARGUMENTS
399 // device: is the integer handle provided by deviceopen()
400 // command: defines the different operations, see atacmds.h
401 // select: additional input data IF NEEDED (which log, which type of
403 // data: location to write output data, IF NEEDED (1 or 512 bytes).
404 // Note: not all commands use all arguments.
405 // RETURN VALUES (for all commands BUT command==STATUS_CHECK)
406 // -1 if the command failed
407 // 0 if the command succeeded,
408 // RETURN VALUES if command==STATUS_CHECK
409 // -1 if the command failed OR the disk SMART status can't be determined
410 // 0 if the command succeeded and disk SMART status is "OK"
411 // 1 if the command succeeded and disk SMART status is "FAILING"
413 // huge value of buffer size needed because HDIO_DRIVE_CMD assumes
414 // that buff[3] is the data size. Since the ATA_SMART_AUTOSAVE and
415 // ATA_SMART_AUTO_OFFLINE use values of 0xf1 and 0xf8 we need the space.
416 // Otherwise a 4+512 byte buffer would be enough.
417 #define STRANGE_BUFFER_LENGTH (4+512*0xf8)
419 int ata_command_interface(int device
, smart_command_set command
, int select
, char *data
){
420 unsigned char buff
[STRANGE_BUFFER_LENGTH
];
421 // positive: bytes to write to caller. negative: bytes to READ from
422 // caller. zero: non-data command
425 const int HDIO_DRIVE_CMD_OFFSET
= 4;
427 // See struct hd_drive_cmd_hdr in hdreg.h. Before calling ioctl()
428 // buff[0]: ATA COMMAND CODE REGISTER
429 // buff[1]: ATA SECTOR NUMBER REGISTER == LBA LOW REGISTER
430 // buff[2]: ATA FEATURES REGISTER
431 // buff[3]: ATA SECTOR COUNT REGISTER
433 // Note that on return:
434 // buff[2] contains the ATA SECTOR COUNT REGISTER
436 // clear out buff. Large enough for HDIO_DRIVE_CMD (4+512 bytes)
437 memset(buff
, 0, STRANGE_BUFFER_LENGTH
);
439 //printf( "command, select %d,%d\n", command, select);
440 buff
[0]=ATA_SMART_CMD
;
442 case CHECK_POWER_MODE
:
443 buff
[0]=ATA_CHECK_POWER_MODE
;
447 buff
[2]=ATA_SMART_READ_VALUES
;
451 case READ_THRESHOLDS
:
452 buff
[2]=ATA_SMART_READ_THRESHOLDS
;
457 buff
[2]=ATA_SMART_READ_LOG_SECTOR
;
465 buff
[0]=ATA_IDENTIFY_DEVICE
;
470 buff
[0]=ATA_IDENTIFY_PACKET_DEVICE
;
475 buff
[2]=ATA_SMART_ENABLE
;
479 buff
[2]=ATA_SMART_DISABLE
;
484 // this command only says if SMART is working. It could be
485 // replaced with STATUS_CHECK below.
486 buff
[2]=ATA_SMART_STATUS
;
490 buff
[2]=ATA_SMART_AUTO_OFFLINE
;
491 buff
[3]=select
; // YET NOTE - THIS IS A NON-DATA COMMAND!!
494 buff
[2]=ATA_SMART_AUTOSAVE
;
495 buff
[3]=select
; // YET NOTE - THIS IS A NON-DATA COMMAND!!
497 case IMMEDIATE_OFFLINE
:
498 buff
[2]=ATA_SMART_IMMEDIATE_OFFLINE
;
502 // // This command uses HDIO_DRIVE_TASK and has different syntax than
503 // // the other commands.
504 // buff[1]=ATA_SMART_STATUS;
507 pout("Unrecognized command %d in linux_ata_command_interface()\n"
508 "Please contact " PACKAGE_BUGREPORT
"\n", command
);
513 // We are now calling ioctl wrapper to the driver.
514 // TODO: use PASSTHRU in case of OS2AHCI driver
515 if ((dani_ioctl(device
, buff
)))
518 // There are two different types of ioctls(). The HDIO_DRIVE_TASK
520 if (command
==STATUS_CHECK
){
521 // Cyl low and Cyl high unchanged means "Good SMART status"
525 // These values mean "Bad SMART status"
529 // We haven't gotten output that makes sense; print out some debugging info
530 syserror("Error SMART Status command failed");
531 pout("Please get assistance from " PACKAGE_HOMEPAGE
"\n");
535 // CHECK POWER MODE command returns information in the Sector Count
536 // register (buff[3]). Copy to return data buffer.
537 if (command
==CHECK_POWER_MODE
)
538 buff
[HDIO_DRIVE_CMD_OFFSET
]=buff
[2];
540 // if the command returns data then copy it back
542 memcpy(data
, buff
+HDIO_DRIVE_CMD_OFFSET
, copydata
);
547 // Interface to SCSI devices. N/A under OS/2
548 int do_scsi_cmnd_io(int /* fd */, struct scsi_cmnd_io
* /* iop */, int /* report */) {
549 pout("SCSI interface is not implemented\n");