]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - os_os2.cpp
import smartmontools 7.0
[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 7 *
ff28b140 8 * SPDX-License-Identifier: GPL-2.0-or-later
a23d5117
GI
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
f9e10201 18#include "config.h"
f9e10201
JD
19
20#include <ctype.h>
a23d5117
GI
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
ff28b140 30const char *os_XXXX_c_cvsid="$Id: os_os2.cpp 4842 2018-12-02 16:07:26Z chrfranke $" \
a23d5117
GI
31ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
32
33// global handle to device driver
34static HFILE hDevice;
35
a23d5117
GI
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).
39void print_smartctl_examples(){
ff28b140 40 printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
f9e10201
JD
41 " smartctl -a hd0 (Prints all SMART information)\n\n"
42 " smartctl --smart=on --offlineauto=on --saveauto=on hd0\n"
a23d5117 43 " (Enables SMART on first disk)\n\n"
f9e10201
JD
44 " smartctl -t long hd0 (Executes extended disk self-test)\n\n"
45 " smartctl --attributes --log=selftest --quietmode=errorsonly hd0\n"
a23d5117 46 " (Prints Self-Test & Attribute errors)\n"
a23d5117 47 );
a23d5117
GI
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);
f9e10201 62 if (!strncmp(dev_name, "hd", 2) || !strncmp(dev_name, "ahci", 4))
a23d5117 63 return CONTROLLER_ATA;
a23d5117
GI
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).
f9e10201 73
a23d5117 74int make_device_names (char*** devlist, const char* name) {
f9e10201
JD
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
105 DSKSP_CommandParameters Parms;
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;
a23d5117
GI
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.
f9e10201 186int deviceopen(const char *pathname, char * /* type */ ){
a23d5117 187
f9e10201 188 int fd = 0;
a23d5117
GI
189 APIRET rc;
190 ULONG ActionTaken;
f9e10201
JD
191
192 char * activedev = NULL;
a23d5117 193
f9e10201
JD
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 }
a23d5117 212 //printf( "deviceopen pathname %s\n", pathname);
f9e10201 213 rc = DosOpen ((const char unsigned *)activedev, &hDevice, &ActionTaken, 0, FILE_SYSTEM,
a23d5117
GI
214 OPEN_ACTION_OPEN_IF_EXISTS, OPEN_SHARE_DENYNONE |
215 OPEN_FLAGS_NOINHERIT | OPEN_ACCESS_READONLY, NULL);
216 if (rc) {
217 char errmsg[256];
f9e10201 218 snprintf(errmsg,256,"Smartctl open driver %s failed (%lu)", activedev, rc);
a23d5117
GI
219 errmsg[255]='\0';
220 syserror(errmsg);
221 return -1;
222 }
223
a23d5117
GI
224 return fd;
225}
226
227// Like close(). Acts only on integer handles returned by
228// deviceopen() above.
f9e10201 229int deviceclose(int /* fd */){
a23d5117
GI
230
231 DosClose( hDevice);
232 hDevice = NULL;
233
234 return 0;
235}
236
a23d5117 237//
f9e10201 238// OS/2 direct ioctl interface to IBMS506$/OS2AHCI$
a23d5117 239//
f9e10201 240static int dani_ioctl( int device, void* arg)
a23d5117
GI
241{
242 unsigned char* buff = (unsigned char*) arg;
243 APIRET rc;
244 DSKSP_CommandParameters Parms;
245 ULONG PLen = 1;
246 ULONG DLen = 512; //sizeof (*buf);
a23d5117 247 ULONG value = 0;
a23d5117 248
f9e10201 249 // printf( "device %d, request 0x%x, arg[0] 0x%x, arg[2] 0x%x\n", device, request, buff[0], buff[2]);
a23d5117
GI
250
251 Parms.byPhysicalUnit = device;
252 switch( buff[0]) {
f9e10201 253 case ATA_IDENTIFY_DEVICE:
a23d5117 254 rc = DosDevIOCtl (hDevice, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA,
f9e10201 255 (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
a23d5117
GI
256 if (rc != 0)
257 {
f9e10201 258 printf ("DANIS506 ATA DSKSP_GET_INQUIRY_DATA failed (%lu)\n", rc);
a23d5117
GI
259 return -1;
260 }
261 break;
f9e10201 262 case ATA_SMART_CMD:
a23d5117 263 switch( buff[2]) {
f9e10201 264 case ATA_SMART_STATUS:
a23d5117
GI
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);
270 if (rc)
271 {
f9e10201 272 printf ("DANIS506 ATA GET SMART_STATUS failed (%lu)\n", rc);
a23d5117
GI
273 return -1;
274 }
275 buff[4] = (unsigned char)value;
276 break;
f9e10201 277 case ATA_SMART_READ_VALUES:
a23d5117 278 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_ATTRIBUTES,
f9e10201 279 (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
a23d5117
GI
280 if (rc)
281 {
f9e10201 282 printf ("DANIS506 ATA GET DSKSP_SMART_GET_ATTRIBUTES failed (%lu)\n", rc);
a23d5117
GI
283 return -1;
284 }
285 break;
f9e10201 286 case ATA_SMART_READ_THRESHOLDS:
a23d5117 287 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_THRESHOLDS,
f9e10201 288 (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
a23d5117
GI
289 if (rc)
290 {
f9e10201 291 printf ("DANIS506 ATA GET DSKSP_SMART_GET_THRESHOLDS failed (%lu)\n", rc);
a23d5117
GI
292 return -1;
293 }
294 break;
f9e10201 295 case ATA_SMART_READ_LOG_SECTOR:
a23d5117 296 buff[4] = buff[1]; // copy select field
f9e10201
JD
297 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_LOG,
298 (PVOID)&Parms, PLen, &PLen, (UCHAR *)arg+4, DLen, &DLen);
a23d5117
GI
299 if (rc)
300 {
f9e10201 301 printf ("DANIS506 ATA GET DSKSP_SMART_GET_LOG failed (%lu)\n", rc);
a23d5117
GI
302 return -1;
303 }
304 break;
f9e10201 305 case ATA_SMART_ENABLE:
a23d5117
GI
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) {
f9e10201 311 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
a23d5117
GI
312 return -1;
313 }
314 break;
f9e10201 315 case ATA_SMART_DISABLE:
a23d5117
GI
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) {
f9e10201 321 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
a23d5117
GI
322 return -1;
323 }
324 break;
325#if 0
f9e10201 326 case ATA_SMART_AUTO_OFFLINE:
a23d5117
GI
327 buff[0] = buff[3]; // select field
328 DLen = 1;
329 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_AUTO_OFFLINE,
330 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
331 if (rc) {
f9e10201 332 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
a23d5117
GI
333 return -1;
334 }
335 break;
336#endif
f9e10201 337 case ATA_SMART_AUTOSAVE:
a23d5117
GI
338 buff[0] = buff[3]; // select field
339 DLen = 1;
340 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_AUTOSAVE_ONOFF,
341 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
342 if (rc) {
f9e10201 343 printf ("DANIS506 ATA DSKSP_SMART_AUTOSAVE_ONOFF failed (%lu)\n", rc);
a23d5117
GI
344 return -1;
345 }
346 break;
f9e10201 347 case ATA_SMART_IMMEDIATE_OFFLINE:
a23d5117
GI
348 buff[0] = buff[1]; // select field
349 DLen = 1;
f9e10201 350 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_EXEC_OFFLINE,
a23d5117
GI
351 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
352 if (rc) {
f9e10201 353 printf ("DANIS506 ATA GET DSKSP_SMART_EXEC_OFFLINE failed (%lu)\n", rc);
a23d5117
GI
354 return -1;
355 }
356 break;
357
358 default:
f9e10201 359 fprintf( stderr, "device %d, arg[0] 0x%x, arg[2] 0x%x\n", device, buff[0], buff[2]);
a23d5117
GI
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
ff28b140 377// Interface to ATA devices. See os_linux.cpp for the canonical example.
a23d5117
GI
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:
432 buff[2]=ATA_SMART_READ_THRESHOLDS;
433 buff[1]=buff[3]=1;
434 copydata=512;
435 break;
436 case READ_LOG:
437 buff[2]=ATA_SMART_READ_LOG_SECTOR;
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:
450 buff[0]=ATA_IDENTIFY_PACKET_DEVICE;
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:
470 buff[2]=ATA_SMART_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;
477 case IMMEDIATE_OFFLINE:
478 buff[2]=ATA_SMART_IMMEDIATE_OFFLINE;
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
f9e10201
JD
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)))
a23d5117
GI
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){
a23d5117
GI
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_HOMEPAGE "\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
f9e10201
JD
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");
a23d5117
GI
530 return -ENOSYS;
531}