]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - os_os2.cpp
New upstream version 6.6
[mirror_smartmontools-debian.git] / os_os2.cpp
CommitLineData
a23d5117
GI
1/*
2 * os_os2.c
3 *
a86ec89e 4 * Home page of code is: http://www.smartmontools.org
a23d5117 5 *
f9e10201 6 * Copyright (C) 2004-8 Yuri Dario
a23d5117
GI
7 *
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)
11 * any later version.
12 *
13 * You should have received a copy of the GNU General Public License
ee38a438
GI
14 * (for example COPYING); if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
a23d5117
GI
16 */
17
18/*
19 *
20 * Thanks to Daniela Engert for providing sample code for SMART ioctl access.
21 *
22 */
23
24// These are needed to define prototypes for the functions defined below
f9e10201
JD
25#include "config.h"
26#include "int64.h"
27
28#include <ctype.h>
a23d5117
GI
29#include <errno.h>
30#include "atacmds.h"
31#include "scsicmds.h"
32#include "utility.h"
33
34// This is to include whatever prototypes you define in os_generic.h
35#include "os_os2.h"
36
37// Needed by '-V' option (CVS versioning) of smartd/smartctl
f9e10201 38const char *os_XXXX_c_cvsid="$Id: os_os2.cpp 4431 2017-08-08 19:38:15Z chrfranke $" \
a23d5117
GI
39ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
40
41// global handle to device driver
42static HFILE hDevice;
43
a23d5117
GI
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).
47void print_smartctl_examples(){
48 printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
49#ifdef HAVE_GETOPT_LONG
50 printf(
f9e10201
JD
51 " smartctl -a hd0 (Prints all SMART information)\n\n"
52 " smartctl --smart=on --offlineauto=on --saveauto=on hd0\n"
a23d5117 53 " (Enables SMART on first disk)\n\n"
f9e10201
JD
54 " smartctl -t long hd0 (Executes extended disk self-test)\n\n"
55 " smartctl --attributes --log=selftest --quietmode=errorsonly hd0\n"
a23d5117 56 " (Prints Self-Test & Attribute errors)\n"
a23d5117
GI
57 );
58#else
59 printf(
f9e10201
JD
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"
a23d5117 65 " (Prints Self-Test & Attribute errors)\n"
a23d5117
GI
66 );
67#endif
68 return;
69}
70
71static const char * skipdev(const char * s)
72{
73 return (!strncmp(s, "/dev/", 5) ? s + 5 : s);
74}
75
76// tries to guess device type given the name (a path). See utility.h
77// for return values.
78int guess_device_type (const char* dev_name) {
79
80 //printf( "dev_name %s\n", dev_name);
81 dev_name = skipdev(dev_name);
f9e10201 82 if (!strncmp(dev_name, "hd", 2) || !strncmp(dev_name, "ahci", 4))
a23d5117 83 return CONTROLLER_ATA;
a23d5117
GI
84 return CONTROLLER_UNKNOWN;
85}
86
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).
f9e10201 93
a23d5117 94int make_device_names (char*** devlist, const char* name) {
f9e10201
JD
95
96 int result;
97 int index;
98 const int max_dev = 32; // scan only first 32 devices
99
100 // SCSI is not supported
101 if (strcmp (name, "ATA") != 0)
102 return 0;
103
104 // try to open DANIS
105 APIRET rc;
106 ULONG ActionTaken;
107 HFILE danisDev, ahciDev;
108 bool is_danis = 0, is_ahci = 0;
109
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);
113 if (!rc)
114 is_danis = 1;
115
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);
119 if (!rc)
120 is_ahci = 1;
121
122 // Count the devices.
123 result = 0;
124
125 DSKSP_CommandParameters Parms;
126 ULONG PLen = 1;
127 ULONG IDLen = 512;
128 struct ata_identify_device Id;
129
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) result++;
136 }
137 if (is_danis) {
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);
141 if (!rc) result++;
142 }
143 }
144 *devlist = (char**)calloc (result, sizeof (char *));
145 if (! *devlist)
146 goto error;
147 index = 0;
148
149 // add devices
150 for(int i = 0; i < max_dev; i++) {
151 if (is_ahci) {
152 Parms.byPhysicalUnit = i;
153 rc = DosDevIOCtl (ahciDev, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA,
154 (PVOID)&Parms, PLen, &PLen, (PVOID)&Id, IDLen, &IDLen);
155 if (!rc) {
156 asprintf(&(*devlist)[index], "ahci%d", i);
157 if (! (*devlist)[index])
158 goto error;
159 index++;
160 }
161 }
162 if (is_danis) {
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);
166 if (!rc) {
167 asprintf(&(*devlist)[index], "hd%d", i);
168 if (! (*devlist)[index])
169 goto error;
170 index++;
171 }
172 }
173 }
174
175 if (is_danis)
176 DosClose( danisDev);
177
178 if (is_ahci)
179 DosClose( ahciDev);
180
181 return result;
182
183 error:
184 if (*devlist)
185 {
186 for (index = 0; index < result; index++)
187 if ((*devlist)[index])
188 free ((*devlist)[index]);
189 free (*devlist);
190 }
191 if (is_danis)
192 DosClose( danisDev);
193
194 if (is_ahci)
195 DosClose( ahciDev);
196
197 return -1;
a23d5117
GI
198}
199
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.
f9e10201 206int deviceopen(const char *pathname, char * /* type */ ){
a23d5117 207
f9e10201 208 int fd = 0;
a23d5117
GI
209 APIRET rc;
210 ULONG ActionTaken;
f9e10201
JD
211
212 char * activedev = NULL;
a23d5117 213
f9e10201
JD
214 pathname = skipdev(pathname);
215 // DANIS506 driver
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;
220 }
221 // OS2AHCI driver
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;
226 }
227
228 if(!activedev) {
229 pout("Error: please specify hdX or ahciX device name\n");
230 return -1;
231 }
a23d5117 232 //printf( "deviceopen pathname %s\n", pathname);
f9e10201 233 rc = DosOpen ((const char unsigned *)activedev, &hDevice, &ActionTaken, 0, FILE_SYSTEM,
a23d5117
GI
234 OPEN_ACTION_OPEN_IF_EXISTS, OPEN_SHARE_DENYNONE |
235 OPEN_FLAGS_NOINHERIT | OPEN_ACCESS_READONLY, NULL);
236 if (rc) {
237 char errmsg[256];
f9e10201 238 snprintf(errmsg,256,"Smartctl open driver %s failed (%lu)", activedev, rc);
a23d5117
GI
239 errmsg[255]='\0';
240 syserror(errmsg);
241 return -1;
242 }
243
a23d5117
GI
244 return fd;
245}
246
247// Like close(). Acts only on integer handles returned by
248// deviceopen() above.
f9e10201 249int deviceclose(int /* fd */){
a23d5117
GI
250
251 DosClose( hDevice);
252 hDevice = NULL;
253
254 return 0;
255}
256
a23d5117 257//
f9e10201 258// OS/2 direct ioctl interface to IBMS506$/OS2AHCI$
a23d5117 259//
f9e10201 260static int dani_ioctl( int device, void* arg)
a23d5117
GI
261{
262 unsigned char* buff = (unsigned char*) arg;
263 APIRET rc;
264 DSKSP_CommandParameters Parms;
265 ULONG PLen = 1;
266 ULONG DLen = 512; //sizeof (*buf);
a23d5117 267 ULONG value = 0;
a23d5117 268
f9e10201 269 // printf( "device %d, request 0x%x, arg[0] 0x%x, arg[2] 0x%x\n", device, request, buff[0], buff[2]);
a23d5117
GI
270
271 Parms.byPhysicalUnit = device;
272 switch( buff[0]) {
f9e10201 273 case ATA_IDENTIFY_DEVICE:
a23d5117 274 rc = DosDevIOCtl (hDevice, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA,
f9e10201 275 (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
a23d5117
GI
276 if (rc != 0)
277 {
f9e10201 278 printf ("DANIS506 ATA DSKSP_GET_INQUIRY_DATA failed (%lu)\n", rc);
a23d5117
GI
279 return -1;
280 }
281 break;
f9e10201 282 case ATA_SMART_CMD:
a23d5117 283 switch( buff[2]) {
f9e10201 284 case ATA_SMART_STATUS:
a23d5117
GI
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);
290 if (rc)
291 {
f9e10201 292 printf ("DANIS506 ATA GET SMART_STATUS failed (%lu)\n", rc);
a23d5117
GI
293 return -1;
294 }
295 buff[4] = (unsigned char)value;
296 break;
f9e10201 297 case ATA_SMART_READ_VALUES:
a23d5117 298 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_ATTRIBUTES,
f9e10201 299 (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
a23d5117
GI
300 if (rc)
301 {
f9e10201 302 printf ("DANIS506 ATA GET DSKSP_SMART_GET_ATTRIBUTES failed (%lu)\n", rc);
a23d5117
GI
303 return -1;
304 }
305 break;
f9e10201 306 case ATA_SMART_READ_THRESHOLDS:
a23d5117 307 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_THRESHOLDS,
f9e10201 308 (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
a23d5117
GI
309 if (rc)
310 {
f9e10201 311 printf ("DANIS506 ATA GET DSKSP_SMART_GET_THRESHOLDS failed (%lu)\n", rc);
a23d5117
GI
312 return -1;
313 }
314 break;
f9e10201 315 case ATA_SMART_READ_LOG_SECTOR:
a23d5117 316 buff[4] = buff[1]; // copy select field
f9e10201
JD
317 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_LOG,
318 (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
a23d5117
GI
319 if (rc)
320 {
f9e10201 321 printf ("DANIS506 ATA GET DSKSP_SMART_GET_LOG failed (%lu)\n", rc);
a23d5117
GI
322 return -1;
323 }
324 break;
f9e10201 325 case ATA_SMART_ENABLE:
a23d5117
GI
326 buff[0] = 1; // enable
327 DLen = 1;
328 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_ONOFF,
329 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
330 if (rc) {
f9e10201 331 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
a23d5117
GI
332 return -1;
333 }
334 break;
f9e10201 335 case ATA_SMART_DISABLE:
a23d5117
GI
336 buff[0] = 0; // disable
337 DLen = 1;
338 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_ONOFF,
339 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
340 if (rc) {
f9e10201 341 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
a23d5117
GI
342 return -1;
343 }
344 break;
345#if 0
f9e10201 346 case ATA_SMART_AUTO_OFFLINE:
a23d5117
GI
347 buff[0] = buff[3]; // select field
348 DLen = 1;
349 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_AUTO_OFFLINE,
350 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
351 if (rc) {
f9e10201 352 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
a23d5117
GI
353 return -1;
354 }
355 break;
356#endif
f9e10201 357 case ATA_SMART_AUTOSAVE:
a23d5117
GI
358 buff[0] = buff[3]; // select field
359 DLen = 1;
360 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_AUTOSAVE_ONOFF,
361 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
362 if (rc) {
f9e10201 363 printf ("DANIS506 ATA DSKSP_SMART_AUTOSAVE_ONOFF failed (%lu)\n", rc);
a23d5117
GI
364 return -1;
365 }
366 break;
f9e10201 367 case ATA_SMART_IMMEDIATE_OFFLINE:
a23d5117
GI
368 buff[0] = buff[1]; // select field
369 DLen = 1;
f9e10201 370 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_EXEC_OFFLINE,
a23d5117
GI
371 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
372 if (rc) {
f9e10201 373 printf ("DANIS506 ATA GET DSKSP_SMART_EXEC_OFFLINE failed (%lu)\n", rc);
a23d5117
GI
374 return -1;
375 }
376 break;
377
378 default:
f9e10201 379 fprintf( stderr, "device %d, arg[0] 0x%x, arg[2] 0x%x\n", device, buff[0], buff[2]);
a23d5117
GI
380 fprintf( stderr, "unknown ioctl\n");
381 return -1;
382 break;
383 }
384 break;
385 //case WIN_PIDENTIFY:
386 // break;
387 default:
388 fprintf( stderr, "unknown ioctl\n");
389 return -1;
390 break;
391 }
392
393 // ok
394 return 0;
395}
396
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
402// self-test).
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"
412
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)
418
419int 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
423 int copydata=0;
424
425 const int HDIO_DRIVE_CMD_OFFSET = 4;
426
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
432
433 // Note that on return:
434 // buff[2] contains the ATA SECTOR COUNT REGISTER
435
436 // clear out buff. Large enough for HDIO_DRIVE_CMD (4+512 bytes)
437 memset(buff, 0, STRANGE_BUFFER_LENGTH);
438
439 //printf( "command, select %d,%d\n", command, select);
440 buff[0]=ATA_SMART_CMD;
441 switch (command){
442 case CHECK_POWER_MODE:
443 buff[0]=ATA_CHECK_POWER_MODE;
444 copydata=1;
445 break;
446 case READ_VALUES:
447 buff[2]=ATA_SMART_READ_VALUES;
448 buff[3]=1;
449 copydata=512;
450 break;
451 case READ_THRESHOLDS:
452 buff[2]=ATA_SMART_READ_THRESHOLDS;
453 buff[1]=buff[3]=1;
454 copydata=512;
455 break;
456 case READ_LOG:
457 buff[2]=ATA_SMART_READ_LOG_SECTOR;
458 buff[1]=select;
459 buff[3]=1;
460 copydata=512;
461 break;
462 case WRITE_LOG:
463 break;
464 case IDENTIFY:
465 buff[0]=ATA_IDENTIFY_DEVICE;
466 buff[3]=1;
467 copydata=512;
468 break;
469 case PIDENTIFY:
470 buff[0]=ATA_IDENTIFY_PACKET_DEVICE;
471 buff[3]=1;
472 copydata=512;
473 break;
474 case ENABLE:
475 buff[2]=ATA_SMART_ENABLE;
476 buff[1]=1;
477 break;
478 case DISABLE:
479 buff[2]=ATA_SMART_DISABLE;
480 buff[1]=1;
481 break;
482 case STATUS:
483 case STATUS_CHECK:
484 // this command only says if SMART is working. It could be
485 // replaced with STATUS_CHECK below.
486 buff[2]=ATA_SMART_STATUS;
487 buff[4]=0;
488 break;
489 case AUTO_OFFLINE:
490 buff[2]=ATA_SMART_AUTO_OFFLINE;
491 buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!!
492 break;
493 case AUTOSAVE:
494 buff[2]=ATA_SMART_AUTOSAVE;
495 buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!!
496 break;
497 case IMMEDIATE_OFFLINE:
498 buff[2]=ATA_SMART_IMMEDIATE_OFFLINE;
499 buff[1]=select;
500 break;
501 //case STATUS_CHECK:
502 // // This command uses HDIO_DRIVE_TASK and has different syntax than
503 // // the other commands.
504 // buff[1]=ATA_SMART_STATUS;
505 // break;
506 default:
507 pout("Unrecognized command %d in linux_ata_command_interface()\n"
508 "Please contact " PACKAGE_BUGREPORT "\n", command);
509 errno=ENOSYS;
510 return -1;
511 }
512
f9e10201
JD
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)))
a23d5117
GI
516 return -1;
517
518 // There are two different types of ioctls(). The HDIO_DRIVE_TASK
519 // one is this:
520 if (command==STATUS_CHECK){
a23d5117
GI
521 // Cyl low and Cyl high unchanged means "Good SMART status"
522 if (buff[4]==0)
523 return 0;
524
525 // These values mean "Bad SMART status"
526 if (buff[4]==1)
527 return 1;
528
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");
532 return -1;
533 }
534
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];
539
540 // if the command returns data then copy it back
541 if (copydata)
542 memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata);
543
544 return 0;
545}
546
f9e10201
JD
547// Interface to SCSI devices. N/A under OS/2
548int do_scsi_cmnd_io(int /* fd */, struct scsi_cmnd_io * /* iop */, int /* report */) {
549 pout("SCSI interface is not implemented\n");
a23d5117
GI
550 return -ENOSYS;
551}