4 * Home page of code is: http://www.smartmontools.org
6 * Copyright (C) 2004-8 Yuri Dario
8 * SPDX-License-Identifier: GPL-2.0-or-later
13 * Thanks to Daniela Engert for providing sample code for SMART ioctl access.
17 // These are needed to define prototypes for the functions defined below
26 // This is to include whatever prototypes you define in os_generic.h
29 // Needed by '-V' option (CVS versioning) of smartd/smartctl
30 const char *os_XXXX_c_cvsid
="$Id: os_os2.cpp 4842 2018-12-02 16:07:26Z chrfranke $" \
31 ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID
;
33 // global handle to device driver
36 // print examples for smartctl. You should modify this function so
37 // that the device paths are sensible for your OS, and to eliminate
38 // unsupported commands (eg, 3ware controllers).
39 void print_smartctl_examples(){
40 printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
41 " smartctl -a hd0 (Prints all SMART information)\n\n"
42 " smartctl --smart=on --offlineauto=on --saveauto=on hd0\n"
43 " (Enables SMART on first disk)\n\n"
44 " smartctl -t long hd0 (Executes extended disk self-test)\n\n"
45 " smartctl --attributes --log=selftest --quietmode=errorsonly hd0\n"
46 " (Prints Self-Test & Attribute errors)\n"
51 static const char * skipdev(const char * s
)
53 return (!strncmp(s
, "/dev/", 5) ? s
+ 5 : s
);
56 // tries to guess device type given the name (a path). See utility.h
58 int guess_device_type (const char* dev_name
) {
60 //printf( "dev_name %s\n", dev_name);
61 dev_name
= skipdev(dev_name
);
62 if (!strncmp(dev_name
, "hd", 2) || !strncmp(dev_name
, "ahci", 4))
63 return CONTROLLER_ATA
;
64 return CONTROLLER_UNKNOWN
;
67 // makes a list of ATA or SCSI devices for the DEVICESCAN directive of
68 // smartd. Returns number N of devices, or -1 if out of
69 // memory. Allocates N+1 arrays: one of N pointers (devlist); the
70 // other N arrays each contain null-terminated character strings. In
71 // the case N==0, no arrays are allocated because the array of 0
72 // pointers has zero length, equivalent to calling malloc(0).
74 int make_device_names (char*** devlist
, const char* name
) {
78 const int max_dev
= 32; // scan only first 32 devices
80 // SCSI is not supported
81 if (strcmp (name
, "ATA") != 0)
87 HFILE danisDev
, ahciDev
;
88 bool is_danis
= 0, is_ahci
= 0;
90 rc
= DosOpen ((const char unsigned *)danisdev
, &danisDev
, &ActionTaken
, 0, FILE_SYSTEM
,
91 OPEN_ACTION_OPEN_IF_EXISTS
, OPEN_SHARE_DENYNONE
|
92 OPEN_FLAGS_NOINHERIT
| OPEN_ACCESS_READONLY
, NULL
);
96 rc
= DosOpen ((const char unsigned *)ahcidev
, &ahciDev
, &ActionTaken
, 0, FILE_SYSTEM
,
97 OPEN_ACTION_OPEN_IF_EXISTS
, OPEN_SHARE_DENYNONE
|
98 OPEN_FLAGS_NOINHERIT
| OPEN_ACCESS_READONLY
, NULL
);
102 // Count the devices.
105 DSKSP_CommandParameters Parms
;
108 struct ata_identify_device Id
;
110 for(int i
= 0; i
< max_dev
; i
++) {
112 Parms
.byPhysicalUnit
= i
;
113 rc
= DosDevIOCtl (ahciDev
, DSKSP_CAT_GENERIC
, DSKSP_GET_INQUIRY_DATA
,
114 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)&Id
, IDLen
, &IDLen
);
118 Parms
.byPhysicalUnit
= i
+ 0x80;
119 rc
= DosDevIOCtl (danisDev
, DSKSP_CAT_GENERIC
, DSKSP_GET_INQUIRY_DATA
,
120 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)&Id
, IDLen
, &IDLen
);
124 *devlist
= (char**)calloc (result
, sizeof (char *));
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
);
136 asprintf(&(*devlist
)[index
], "ahci%d", i
);
137 if (! (*devlist
)[index
])
143 Parms
.byPhysicalUnit
= i
+ 0x80;
144 rc
= DosDevIOCtl (danisDev
, DSKSP_CAT_GENERIC
, DSKSP_GET_INQUIRY_DATA
,
145 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)&Id
, IDLen
, &IDLen
);
147 asprintf(&(*devlist
)[index
], "hd%d", i
);
148 if (! (*devlist
)[index
])
166 for (index
= 0; index
< result
; index
++)
167 if ((*devlist
)[index
])
168 free ((*devlist
)[index
]);
180 // Like open(). Return non-negative integer handle, only used by the
181 // functions below. type=="ATA" or "SCSI". If you need to store
182 // extra information about your devices, create a private internal
183 // array within this file (see os_freebsd.cpp for an example). If you
184 // can not open the device (permission denied, does not exist, etc)
185 // set errno as open() does and return <0.
186 int deviceopen(const char *pathname
, char * /* type */ ){
192 char * activedev
= NULL
;
194 pathname
= skipdev(pathname
);
196 if(strlen(pathname
) > strlen(danispref
)
197 && strncmp(pathname
, danispref
, strlen(danispref
)) == 0) {
198 fd
= strtol(pathname
+ strlen(danispref
), NULL
, 10) + 0x80;
199 activedev
= (char *)danisdev
;
202 if(strlen(pathname
) > strlen(ahcipref
)
203 && strncmp(pathname
, ahcipref
, strlen(ahcipref
)) == 0) {
204 fd
= strtol(pathname
+ strlen(ahcipref
), NULL
, 10);
205 activedev
= (char *)ahcidev
;
209 pout("Error: please specify hdX or ahciX device name\n");
212 //printf( "deviceopen pathname %s\n", pathname);
213 rc
= DosOpen ((const char unsigned *)activedev
, &hDevice
, &ActionTaken
, 0, FILE_SYSTEM
,
214 OPEN_ACTION_OPEN_IF_EXISTS
, OPEN_SHARE_DENYNONE
|
215 OPEN_FLAGS_NOINHERIT
| OPEN_ACCESS_READONLY
, NULL
);
218 snprintf(errmsg
,256,"Smartctl open driver %s failed (%lu)", activedev
, rc
);
227 // Like close(). Acts only on integer handles returned by
228 // deviceopen() above.
229 int deviceclose(int /* fd */){
238 // OS/2 direct ioctl interface to IBMS506$/OS2AHCI$
240 static int dani_ioctl( int device
, void* arg
)
242 unsigned char* buff
= (unsigned char*) arg
;
244 DSKSP_CommandParameters Parms
;
246 ULONG DLen
= 512; //sizeof (*buf);
249 // printf( "device %d, request 0x%x, arg[0] 0x%x, arg[2] 0x%x\n", device, request, buff[0], buff[2]);
251 Parms
.byPhysicalUnit
= device
;
253 case ATA_IDENTIFY_DEVICE
:
254 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_GENERIC
, DSKSP_GET_INQUIRY_DATA
,
255 (PVOID
)&Parms
, PLen
, &PLen
, (UCHAR
*)arg
+4, DLen
, &DLen
);
258 printf ("DANIS506 ATA DSKSP_GET_INQUIRY_DATA failed (%lu)\n", rc
);
264 case ATA_SMART_STATUS
:
265 DLen
= sizeof(value
);
266 // OS/2 already checks CL/CH in IBM1S506 code!! see s506rte.c (ddk)
267 // value: -1=not supported, 0=ok, 1=failing
268 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_GETSTATUS
,
269 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)&value
, DLen
, &DLen
);
272 printf ("DANIS506 ATA GET SMART_STATUS failed (%lu)\n", rc
);
275 buff
[4] = (unsigned char)value
;
277 case ATA_SMART_READ_VALUES
:
278 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_GET_ATTRIBUTES
,
279 (PVOID
)&Parms
, PLen
, &PLen
, (UCHAR
*)arg
+4, DLen
, &DLen
);
282 printf ("DANIS506 ATA GET DSKSP_SMART_GET_ATTRIBUTES failed (%lu)\n", rc
);
286 case ATA_SMART_READ_THRESHOLDS
:
287 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_GET_THRESHOLDS
,
288 (PVOID
)&Parms
, PLen
, &PLen
, (UCHAR
*)arg
+4, DLen
, &DLen
);
291 printf ("DANIS506 ATA GET DSKSP_SMART_GET_THRESHOLDS failed (%lu)\n", rc
);
295 case ATA_SMART_READ_LOG_SECTOR
:
296 buff
[4] = buff
[1]; // copy select field
297 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_GET_LOG
,
298 (PVOID
)&Parms
, PLen
, &PLen
, (UCHAR
*)arg
+4, DLen
, &DLen
);
301 printf ("DANIS506 ATA GET DSKSP_SMART_GET_LOG failed (%lu)\n", rc
);
305 case ATA_SMART_ENABLE
:
306 buff
[0] = 1; // enable
308 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_ONOFF
,
309 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)buff
, DLen
, &DLen
);
311 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc
);
315 case ATA_SMART_DISABLE
:
316 buff
[0] = 0; // disable
318 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_ONOFF
,
319 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)buff
, DLen
, &DLen
);
321 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc
);
326 case ATA_SMART_AUTO_OFFLINE
:
327 buff
[0] = buff
[3]; // select field
329 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_AUTO_OFFLINE
,
330 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)buff
, DLen
, &DLen
);
332 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc
);
337 case ATA_SMART_AUTOSAVE
:
338 buff
[0] = buff
[3]; // select field
340 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_AUTOSAVE_ONOFF
,
341 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)buff
, DLen
, &DLen
);
343 printf ("DANIS506 ATA DSKSP_SMART_AUTOSAVE_ONOFF failed (%lu)\n", rc
);
347 case ATA_SMART_IMMEDIATE_OFFLINE
:
348 buff
[0] = buff
[1]; // select field
350 rc
= DosDevIOCtl (hDevice
, DSKSP_CAT_SMART
, DSKSP_SMART_EXEC_OFFLINE
,
351 (PVOID
)&Parms
, PLen
, &PLen
, (PVOID
)buff
, DLen
, &DLen
);
353 printf ("DANIS506 ATA GET DSKSP_SMART_EXEC_OFFLINE failed (%lu)\n", rc
);
359 fprintf( stderr
, "device %d, arg[0] 0x%x, arg[2] 0x%x\n", device
, buff
[0], buff
[2]);
360 fprintf( stderr
, "unknown ioctl\n");
365 //case WIN_PIDENTIFY:
368 fprintf( stderr
, "unknown ioctl\n");
377 // Interface to ATA devices. See os_linux.cpp for the canonical example.
378 // DETAILED DESCRIPTION OF ARGUMENTS
379 // device: is the integer handle provided by deviceopen()
380 // command: defines the different operations, see atacmds.h
381 // select: additional input data IF NEEDED (which log, which type of
383 // data: location to write output data, IF NEEDED (1 or 512 bytes).
384 // Note: not all commands use all arguments.
385 // RETURN VALUES (for all commands BUT command==STATUS_CHECK)
386 // -1 if the command failed
387 // 0 if the command succeeded,
388 // RETURN VALUES if command==STATUS_CHECK
389 // -1 if the command failed OR the disk SMART status can't be determined
390 // 0 if the command succeeded and disk SMART status is "OK"
391 // 1 if the command succeeded and disk SMART status is "FAILING"
393 // huge value of buffer size needed because HDIO_DRIVE_CMD assumes
394 // that buff[3] is the data size. Since the ATA_SMART_AUTOSAVE and
395 // ATA_SMART_AUTO_OFFLINE use values of 0xf1 and 0xf8 we need the space.
396 // Otherwise a 4+512 byte buffer would be enough.
397 #define STRANGE_BUFFER_LENGTH (4+512*0xf8)
399 int ata_command_interface(int device
, smart_command_set command
, int select
, char *data
){
400 unsigned char buff
[STRANGE_BUFFER_LENGTH
];
401 // positive: bytes to write to caller. negative: bytes to READ from
402 // caller. zero: non-data command
405 const int HDIO_DRIVE_CMD_OFFSET
= 4;
407 // See struct hd_drive_cmd_hdr in hdreg.h. Before calling ioctl()
408 // buff[0]: ATA COMMAND CODE REGISTER
409 // buff[1]: ATA SECTOR NUMBER REGISTER == LBA LOW REGISTER
410 // buff[2]: ATA FEATURES REGISTER
411 // buff[3]: ATA SECTOR COUNT REGISTER
413 // Note that on return:
414 // buff[2] contains the ATA SECTOR COUNT REGISTER
416 // clear out buff. Large enough for HDIO_DRIVE_CMD (4+512 bytes)
417 memset(buff
, 0, STRANGE_BUFFER_LENGTH
);
419 //printf( "command, select %d,%d\n", command, select);
420 buff
[0]=ATA_SMART_CMD
;
422 case CHECK_POWER_MODE
:
423 buff
[0]=ATA_CHECK_POWER_MODE
;
427 buff
[2]=ATA_SMART_READ_VALUES
;
431 case READ_THRESHOLDS
:
432 buff
[2]=ATA_SMART_READ_THRESHOLDS
;
437 buff
[2]=ATA_SMART_READ_LOG_SECTOR
;
445 buff
[0]=ATA_IDENTIFY_DEVICE
;
450 buff
[0]=ATA_IDENTIFY_PACKET_DEVICE
;
455 buff
[2]=ATA_SMART_ENABLE
;
459 buff
[2]=ATA_SMART_DISABLE
;
464 // this command only says if SMART is working. It could be
465 // replaced with STATUS_CHECK below.
466 buff
[2]=ATA_SMART_STATUS
;
470 buff
[2]=ATA_SMART_AUTO_OFFLINE
;
471 buff
[3]=select
; // YET NOTE - THIS IS A NON-DATA COMMAND!!
474 buff
[2]=ATA_SMART_AUTOSAVE
;
475 buff
[3]=select
; // YET NOTE - THIS IS A NON-DATA COMMAND!!
477 case IMMEDIATE_OFFLINE
:
478 buff
[2]=ATA_SMART_IMMEDIATE_OFFLINE
;
482 // // This command uses HDIO_DRIVE_TASK and has different syntax than
483 // // the other commands.
484 // buff[1]=ATA_SMART_STATUS;
487 pout("Unrecognized command %d in linux_ata_command_interface()\n"
488 "Please contact " PACKAGE_BUGREPORT
"\n", command
);
493 // We are now calling ioctl wrapper to the driver.
494 // TODO: use PASSTHRU in case of OS2AHCI driver
495 if ((dani_ioctl(device
, buff
)))
498 // There are two different types of ioctls(). The HDIO_DRIVE_TASK
500 if (command
==STATUS_CHECK
){
501 // Cyl low and Cyl high unchanged means "Good SMART status"
505 // These values mean "Bad SMART status"
509 // We haven't gotten output that makes sense; print out some debugging info
510 syserror("Error SMART Status command failed");
511 pout("Please get assistance from " PACKAGE_HOMEPAGE
"\n");
515 // CHECK POWER MODE command returns information in the Sector Count
516 // register (buff[3]). Copy to return data buffer.
517 if (command
==CHECK_POWER_MODE
)
518 buff
[HDIO_DRIVE_CMD_OFFSET
]=buff
[2];
520 // if the command returns data then copy it back
522 memcpy(data
, buff
+HDIO_DRIVE_CMD_OFFSET
, copydata
);
527 // Interface to SCSI devices. N/A under OS/2
528 int do_scsi_cmnd_io(int /* fd */, struct scsi_cmnd_io
* /* iop */, int /* report */) {
529 pout("SCSI interface is not implemented\n");