]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - os_os2.cpp
16f9b273890e9c81700c4de74b6460abff6fc6e0
[mirror_smartmontools-debian.git] / os_os2.cpp
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 * 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
14 * (for example COPYING); if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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
25 #include "config.h"
26 #include "int64.h"
27
28 #include <ctype.h>
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
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;
40
41 // global handle to device driver
42 static HFILE hDevice;
43
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
50 printf(
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"
57 );
58 #else
59 printf(
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"
66 );
67 #endif
68 return;
69 }
70
71 static 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.
78 int guess_device_type (const char* dev_name) {
79
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;
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).
93
94 int make_device_names (char*** devlist, const char* name) {
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;
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.
206 int deviceopen(const char *pathname, char * /* type */ ){
207
208 int fd = 0;
209 APIRET rc;
210 ULONG ActionTaken;
211
212 char * activedev = NULL;
213
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 }
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);
236 if (rc) {
237 char errmsg[256];
238 snprintf(errmsg,256,"Smartctl open driver %s failed (%lu)", activedev, rc);
239 errmsg[255]='\0';
240 syserror(errmsg);
241 return -1;
242 }
243
244 return fd;
245 }
246
247 // Like close(). Acts only on integer handles returned by
248 // deviceopen() above.
249 int deviceclose(int /* fd */){
250
251 DosClose( hDevice);
252 hDevice = NULL;
253
254 return 0;
255 }
256
257 //
258 // OS/2 direct ioctl interface to IBMS506$/OS2AHCI$
259 //
260 static int dani_ioctl( int device, void* arg)
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);
267 ULONG value = 0;
268
269 // printf( "device %d, request 0x%x, arg[0] 0x%x, arg[2] 0x%x\n", device, request, buff[0], buff[2]);
270
271 Parms.byPhysicalUnit = device;
272 switch( buff[0]) {
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);
276 if (rc != 0)
277 {
278 printf ("DANIS506 ATA DSKSP_GET_INQUIRY_DATA failed (%lu)\n", rc);
279 return -1;
280 }
281 break;
282 case ATA_SMART_CMD:
283 switch( buff[2]) {
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);
290 if (rc)
291 {
292 printf ("DANIS506 ATA GET SMART_STATUS failed (%lu)\n", rc);
293 return -1;
294 }
295 buff[4] = (unsigned char)value;
296 break;
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);
300 if (rc)
301 {
302 printf ("DANIS506 ATA GET DSKSP_SMART_GET_ATTRIBUTES failed (%lu)\n", rc);
303 return -1;
304 }
305 break;
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);
309 if (rc)
310 {
311 printf ("DANIS506 ATA GET DSKSP_SMART_GET_THRESHOLDS failed (%lu)\n", rc);
312 return -1;
313 }
314 break;
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);
319 if (rc)
320 {
321 printf ("DANIS506 ATA GET DSKSP_SMART_GET_LOG failed (%lu)\n", rc);
322 return -1;
323 }
324 break;
325 case ATA_SMART_ENABLE:
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) {
331 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
332 return -1;
333 }
334 break;
335 case ATA_SMART_DISABLE:
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) {
341 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
342 return -1;
343 }
344 break;
345 #if 0
346 case ATA_SMART_AUTO_OFFLINE:
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) {
352 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%lu)\n", rc);
353 return -1;
354 }
355 break;
356 #endif
357 case ATA_SMART_AUTOSAVE:
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) {
363 printf ("DANIS506 ATA DSKSP_SMART_AUTOSAVE_ONOFF failed (%lu)\n", rc);
364 return -1;
365 }
366 break;
367 case ATA_SMART_IMMEDIATE_OFFLINE:
368 buff[0] = buff[1]; // select field
369 DLen = 1;
370 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_EXEC_OFFLINE,
371 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
372 if (rc) {
373 printf ("DANIS506 ATA GET DSKSP_SMART_EXEC_OFFLINE failed (%lu)\n", rc);
374 return -1;
375 }
376 break;
377
378 default:
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");
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
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
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
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)))
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){
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
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");
550 return -ENOSYS;
551 }