smartmontools SVN Rev 5599
Utility to control and monitor storage systems with "S.M.A.R.T."
os_os2.cpp
Go to the documentation of this file.
1/*
2 * os_os2.c
3 *
4 * Home page of code is: http://www.smartmontools.org
5 *
6 * Copyright (C) 2004-8 Yuri Dario
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11/*
12 *
13 * Thanks to Daniela Engert for providing sample code for SMART ioctl access.
14 *
15 */
16
17// These are needed to define prototypes for the functions defined below
18#include "config.h"
19
20#include <ctype.h>
21#include <errno.h>
22#include "atacmds.h"
23#include "scsicmds.h"
24#include "utility.h"
25
26// This is to include whatever prototypes you define in os_generic.h
27#include "os_os2.h"
28
29// Needed by '-V' option (CVS versioning) of smartd/smartctl
30const char *os_XXXX_c_cvsid="$Id: os_os2.cpp 5376 2022-05-01 12:49:30Z chrfranke $" \
32
33// global handle to device driver
34static HFILE hDevice;
35
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).
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"
47 );
48 return;
49}
50
51static const char * skipdev(const char * s)
52{
53 return (!strncmp(s, "/dev/", 5) ? s + 5 : s);
54}
55
56// tries to guess device type given the name (a path). See utility.h
57// for return values.
58int guess_device_type (const char* dev_name) {
59
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;
65}
66
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).
73
74int make_device_names (char*** devlist, const char* name) {
75
76 int result;
77 int index;
78 const int max_dev = 32; // scan only first 32 devices
79
80 // SCSI is not supported
81 if (strcmp (name, "ATA") != 0)
82 return 0;
83
84 // try to open DANIS
85 APIRET rc;
86 ULONG ActionTaken;
87 HFILE danisDev, ahciDev;
88 bool is_danis = 0, is_ahci = 0;
89
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);
93 if (!rc)
94 is_danis = 1;
95
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);
99 if (!rc)
100 is_ahci = 1;
101
102 // Count the devices.
103 result = 0;
104
106 ULONG PLen = 1;
107 ULONG IDLen = 512;
108 struct ata_identify_device Id;
109
110 for(int i = 0; i < max_dev; i++) {
111 if (is_ahci) {
112 Parms.byPhysicalUnit = i;
113 rc = DosDevIOCtl (ahciDev, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA,
114 (PVOID)&Parms, PLen, &PLen, (PVOID)&Id, IDLen, &IDLen);
115 if (!rc) result++;
116 }
117 if (is_danis) {
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);
121 if (!rc) result++;
122 }
123 }
124 *devlist = (char**)calloc (result, sizeof (char *));
125 if (! *devlist)
126 goto error;
127 index = 0;
128
129 // add devices
130 for(int i = 0; i < max_dev; i++) {
131 if (is_ahci) {
132 Parms.byPhysicalUnit = i;
133 rc = DosDevIOCtl (ahciDev, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA,
134 (PVOID)&Parms, PLen, &PLen, (PVOID)&Id, IDLen, &IDLen);
135 if (!rc) {
136 asprintf(&(*devlist)[index], "ahci%d", i);
137 if (! (*devlist)[index])
138 goto error;
139 index++;
140 }
141 }
142 if (is_danis) {
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);
146 if (!rc) {
147 asprintf(&(*devlist)[index], "hd%d", i);
148 if (! (*devlist)[index])
149 goto error;
150 index++;
151 }
152 }
153 }
154
155 if (is_danis)
156 DosClose( danisDev);
157
158 if (is_ahci)
159 DosClose( ahciDev);
160
161 return result;
162
163 error:
164 if (*devlist)
165 {
166 for (index = 0; index < result; index++)
167 if ((*devlist)[index])
168 free ((*devlist)[index]);
169 free (*devlist);
170 }
171 if (is_danis)
172 DosClose( danisDev);
173
174 if (is_ahci)
175 DosClose( ahciDev);
176
177 return -1;
178}
179
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.
186int deviceopen(const char *pathname, char * /* type */ ){
187
188 int fd = 0;
189 APIRET rc;
190 ULONG ActionTaken;
191
192 char * activedev = NULL;
193
194 pathname = skipdev(pathname);
195 // DANIS506 driver
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;
200 }
201 // OS2AHCI driver
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;
206 }
207
208 if(!activedev) {
209 pout("Error: please specify hdX or ahciX device name\n");
210 return -1;
211 }
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);
216 if (rc) {
217 char errmsg[256];
218 snprintf(errmsg,256,"Smartctl open driver %s failed (%lu)", activedev, rc);
219 errmsg[255]='\0';
220 syserror(errmsg);
221 return -1;
222 }
223
224 return fd;
225}
226
227// Like close(). Acts only on integer handles returned by
228// deviceopen() above.
229int deviceclose(int /* fd */){
230
231 DosClose( hDevice);
232 hDevice = NULL;
233
234 return 0;
235}
236
237//
238// OS/2 direct ioctl interface to IBMS506$/OS2AHCI$
239//
240static int dani_ioctl( int device, void* arg)
241{
242 unsigned char* buff = (unsigned char*) arg;
243 APIRET rc;
245 ULONG PLen = 1;
246 ULONG DLen = 512; //sizeof (*buf);
247 ULONG value = 0;
248
249 // printf( "device %d, request 0x%x, arg[0] 0x%x, arg[2] 0x%x\n", device, request, buff[0], buff[2]);
250
251 Parms.byPhysicalUnit = device;
252 switch( buff[0]) {
255 (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
256 if (rc != 0)
257 {
258 printf ("DANIS506 ATA DSKSP_GET_INQUIRY_DATA failed (%lu)\n", rc);
259 return -1;
260 }
261 break;
262 case ATA_SMART_CMD:
263 switch( buff[2]) {
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
269 (PVOID)&Parms, PLen, &PLen, (PVOID)&value, DLen, &DLen);
270 if (rc)
271 {
272 printf ("DANIS506 ATA GET SMART_STATUS failed (%lu)\n", rc);
273 return -1;
274 }
275 buff[4] = (unsigned char)value;
276 break;
279 (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
280 if (rc)
281 {
282 printf ("DANIS506 ATA GET DSKSP_SMART_GET_ATTRIBUTES failed (%lu)\n", rc);
283 return -1;
284 }
285 break;
288 (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
289 if (rc)
290 {
291 printf ("DANIS506 ATA GET DSKSP_SMART_GET_THRESHOLDS failed (%lu)\n", rc);
292 return -1;
293 }
294 break;
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);
299 if (rc)
300 {
301 printf ("DANIS506 ATA GET DSKSP_SMART_GET_LOG failed (%lu)\n", rc);
302 return -1;
303 }
304 break;
305 case ATA_SMART_ENABLE:
306 buff[0] = 1; // enable
307 DLen = 1;
308 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_ONOFF,
309 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
310 if (rc) {
311 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
312 return -1;
313 }
314 break;
316 buff[0] = 0; // disable
317 DLen = 1;
318 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_ONOFF,
319 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
320 if (rc) {
321 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
322 return -1;
323 }
324 break;
325#if 0
327 buff[0] = buff[3]; // select field
328 DLen = 1;
330 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
331 if (rc) {
332 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
333 return -1;
334 }
335 break;
336#endif
338 buff[0] = buff[3]; // select field
339 DLen = 1;
341 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
342 if (rc) {
343 printf ("DANIS506 ATA DSKSP_SMART_AUTOSAVE_ONOFF failed (%lu)\n", rc);
344 return -1;
345 }
346 break;
348 buff[0] = buff[1]; // select field
349 DLen = 1;
351 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
352 if (rc) {
353 printf ("DANIS506 ATA GET DSKSP_SMART_EXEC_OFFLINE failed (%lu)\n", rc);
354 return -1;
355 }
356 break;
357
358 default:
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");
361 return -1;
362 break;
363 }
364 break;
365 //case WIN_PIDENTIFY:
366 // break;
367 default:
368 fprintf( stderr, "unknown ioctl\n");
369 return -1;
370 break;
371 }
372
373 // ok
374 return 0;
375}
376
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
382// self-test).
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"
392
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)
398
399int 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
403 int copydata=0;
404
405 const int HDIO_DRIVE_CMD_OFFSET = 4;
406
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
412
413 // Note that on return:
414 // buff[2] contains the ATA SECTOR COUNT REGISTER
415
416 // clear out buff. Large enough for HDIO_DRIVE_CMD (4+512 bytes)
417 memset(buff, 0, STRANGE_BUFFER_LENGTH);
418
419 //printf( "command, select %d,%d\n", command, select);
420 buff[0]=ATA_SMART_CMD;
421 switch (command){
422 case CHECK_POWER_MODE:
423 buff[0]=ATA_CHECK_POWER_MODE;
424 copydata=1;
425 break;
426 case READ_VALUES:
427 buff[2]=ATA_SMART_READ_VALUES;
428 buff[3]=1;
429 copydata=512;
430 break;
431 case READ_THRESHOLDS:
433 buff[1]=buff[3]=1;
434 copydata=512;
435 break;
436 case READ_LOG:
438 buff[1]=select;
439 buff[3]=1;
440 copydata=512;
441 break;
442 case WRITE_LOG:
443 break;
444 case IDENTIFY:
445 buff[0]=ATA_IDENTIFY_DEVICE;
446 buff[3]=1;
447 copydata=512;
448 break;
449 case PIDENTIFY:
451 buff[3]=1;
452 copydata=512;
453 break;
454 case ENABLE:
455 buff[2]=ATA_SMART_ENABLE;
456 buff[1]=1;
457 break;
458 case DISABLE:
459 buff[2]=ATA_SMART_DISABLE;
460 buff[1]=1;
461 break;
462 case STATUS:
463 case STATUS_CHECK:
464 // this command only says if SMART is working. It could be
465 // replaced with STATUS_CHECK below.
466 buff[2]=ATA_SMART_STATUS;
467 buff[4]=0;
468 break;
469 case AUTO_OFFLINE:
471 buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!!
472 break;
473 case AUTOSAVE:
474 buff[2]=ATA_SMART_AUTOSAVE;
475 buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!!
476 break;
479 buff[1]=select;
480 break;
481 //case STATUS_CHECK:
482 // // This command uses HDIO_DRIVE_TASK and has different syntax than
483 // // the other commands.
484 // buff[1]=ATA_SMART_STATUS;
485 // break;
486 default:
487 pout("Unrecognized command %d in linux_ata_command_interface()\n"
488 "Please contact " PACKAGE_BUGREPORT "\n", command);
489 errno=ENOSYS;
490 return -1;
491 }
492
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)))
496 return -1;
497
498 // There are two different types of ioctls(). The HDIO_DRIVE_TASK
499 // one is this:
500 if (command==STATUS_CHECK){
501 // Cyl low and Cyl high unchanged means "Good SMART status"
502 if (buff[4]==0)
503 return 0;
504
505 // These values mean "Bad SMART status"
506 if (buff[4]==1)
507 return 1;
508
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_URL "\n");
512 return -1;
513 }
514
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];
519
520 // if the command returns data then copy it back
521 if (copydata)
522 memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata);
523
524 return 0;
525}
526
527// Interface to SCSI devices. N/A under OS/2
528int do_scsi_cmnd_io(int /* fd */, struct scsi_cmnd_io * /* iop */, int /* report */) {
529 pout("SCSI interface is not implemented\n");
530 return -ENOSYS;
531}
#define ATA_SMART_AUTO_OFFLINE
Definition: atacmds.h:94
#define ATA_IDENTIFY_DEVICE
Definition: atacmds.h:53
#define ATA_IDENTIFY_PACKET_DEVICE
Definition: atacmds.h:54
#define ATA_SMART_STATUS
Definition: atacmds.h:91
#define ATA_SMART_READ_VALUES
Definition: atacmds.h:81
#define ATA_SMART_READ_THRESHOLDS
Definition: atacmds.h:82
#define ATACMDS_H_CVSID
Definition: atacmds.h:16
#define ATA_SMART_READ_LOG_SECTOR
Definition: atacmds.h:86
#define ATA_SMART_CMD
Definition: atacmds.h:56
#define ATA_SMART_IMMEDIATE_OFFLINE
Definition: atacmds.h:85
smart_command_set
Definition: atacmds.h:29
@ PIDENTIFY
Definition: atacmds.h:43
@ CHECK_POWER_MODE
Definition: atacmds.h:45
@ IDENTIFY
Definition: atacmds.h:42
@ STATUS_CHECK
Definition: atacmds.h:37
@ IMMEDIATE_OFFLINE
Definition: atacmds.h:34
@ AUTO_OFFLINE
Definition: atacmds.h:35
@ ENABLE
Definition: atacmds.h:31
@ WRITE_LOG
Definition: atacmds.h:47
@ READ_VALUES
Definition: atacmds.h:39
@ AUTOSAVE
Definition: atacmds.h:33
@ STATUS
Definition: atacmds.h:36
@ READ_THRESHOLDS
Definition: atacmds.h:40
@ DISABLE
Definition: atacmds.h:32
@ READ_LOG
Definition: atacmds.h:41
#define ATA_SMART_ENABLE
Definition: atacmds.h:89
#define ATA_SMART_AUTOSAVE
Definition: atacmds.h:83
#define ATA_SMART_DISABLE
Definition: atacmds.h:90
#define ATA_CHECK_POWER_MODE
Definition: atacmds.h:52
u16 s[6]
Definition: megaraid.h:18
ptr_t data
Definition: megaraid.h:15
int deviceclose(int)
Definition: os_os2.cpp:229
int deviceopen(const char *pathname, char *)
Definition: os_os2.cpp:186
static HFILE hDevice
Definition: os_os2.cpp:34
int make_device_names(char ***devlist, const char *name)
Definition: os_os2.cpp:74
int ata_command_interface(int device, smart_command_set command, int select, char *data)
Definition: os_os2.cpp:399
int guess_device_type(const char *dev_name)
Definition: os_os2.cpp:58
const char * os_XXXX_c_cvsid
Definition: os_os2.cpp:30
static int dani_ioctl(int device, void *arg)
Definition: os_os2.cpp:240
void print_smartctl_examples()
Definition: os_os2.cpp:39
#define STRANGE_BUFFER_LENGTH
Definition: os_os2.cpp:397
int do_scsi_cmnd_io(int, struct scsi_cmnd_io *, int)
Definition: os_os2.cpp:528
static const char * skipdev(const char *s)
Definition: os_os2.cpp:51
#define DSKSP_CAT_SMART
Definition: os_os2.h:29
#define DSKSP_SMART_GET_LOG
Definition: os_os2.h:36
#define DSKSP_SMART_ONOFF
Definition: os_os2.h:30
const char * danisdev
Definition: os_os2.h:61
#define DSKSP_CAT_GENERIC
Definition: os_os2.h:43
const char * ahcipref
Definition: os_os2.h:64
#define DSKSP_GET_INQUIRY_DATA
Definition: os_os2.h:44
#define DSKSP_SMART_AUTOSAVE_ONOFF
Definition: os_os2.h:31
const char * ahcidev
Definition: os_os2.h:63
#define DSKSP_SMART_AUTO_OFFLINE
Definition: os_os2.h:37
#define DSKSP_SMART_GET_ATTRIBUTES
Definition: os_os2.h:34
#define DSKSP_SMART_EXEC_OFFLINE
Definition: os_os2.h:38
#define OS_XXXX_H_CVSID
Definition: os_os2.h:14
const char * danispref
Definition: os_os2.h:62
#define DSKSP_SMART_GET_THRESHOLDS
Definition: os_os2.h:35
#define DSKSP_SMART_GETSTATUS
Definition: os_os2.h:33
#define SCSICMDS_H_CVSID
Definition: scsicmds.h:22
void pout(const char *fmt,...)
Definition: smartd.cpp:1338
void syserror(const char *message)
Definition: utility.cpp:409
#define UTILITY_H_CVSID
Definition: utility.h:16