|
smartmontools SVN Rev 3317
Utility to control and monitor storage systems with "S.M.A.R.T."
|
00001 /* 00002 * os_os2.c 00003 * 00004 * Home page of code is: http://smartmontools.sourceforge.net 00005 * 00006 * Copyright (C) 2004-8 Yuri Dario <smartmontools-support@lists.sourceforge.net> 00007 * 00008 * This program is free software; you can redistribute it and/or modify 00009 * it under the terms of the GNU General Public License as published by 00010 * the Free Software Foundation; either version 2, or (at your option) 00011 * any later version. 00012 * 00013 * You should have received a copy of the GNU General Public License 00014 * (for example COPYING); if not, write to the Free Software Foundation, 00015 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 00016 */ 00017 00018 /* 00019 * 00020 * Thanks to Daniela Engert for providing sample code for SMART ioctl access. 00021 * 00022 */ 00023 00024 // These are needed to define prototypes for the functions defined below 00025 #include <errno.h> 00026 #include "atacmds.h" 00027 #include "scsicmds.h" 00028 #include "utility.h" 00029 00030 // This is to include whatever prototypes you define in os_generic.h 00031 #include "os_os2.h" 00032 00033 // Needed by '-V' option (CVS versioning) of smartd/smartctl 00034 const char *os_XXXX_c_cvsid="$Id: os_os2.cpp 3806 2013-03-29 20:17:03Z chrfranke $" \ 00035 ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; 00036 00037 // global handle to device driver 00038 static HFILE hDevice; 00039 00040 // Please eliminate the following block: both the two #includes and 00041 // the 'unsupported()' function. They are only here to warn 00042 // unsuspecting users that their Operating System is not supported! If 00043 // you wish, you can use a similar warning mechanism for any of the 00044 // functions in this file that you can not (or choose not to) 00045 // implement. 00046 00047 #include "config.h" 00048 00049 typedef struct _IDEREGS { 00050 UCHAR bFeaturesReg; 00051 UCHAR bSectorCountReg; 00052 UCHAR bSectorNumberReg; 00053 UCHAR bCylLowReg; 00054 UCHAR bCylHighReg; 00055 UCHAR bDriveHeadReg; 00056 UCHAR bCommandReg; 00057 UCHAR bReserved; 00058 } IDEREGS, *PIDEREGS, *LPIDEREGS; 00059 00060 static void unsupported(int which){ 00061 static int warninggiven[4]; 00062 00063 if (which<0 || which>3) 00064 return; 00065 00066 if (!warninggiven[which]) { 00067 char msg; 00068 warninggiven[which]=1; 00069 00070 switch (which) { 00071 case 0: 00072 msg="generate a list of devices"; 00073 break; 00074 case 1: 00075 msg="interface to Marvell-based SATA controllers"; 00076 break; 00077 case 2: 00078 msg="interface to 3ware-based RAID controllers"; 00079 break; 00080 case 3: 00081 msg="interface to SCSI devices"; 00082 break; 00083 } 00084 pout("Under OS/2, smartmontools can not %s\n"); 00085 } 00086 return; 00087 } 00088 00089 // print examples for smartctl. You should modify this function so 00090 // that the device paths are sensible for your OS, and to eliminate 00091 // unsupported commands (eg, 3ware controllers). 00092 void print_smartctl_examples(){ 00093 printf("=================================================== SMARTCTL EXAMPLES =====\n\n"); 00094 #ifdef HAVE_GETOPT_LONG 00095 printf( 00096 " smartctl -a /dev/hda (Prints all SMART information)\n\n" 00097 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n" 00098 " (Enables SMART on first disk)\n\n" 00099 " smartctl -t long /dev/hda (Executes extended disk self-test)\n\n" 00100 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n" 00101 " (Prints Self-Test & Attribute errors)\n" 00102 " smartctl -a --device=3ware,2 /dev/sda\n" 00103 " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" 00104 ); 00105 #else 00106 printf( 00107 " smartctl -a /dev/hda (Prints all SMART information)\n" 00108 " smartctl -s on -o on -S on /dev/hda (Enables SMART on first disk)\n" 00109 " smartctl -t long /dev/hda (Executes extended disk self-test)\n" 00110 " smartctl -A -l selftest -q errorsonly /dev/hda\n" 00111 " (Prints Self-Test & Attribute errors)\n" 00112 " smartctl -a -d 3ware,2 /dev/sda\n" 00113 " (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n" 00114 ); 00115 #endif 00116 return; 00117 } 00118 00119 static const char * skipdev(const char * s) 00120 { 00121 return (!strncmp(s, "/dev/", 5) ? s + 5 : s); 00122 } 00123 00124 // tries to guess device type given the name (a path). See utility.h 00125 // for return values. 00126 int guess_device_type (const char* dev_name) { 00127 00128 //printf( "dev_name %s\n", dev_name); 00129 dev_name = skipdev(dev_name); 00130 if (!strncmp(dev_name, "hd", 2)) 00131 return CONTROLLER_ATA; 00132 if (!strncmp(dev_name, "scsi", 4)) 00133 return CONTROLLER_SCSI; 00134 return CONTROLLER_UNKNOWN; 00135 } 00136 00137 // makes a list of ATA or SCSI devices for the DEVICESCAN directive of 00138 // smartd. Returns number N of devices, or -1 if out of 00139 // memory. Allocates N+1 arrays: one of N pointers (devlist); the 00140 // other N arrays each contain null-terminated character strings. In 00141 // the case N==0, no arrays are allocated because the array of 0 00142 // pointers has zero length, equivalent to calling malloc(0). 00143 int make_device_names (char*** devlist, const char* name) { 00144 unsupported(0); 00145 return 0; 00146 } 00147 00148 // Like open(). Return non-negative integer handle, only used by the 00149 // functions below. type=="ATA" or "SCSI". If you need to store 00150 // extra information about your devices, create a private internal 00151 // array within this file (see os_freebsd.cpp for an example). If you 00152 // can not open the device (permission denied, does not exist, etc) 00153 // set errno as open() does and return <0. 00154 int deviceopen(const char *pathname, char *type){ 00155 00156 int fd; 00157 APIRET rc; 00158 ULONG ActionTaken; 00159 00160 //printf( "deviceopen pathname %s\n", pathname); 00161 rc = DosOpen ("\\DEV\\IBMS506$", &hDevice, &ActionTaken, 0, FILE_SYSTEM, 00162 OPEN_ACTION_OPEN_IF_EXISTS, OPEN_SHARE_DENYNONE | 00163 OPEN_FLAGS_NOINHERIT | OPEN_ACCESS_READONLY, NULL); 00164 if (rc) { 00165 char errmsg[256]; 00166 snprintf(errmsg,256,"Smartctl open driver IBMS506$ failed (%d)", rc); 00167 errmsg[255]='\0'; 00168 syserror(errmsg); 00169 return -1; 00170 } 00171 00172 pathname = skipdev(pathname); 00173 fd = tolower(pathname[2]) - 'a'; 00174 00175 return fd; 00176 } 00177 00178 // Like close(). Acts only on integer handles returned by 00179 // deviceopen() above. 00180 int deviceclose(int fd){ 00181 00182 DosClose( hDevice); 00183 hDevice = NULL; 00184 00185 return 0; 00186 } 00187 00188 static void print_ide_regs(const IDEREGS * r, int out) 00189 { 00190 pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, NS=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n", 00191 (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg, 00192 r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg); 00193 } 00194 00195 // 00196 // OS/2 direct ioctl interface to IBMS506$ 00197 // 00198 int dani_ioctl( int device, int request, void* arg) 00199 { 00200 unsigned char* buff = (unsigned char*) arg; 00201 APIRET rc; 00202 DSKSP_CommandParameters Parms; 00203 ULONG PLen = 1; 00204 ULONG DLen = 512; //sizeof (*buf); 00205 UCHAR temp; 00206 ULONG value = 0; 00207 IDEREGS regs; 00208 00209 //printf( "device %d, request 0x%x, arg[0] 0x%x, arg[2] 0x%x\n", device, request, buff[0], buff[2]); 00210 00211 Parms.byPhysicalUnit = device; 00212 switch( buff[0]) { 00213 case WIN_IDENTIFY: 00214 rc = DosDevIOCtl (hDevice, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA, 00215 (PVOID)&Parms, PLen, &PLen, (PVOID)arg+4, DLen, &DLen); 00216 if (rc != 0) 00217 { 00218 printf ("DANIS506 ATA GET HD Failed (%d,0x%x)\n", rc, rc); 00219 return -1; 00220 } 00221 break; 00222 case WIN_SMART: 00223 switch( buff[2]) { 00224 case SMART_STATUS: 00225 DLen = sizeof(value); 00226 // OS/2 already checks CL/CH in IBM1S506 code!! see s506rte.c (ddk) 00227 // value: -1=not supported, 0=ok, 1=failing 00228 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GETSTATUS, 00229 (PVOID)&Parms, PLen, &PLen, (PVOID)&value, DLen, &DLen); 00230 if (rc) 00231 { 00232 printf ("DANIS506 ATA GET SMART_STATUS failed (%d,0x%x)\n", rc, rc); 00233 return -1; 00234 } 00235 buff[4] = (unsigned char)value; 00236 break; 00237 case SMART_READ_VALUES: 00238 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_ATTRIBUTES, 00239 (PVOID)&Parms, PLen, &PLen, (PVOID)arg+4, DLen, &DLen); 00240 if (rc) 00241 { 00242 printf ("DANIS506 ATA GET DSKSP_SMART_GET_ATTRIBUTES failed (%d,0x%x)\n", rc, rc); 00243 return -1; 00244 } 00245 break; 00246 case SMART_READ_THRESHOLDS: 00247 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_THRESHOLDS, 00248 (PVOID)&Parms, PLen, &PLen, (PVOID)arg+4, DLen, &DLen); 00249 if (rc) 00250 { 00251 printf ("DANIS506 ATA GET DSKSP_SMART_GET_THRESHOLDS failed (%d,0x%x)\n", rc, rc); 00252 return -1; 00253 } 00254 break; 00255 case SMART_READ_LOG_SECTOR: 00256 buff[4] = buff[1]; // copy select field 00257 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_READ_LOG, 00258 (PVOID)&Parms, PLen, &PLen, (PVOID)arg+4, DLen, &DLen); 00259 if (rc) 00260 { 00261 printf ("DANIS506 ATA GET DSKSP_SMART_READ_LOG failed (%d,0x%x)\n", rc, rc); 00262 return -1; 00263 } 00264 break; 00265 case SMART_ENABLE: 00266 buff[0] = 1; // enable 00267 DLen = 1; 00268 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_ONOFF, 00269 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen); 00270 if (rc) { 00271 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%d,0x%x)\n", rc, rc); 00272 return -1; 00273 } 00274 break; 00275 case SMART_DISABLE: 00276 buff[0] = 0; // disable 00277 DLen = 1; 00278 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_ONOFF, 00279 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen); 00280 if (rc) { 00281 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%d,0x%x)\n", rc, rc); 00282 return -1; 00283 } 00284 break; 00285 #if 0 00286 case SMART_AUTO_OFFLINE: 00287 buff[0] = buff[3]; // select field 00288 DLen = 1; 00289 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_AUTO_OFFLINE, 00290 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen); 00291 if (rc) { 00292 printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%d,0x%x)\n", rc, rc); 00293 return -1; 00294 } 00295 break; 00296 #endif 00297 case SMART_AUTOSAVE: 00298 buff[0] = buff[3]; // select field 00299 DLen = 1; 00300 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_AUTOSAVE_ONOFF, 00301 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen); 00302 if (rc) { 00303 printf ("DANIS506 ATA DSKSP_SMART_AUTOSAVE_ONOFF failed (%d,0x%x)\n", rc, rc); 00304 return -1; 00305 } 00306 break; 00307 case SMART_IMMEDIATE_OFFLINE: 00308 buff[0] = buff[1]; // select field 00309 DLen = 1; 00310 rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_EOLI, 00311 (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen); 00312 if (rc) { 00313 printf ("DANIS506 ATA GET DSKSP_SMART_EXEC_OFFLINE failed (%d,0x%x)\n", rc, rc); 00314 return -1; 00315 } 00316 break; 00317 00318 default: 00319 fprintf( stderr, "device %d, request 0x%x, arg[0] 0x%x, arg[2] 0x%x\n", device, request, buff[0], buff[2]); 00320 fprintf( stderr, "unknown ioctl\n"); 00321 return -1; 00322 break; 00323 } 00324 break; 00325 //case WIN_PIDENTIFY: 00326 // break; 00327 default: 00328 fprintf( stderr, "unknown ioctl\n"); 00329 return -1; 00330 break; 00331 } 00332 00333 // ok 00334 return 0; 00335 } 00336 00337 // Interface to ATA devices. See os_linux.cpp for the cannonical example. 00338 // DETAILED DESCRIPTION OF ARGUMENTS 00339 // device: is the integer handle provided by deviceopen() 00340 // command: defines the different operations, see atacmds.h 00341 // select: additional input data IF NEEDED (which log, which type of 00342 // self-test). 00343 // data: location to write output data, IF NEEDED (1 or 512 bytes). 00344 // Note: not all commands use all arguments. 00345 // RETURN VALUES (for all commands BUT command==STATUS_CHECK) 00346 // -1 if the command failed 00347 // 0 if the command succeeded, 00348 // RETURN VALUES if command==STATUS_CHECK 00349 // -1 if the command failed OR the disk SMART status can't be determined 00350 // 0 if the command succeeded and disk SMART status is "OK" 00351 // 1 if the command succeeded and disk SMART status is "FAILING" 00352 00353 // huge value of buffer size needed because HDIO_DRIVE_CMD assumes 00354 // that buff[3] is the data size. Since the ATA_SMART_AUTOSAVE and 00355 // ATA_SMART_AUTO_OFFLINE use values of 0xf1 and 0xf8 we need the space. 00356 // Otherwise a 4+512 byte buffer would be enough. 00357 #define STRANGE_BUFFER_LENGTH (4+512*0xf8) 00358 00359 int ata_command_interface(int device, smart_command_set command, int select, char *data){ 00360 unsigned char buff[STRANGE_BUFFER_LENGTH]; 00361 // positive: bytes to write to caller. negative: bytes to READ from 00362 // caller. zero: non-data command 00363 int copydata=0; 00364 00365 const int HDIO_DRIVE_CMD_OFFSET = 4; 00366 00367 // See struct hd_drive_cmd_hdr in hdreg.h. Before calling ioctl() 00368 // buff[0]: ATA COMMAND CODE REGISTER 00369 // buff[1]: ATA SECTOR NUMBER REGISTER == LBA LOW REGISTER 00370 // buff[2]: ATA FEATURES REGISTER 00371 // buff[3]: ATA SECTOR COUNT REGISTER 00372 00373 // Note that on return: 00374 // buff[2] contains the ATA SECTOR COUNT REGISTER 00375 00376 // clear out buff. Large enough for HDIO_DRIVE_CMD (4+512 bytes) 00377 memset(buff, 0, STRANGE_BUFFER_LENGTH); 00378 00379 //printf( "command, select %d,%d\n", command, select); 00380 buff[0]=ATA_SMART_CMD; 00381 switch (command){ 00382 case CHECK_POWER_MODE: 00383 buff[0]=ATA_CHECK_POWER_MODE; 00384 copydata=1; 00385 break; 00386 case READ_VALUES: 00387 buff[2]=ATA_SMART_READ_VALUES; 00388 buff[3]=1; 00389 copydata=512; 00390 break; 00391 case READ_THRESHOLDS: 00392 buff[2]=ATA_SMART_READ_THRESHOLDS; 00393 buff[1]=buff[3]=1; 00394 copydata=512; 00395 break; 00396 case READ_LOG: 00397 buff[2]=ATA_SMART_READ_LOG_SECTOR; 00398 buff[1]=select; 00399 buff[3]=1; 00400 copydata=512; 00401 break; 00402 case WRITE_LOG: 00403 break; 00404 case IDENTIFY: 00405 buff[0]=ATA_IDENTIFY_DEVICE; 00406 buff[3]=1; 00407 copydata=512; 00408 break; 00409 case PIDENTIFY: 00410 buff[0]=ATA_IDENTIFY_PACKET_DEVICE; 00411 buff[3]=1; 00412 copydata=512; 00413 break; 00414 case ENABLE: 00415 buff[2]=ATA_SMART_ENABLE; 00416 buff[1]=1; 00417 break; 00418 case DISABLE: 00419 buff[2]=ATA_SMART_DISABLE; 00420 buff[1]=1; 00421 break; 00422 case STATUS: 00423 case STATUS_CHECK: 00424 // this command only says if SMART is working. It could be 00425 // replaced with STATUS_CHECK below. 00426 buff[2]=ATA_SMART_STATUS; 00427 buff[4]=0; 00428 break; 00429 case AUTO_OFFLINE: 00430 buff[2]=ATA_SMART_AUTO_OFFLINE; 00431 buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!! 00432 break; 00433 case AUTOSAVE: 00434 buff[2]=ATA_SMART_AUTOSAVE; 00435 buff[3]=select; // YET NOTE - THIS IS A NON-DATA COMMAND!! 00436 break; 00437 case IMMEDIATE_OFFLINE: 00438 buff[2]=ATA_SMART_IMMEDIATE_OFFLINE; 00439 buff[1]=select; 00440 break; 00441 //case STATUS_CHECK: 00442 // // This command uses HDIO_DRIVE_TASK and has different syntax than 00443 // // the other commands. 00444 // buff[1]=ATA_SMART_STATUS; 00445 // break; 00446 default: 00447 pout("Unrecognized command %d in linux_ata_command_interface()\n" 00448 "Please contact " PACKAGE_BUGREPORT "\n", command); 00449 errno=ENOSYS; 00450 return -1; 00451 } 00452 00453 #if 0 00454 // This command uses the HDIO_DRIVE_TASKFILE ioctl(). This is the 00455 // only ioctl() that can be used to WRITE data to the disk. 00456 if (command==WRITE_LOG) { 00457 unsigned char task[sizeof(ide_task_request_t)+512]; 00458 ide_task_request_t *reqtask=(ide_task_request_t *) task; 00459 task_struct_t *taskfile=(task_struct_t *) reqtask->io_ports; 00460 int retval; 00461 00462 memset(task, 0, sizeof(task)); 00463 00464 taskfile->data = 0; 00465 taskfile->feature = ATA_SMART_WRITE_LOG_SECTOR; 00466 taskfile->sector_count = 1; 00467 taskfile->sector_number = select; 00468 taskfile->low_cylinder = 0x4f; 00469 taskfile->high_cylinder = 0xc2; 00470 taskfile->device_head = 0; 00471 taskfile->command = ATA_SMART_CMD; 00472 00473 reqtask->data_phase = TASKFILE_OUT; 00474 reqtask->req_cmd = IDE_DRIVE_TASK_OUT; 00475 reqtask->out_size = 512; 00476 reqtask->in_size = 0; 00477 00478 // copy user data into the task request structure 00479 memcpy(task+sizeof(ide_task_request_t), data, 512); 00480 00481 if ((retval=dani_ioctl(device, HDIO_DRIVE_TASKFILE, task))) { 00482 if (retval==-EINVAL) 00483 pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n"); 00484 return -1; 00485 } 00486 return 0; 00487 } 00488 #endif // 0 00489 00490 // We are now doing the HDIO_DRIVE_CMD type ioctl. 00491 if ((dani_ioctl(device, HDIO_DRIVE_CMD, buff))) 00492 return -1; 00493 00494 // There are two different types of ioctls(). The HDIO_DRIVE_TASK 00495 // one is this: 00496 if (command==STATUS_CHECK){ 00497 int retval; 00498 00499 // Cyl low and Cyl high unchanged means "Good SMART status" 00500 if (buff[4]==0) 00501 return 0; 00502 00503 // These values mean "Bad SMART status" 00504 if (buff[4]==1) 00505 return 1; 00506 00507 // We haven't gotten output that makes sense; print out some debugging info 00508 syserror("Error SMART Status command failed"); 00509 pout("Please get assistance from " PACKAGE_HOMEPAGE "\n"); 00510 return -1; 00511 } 00512 00513 // CHECK POWER MODE command returns information in the Sector Count 00514 // register (buff[3]). Copy to return data buffer. 00515 if (command==CHECK_POWER_MODE) 00516 buff[HDIO_DRIVE_CMD_OFFSET]=buff[2]; 00517 00518 // if the command returns data then copy it back 00519 if (copydata) 00520 memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata); 00521 00522 return 0; 00523 } 00524 00525 // Interface to SCSI devices. See os_linux.c 00526 int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) { 00527 unsupported(3); 00528 return -ENOSYS; 00529 }
1.7.4