| #include <unistd.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <sys/ioctl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include "sg_include.h" |
| #include "sg_lib.h" |
| #include "sg_cmds.h" |
| |
| /* A utility program for the Linux OS SCSI generic ("sg") device driver. |
| * Copyright (C) 2000-2005 D. Gilbert |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2, or (at your option) |
| * any later version. |
| |
| This program outputs information provided by a SCSI LOG SENSE command. |
| |
| */ |
| |
| static char * version_str = "0.39 20050427"; |
| |
| #define ME "sg_logs: " |
| |
| #define MX_ALLOC_LEN (1024 * 17) |
| #define PG_CODE_ALL 0x00 |
| #define EBUFF_SZ 256 |
| |
| |
| /* Call LOG SENSE twice: the first time ask for 4 byte response to determine |
| actual length of response; then a second time requesting the |
| min(actual_len, mx_resp_len) bytes. If the calculated length for the |
| second fetch is odd then it is incremented (perhaps should be made modulo 4 |
| in the future for SAS). Returns 0 if ok, SG_LIB_CAT_INVALID_OP for |
| log_sense not supported, SG_LIB_CAT_ILLEGAL_REQ for bad field in log sense |
| command nd -1 for other errors. */ |
| static int do_logs(int sg_fd, int ppc, int sp, int pc, int pg_code, |
| int paramp, unsigned char * resp, int mx_resp_len, |
| int noisy, int verbose) |
| { |
| int actual_len; |
| int res; |
| |
| memset(resp, 0, mx_resp_len); |
| if ((res = sg_ll_log_sense(sg_fd, ppc, sp, pc, pg_code, paramp, resp, |
| 4, noisy, verbose))) { |
| if ((SG_LIB_CAT_INVALID_OP == res) || (SG_LIB_CAT_ILLEGAL_REQ == res)) |
| return res; |
| return -1; |
| } |
| actual_len = (resp[2] << 8) + resp[3] + 4; |
| /* Some HBAs don't like odd transfer lengths */ |
| if (actual_len % 2) |
| actual_len += 1; |
| if (actual_len > mx_resp_len) |
| actual_len = mx_resp_len; |
| if ((res = sg_ll_log_sense(sg_fd, ppc, sp, pc, pg_code, paramp, resp, |
| actual_len, noisy, verbose))) { |
| if ((SG_LIB_CAT_INVALID_OP == res) || (SG_LIB_CAT_ILLEGAL_REQ == res)) |
| return res; |
| return -1; |
| } |
| return 0; |
| } |
| |
| static void usage() |
| { |
| printf("Usage: 'sg_logs [-a] [-c=<page_control] [-h] [-l] " |
| "[-p=<page_number>]\n [-p=<page_number>] " |
| " [-paramp=<parameter_pointer> [-ppc] [-sp]\n" |
| " [-t] [-v] [-V] <sg_device>'\n" |
| " where -a output all log pages\n" |
| " -c=<page_control> page control(PC) (default: 1)\n" |
| " (0 [current threshhold], 1 [current cumulative]\n" |
| " 2 [default threshhold], 3 [default cumulative])\n" |
| " -h output in hex\n" |
| " -l list supported log page names\n" |
| " -p=<page_code> page code (in hex)\n" |
| " -paramp=<parameter_pointer> (in hex) (def: 0)\n" |
| " -pcb show parameter control bytes (ignored if -h given)\n" |
| " -ppc set the Parameter Pointer Control (PPC) bit (def: 0)\n" |
| " -r reset all implemented parameters to target defined " |
| "defaults\n" |
| " -sp set the Saving Parameters (SP) bit (def: 0)\n" |
| " -t outputs temperature log page (0xd)\n" |
| " -v verbose: output cdbs prior to execution\n" |
| " -V output version string\n" |
| " -? output this usage message\n"); |
| } |
| |
| static void show_page_name(int page_no, |
| struct sg_simple_inquiry_resp * inq_dat) |
| { |
| int done; |
| |
| /* first process log pages that do not depend on peripheral type */ |
| done = 1; |
| switch (page_no) { |
| case 0x0: printf(" 0x00 Supported log pages\n"); break; |
| case 0x1: printf(" 0x01 Buffer over-run/under-run\n"); break; |
| case 0x2: printf(" 0x02 Error counters (write)\n"); break; |
| case 0x3: printf(" 0x03 Error counters (read)\n"); break; |
| case 0x4: printf(" 0x04 Error counters (read reverse)\n"); break; |
| case 0x5: printf(" 0x05 Error counters (verify)\n"); break; |
| case 0x6: printf(" 0x06 Non-medium errors\n"); break; |
| case 0x7: printf(" 0x07 Last n error events\n"); break; |
| case 0xb: printf(" 0x0b Last n deferred errors or " |
| "asynchronous events\n"); break; |
| case 0xd: printf(" 0x0d Temperature\n"); break; |
| case 0xe: printf(" 0x0e Start-stop cycle counter\n"); break; |
| case 0xf: printf(" 0x0f Application client\n"); break; |
| case 0x10: printf(" 0x10 Self-test results\n"); break; |
| case 0x18: printf(" 0x18 Protocol specific port\n"); break; |
| case 0x2f: printf(" 0x2f Informational exceptions (SMART)\n"); |
| break; |
| default : done = 0; break; |
| } |
| if (done) |
| return; |
| |
| done = 1; |
| switch (inq_dat->peripheral_type) { |
| case 0: case 4: case 7: case 0xe: |
| /* disk (direct access) type devices */ |
| { |
| switch (page_no) { |
| case 0x8: |
| printf(" 0x08 Format status (sbc-2)\n"); |
| break; |
| case 0x15: |
| printf(" 0x15 Background scan results (sbc-2)\n"); |
| break; |
| case 0x17: |
| printf(" 0x17 Non-volatile cache (sbc-2)\n"); |
| break; |
| case 0x30: |
| printf(" 0x30 Performance counters (Hitachi)\n"); |
| break; |
| case 0x37: |
| printf(" 0x37 Cache (Seagate), Miscellaneous" |
| " (Hitachi)\n"); |
| break; |
| case 0x3e: |
| printf(" 0x3e Factory (Seagate/Hitachi)\n"); |
| break; |
| default: |
| done = 0; |
| break; |
| } |
| } |
| break; |
| case 1: case 2: case 8: |
| /* tape (streaming) and medium changer type devices */ |
| { |
| switch (page_no) { |
| case 0xc: |
| printf(" 0x0c Sequential Access (ssc-2)\n"); |
| break; |
| case 0x2e: |
| printf(" 0x2e Tape alerts (ssc-2)\n"); |
| break; |
| default: |
| done = 0; |
| break; |
| } |
| } |
| case 0x12: /* Automation Device interface (ADC) */ |
| { |
| switch (page_no) { |
| case 0x11: |
| printf(" 0x11 DTD status (adc)\n"); |
| break; |
| case 0x12: |
| printf(" 0x12 Tape alert response (adc)\n"); |
| break; |
| case 0x13: |
| printf(" 0x13 Requested recovery (adc)\n"); |
| break; |
| case 0x14: |
| printf(" 0x14 Device statistics (adc)\n"); |
| break; |
| case 0x15: |
| printf(" 0x14 DT device log information (adc)\n"); |
| break; |
| default: |
| done = 0; |
| break; |
| } |
| } |
| |
| default: done = 0; break; |
| } |
| if (done) |
| return; |
| |
| printf(" 0x%.2x ??\n", page_no); |
| } |
| |
| static void get_pcb_str(int pcb, char * outp, int maxoutlen) |
| { |
| char buff[128]; |
| int n; |
| |
| n = sprintf(buff, "du=%d ds=%d tsd=%d etc=%d ", ((pcb & 0x80) ? 1 : 0), |
| ((pcb & 0x40) ? 1 : 0), ((pcb & 0x20) ? 1 : 0), |
| ((pcb & 0x10) ? 1 : 0)); |
| if (pcb & 0x10) |
| n += sprintf(buff + n, "tmc=%d ", ((pcb & 0xc) >> 2)); |
| if (pcb & 0x1) |
| n += sprintf(buff + n, "lbin=%d ", ((pcb & 0x2) >> 1)); |
| n += sprintf(buff + n, "lp=%d [0x%.2x]", pcb & 0x1, pcb); |
| if (outp && (n < maxoutlen)) { |
| memcpy(outp, buff, n); |
| outp[n] = '\0'; |
| } else if (outp && (maxoutlen > 0)) |
| outp[0] = '\0'; |
| } |
| |
| static void show_buffer_under_overrun_page(unsigned char * resp, int len, |
| int show_pcb) |
| { |
| int k, j, num, pl, count_basis, cause, pcb; |
| unsigned char * ucp; |
| unsigned char * xp; |
| unsigned long long ull; |
| char pcb_str[64]; |
| |
| printf("Buffer over-run/under-run page\n"); |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| while (num > 3) { |
| pl = ucp[3] + 4; |
| count_basis = (ucp[1] >> 5) & 0x7; |
| cause = (ucp[1] >> 1) & 0xf; |
| if ((0 == count_basis) && (0 == cause)) |
| printf("Count basis+Cause both undefined(0), unsupported??"); |
| else { |
| printf(" Count basis: "); |
| switch (count_basis) { |
| case 0 : printf("undefined"); break; |
| case 1 : printf("per command"); break; |
| case 2 : printf("per failed reconnect"); break; |
| case 3 : printf("per unit of time"); break; |
| default: printf("reserved [0x%x]", count_basis); break; |
| } |
| printf(", Cause: "); |
| switch (cause) { |
| case 0 : printf("undefined"); break; |
| case 1 : printf("bus busy"); break; |
| case 2 : printf("transfer rate too slow"); break; |
| default: printf("reserved [0x%x]", cause); break; |
| } |
| printf(", Type: "); |
| if (ucp[1] & 1) |
| printf("over-run"); |
| else |
| printf("under-run"); |
| printf(", count"); |
| k = pl - 4; |
| xp = ucp + 4; |
| if (k > (int)sizeof(ull)) { |
| xp += (k - sizeof(ull)); |
| k = sizeof(ull); |
| } |
| ull = 0; |
| for (j = 0; j < k; ++j) { |
| if (j > 0) |
| ull <<= 8; |
| ull |= xp[j]; |
| } |
| printf(" = %llu", ull); |
| } |
| if (show_pcb) { |
| pcb = ucp[2]; |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } else |
| printf("\n"); |
| num -= pl; |
| ucp += pl; |
| } |
| } |
| |
| static void show_error_counter_page(unsigned char * resp, int len, |
| int show_pcb) |
| { |
| int k, j, num, pl, pc, pcb; |
| unsigned char * ucp; |
| unsigned char * xp; |
| unsigned long long ull; |
| char pcb_str[64]; |
| |
| switch(resp[0]) { |
| case 2: |
| printf("Write error counter page\n"); |
| break; |
| case 3: |
| printf("Read error counter page\n"); |
| break; |
| case 4: |
| printf("Read Reverse error counter page\n"); |
| break; |
| case 5: |
| printf("Verify error counter page\n"); |
| break; |
| default: |
| printf("expecting error counter page, got page = 0x%x\n", resp[0]); |
| return; |
| } |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| while (num > 3) { |
| pc = (ucp[0] << 8) | ucp[1]; |
| pcb = ucp[2]; |
| pl = ucp[3] + 4; |
| switch (pc) { |
| case 0: printf(" Errors corrected without substantial delay"); break; |
| case 1: printf(" Errors corrected with possible delays"); break; |
| case 2: printf(" Total operations"); break; |
| case 3: printf(" Total errors corrected"); break; |
| case 4: printf(" Total times correction algorithm processed"); break; |
| case 5: printf(" Total bytes processed"); break; |
| case 6: printf(" Total uncorrected errors"); break; |
| case 0x8009: printf(" Track following errors [Hitachi]"); break; |
| case 0x8015: printf(" Positioning errors [Hitachi]"); break; |
| default: printf(" Reserved or vendor specific [0x%x]", pc); break; |
| } |
| k = pl - 4; |
| xp = ucp + 4; |
| if (k > (int)sizeof(ull)) { |
| xp += (k - sizeof(ull)); |
| k = sizeof(ull); |
| } |
| ull = 0; |
| for (j = 0; j < k; ++j) { |
| if (j > 0) |
| ull <<= 8; |
| ull |= xp[j]; |
| } |
| printf(" = %llu", ull); |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } else |
| printf("\n"); |
| num -= pl; |
| ucp += pl; |
| } |
| } |
| |
| static void show_non_medium_error_page(unsigned char * resp, int len, |
| int show_pcb) |
| { |
| int k, j, num, pl, pc, pcb; |
| unsigned char * ucp; |
| unsigned char * xp; |
| unsigned long long ull; |
| char pcb_str[64]; |
| |
| printf("Non-medium error page\n"); |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| while (num > 3) { |
| pc = (ucp[0] << 8) | ucp[1]; |
| pcb = ucp[2]; |
| pl = ucp[3] + 4; |
| switch (pc) { |
| case 0: |
| printf(" Non-medium error count"); break; |
| default: |
| if (pc <= 0x7fff) |
| printf(" Reserved [0x%x]", pc); |
| else |
| printf(" Vendor specific [0x%x]", pc); |
| break; |
| } |
| k = pl - 4; |
| xp = ucp + 4; |
| if (k > (int)sizeof(ull)) { |
| xp += (k - sizeof(ull)); |
| k = sizeof(ull); |
| } |
| ull = 0; |
| for (j = 0; j < k; ++j) { |
| if (j > 0) |
| ull <<= 8; |
| ull |= xp[j]; |
| } |
| printf(" = %llu", ull); |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } else |
| printf("\n"); |
| num -= pl; |
| ucp += pl; |
| } |
| } |
| |
| static void show_last_n_error_page(unsigned char * resp, int len, |
| int show_pcb) |
| { |
| int k, num, pl, pc, pcb; |
| unsigned char * ucp; |
| char pcb_str[64]; |
| |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| if (num < 4) { |
| printf("No error events logged\n"); |
| return; |
| } |
| printf("Last n error events log page\n"); |
| for (k = num; k > 0; k -= pl, ucp += pl) { |
| if (k < 3) { |
| printf("short Last n error events log page\n"); |
| return; |
| } |
| pl = ucp[3] + 4; |
| pc = (ucp[0] << 8) + ucp[1]; |
| pcb = ucp[2]; |
| printf(" Error event %d:\n", pc); |
| if (pcb & 0x2) { |
| printf(" [binary]:\n"); |
| dStrHex((const char *)ucp + 4, pl - 4, 1); |
| } else |
| printf(" %.*s\n", pl - 4, (const char *)(ucp + 4)); |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } |
| num -= pl; |
| ucp += pl; |
| } |
| } |
| |
| static void show_last_n_deferred_error_page(unsigned char * resp, |
| int len, int show_pcb) |
| { |
| int k, num, pl, pc, pcb; |
| unsigned char * ucp; |
| char pcb_str[64]; |
| |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| if (num < 4) { |
| printf("No deferred errors logged\n"); |
| return; |
| } |
| printf("Last n deferred errors log page\n"); |
| for (k = num; k > 0; k -= pl, ucp += pl) { |
| if (k < 3) { |
| printf("short Last n deferred errors log page\n"); |
| return; |
| } |
| pl = ucp[3] + 4; |
| pc = (ucp[0] << 8) + ucp[1]; |
| pcb = ucp[2]; |
| printf(" Deferred error %d:\n", pc); |
| dStrHex((const char *)ucp + 4, pl - 4, 1); |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } |
| num -= pl; |
| ucp += pl; |
| } |
| } |
| |
| static const char * self_test_code[] = { |
| "default", "background short", "background extended", "reserved", |
| "aborted background", "foreground short", "foreground extended", |
| "reserved"}; |
| |
| static const char * self_test_result[] = { |
| "completed without error", |
| "aborted by SEND DIAGNOSTIC", |
| "aborted other than by SEND DIAGNOSTIC", |
| "unknown error, unable to complete", |
| "self test completed with failure in test segment (which one unkown)", |
| "first segment in self test failed", |
| "second segment in self test failed", |
| "another segment in self test failed", |
| "reserved", "reserved", "reserved", "reserved", "reserved", "reserved", |
| "reserved", |
| "self test in progress"}; |
| |
| static void show_self_test_page(unsigned char * resp, int len, int show_pcb) |
| { |
| int k, num, n, res, pcb; |
| unsigned char * ucp; |
| unsigned long long ull; |
| char pcb_str[64]; |
| |
| num = len - 4; |
| if (num < 0x190) { |
| printf("short self-test results page [length 0x%x rather than " |
| "0x190 bytes]\n", num); |
| return; |
| } |
| printf("Self-test results page\n"); |
| for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) { |
| pcb = ucp[2]; |
| n = (ucp[6] << 8) | ucp[7]; |
| if ((0 == n) && (0 == ucp[4])) |
| break; |
| printf(" Parameter code = %d, accumulated power-on hours = %d\n", |
| (ucp[0] << 8) | ucp[1], n); |
| printf(" self-test code: %s [%d]\n", |
| self_test_code[(ucp[4] >> 5) & 0x7], (ucp[4] >> 5) & 0x7); |
| res = ucp[4] & 0xf; |
| printf(" self-test result: %s [%d]\n", |
| self_test_result[res], res); |
| if (ucp[5]) |
| printf(" self-test number = %d\n", (int)ucp[5]); |
| ull = ucp[8]; ull <<= 8; ull |= ucp[9]; ull <<= 8; ull |= ucp[10]; |
| ull <<= 8; ull |= ucp[11]; ull <<= 8; ull |= ucp[12]; |
| ull <<= 8; ull |= ucp[13]; ull <<= 8; ull |= ucp[14]; |
| ull <<= 8; ull |= ucp[15]; |
| if ((0xffffffffffffffffULL != ull) && (res > 0) && ( res < 0xf)) |
| printf(" address of first error = 0x%llx\n", ull); |
| if (ucp[16] & 0xf) |
| printf(" sense key = 0x%x, asc = 0x%x, asq = 0x%x", |
| ucp[16] & 0xf, ucp[17], ucp[18]); |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } else |
| printf("\n"); |
| } |
| } |
| |
| static void show_Temperature_page(unsigned char * resp, int len, |
| int show_pcb, int hdr, int show_unknown) |
| { |
| int k, num, extra, pc, pcb; |
| unsigned char * ucp; |
| char pcb_str[64]; |
| |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| if (num < 4) { |
| printf("badly formed Temperature log page\n"); |
| return; |
| } |
| if (hdr) |
| printf("Temperature log page\n"); |
| for (k = num; k > 0; k -= extra, ucp += extra) { |
| if (k < 3) { |
| printf("short Temperature log page\n"); |
| return; |
| } |
| extra = ucp[3] + 4; |
| pc = (ucp[0] << 8) + ucp[1]; |
| pcb = ucp[2]; |
| if (0 == pc) { |
| if (extra > 5) { |
| if (ucp[5] < 0xff) |
| printf(" Current temperature = %d C", ucp[5]); |
| else |
| printf(" Current temperature = <not available>"); |
| } |
| } else if (1 == pc) { |
| if (extra > 5) { |
| if (ucp[5] < 0xff) |
| printf(" Reference temperature = %d C", ucp[5]); |
| else |
| printf(" Reference temperature = <not available>"); |
| } |
| |
| } else if (show_unknown) { |
| printf(" unknown parameter code = 0x%x, contents in hex:\n", pc); |
| dStrHex((const char *)ucp, extra, 1); |
| } else |
| continue; |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } else |
| printf("\n"); |
| } |
| } |
| |
| static void show_Start_Stop_page(unsigned char * resp, int len, int show_pcb) |
| { |
| int k, num, extra, pc, pcb; |
| unsigned int n; |
| unsigned char * ucp; |
| char pcb_str[64]; |
| |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| if (num < 4) { |
| printf("badly formed Start-stop cycle counter log page\n"); |
| return; |
| } |
| printf("Start-stop cycle counter log page\n"); |
| for (k = num; k > 0; k -= extra, ucp += extra) { |
| if (k < 3) { |
| printf("short Start-stop cycle counter log page\n"); |
| return; |
| } |
| extra = ucp[3] + 4; |
| pc = (ucp[0] << 8) + ucp[1]; |
| pcb = ucp[2]; |
| switch (pc) { |
| case 1: |
| if (extra > 9) |
| printf(" Date of manufacture, year: %.4s, week: %.2s", |
| &ucp[4], &ucp[8]); |
| break; |
| case 2: |
| if (extra > 9) |
| printf(" Accounting date, year: %.4s, week: %.2s", |
| &ucp[4], &ucp[8]); |
| break; |
| case 3: |
| if (extra > 7) { |
| n = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7]; |
| printf(" Specified cycle count over device lifetime = %u", |
| n); |
| } |
| break; |
| case 4: |
| if (extra > 7) { |
| n = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7]; |
| printf(" Accumulated start-stop cycles = %u", n); |
| } |
| break; |
| default: |
| printf(" unknown parameter code = 0x%x, contents in hex:\n", pc); |
| dStrHex((const char *)ucp, extra, 1); |
| break; |
| } |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } else |
| printf("\n"); |
| } |
| } |
| |
| static void show_IE_page(unsigned char * resp, int len, int show_pcb, int full) |
| { |
| int k, num, extra, pc, pcb; |
| unsigned char * ucp; |
| char pcb_str[64]; |
| |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| if (num < 4) { |
| printf("badly formed Informational Exceptions log page\n"); |
| return; |
| } |
| if (full) |
| printf("Informational Exceptions log page\n"); |
| for (k = num; k > 0; k -= extra, ucp += extra) { |
| if (k < 3) { |
| printf("short Informational Exceptions log page\n"); |
| return; |
| } |
| extra = ucp[3] + 4; |
| pc = (ucp[0] << 8) + ucp[1]; |
| pcb = ucp[2]; |
| if (0 == pc) { |
| if (extra > 5) { |
| if (full) |
| printf(" IE asc = 0x%x, ascq = 0x%x", ucp[4], ucp[5]); |
| if (extra > 6) { |
| if (ucp[6] < 0xff) |
| printf("\n Current temperature = %d C", ucp[6]); |
| else |
| printf("\n Current temperature = <not available>"); |
| if (extra > 7) { |
| if (ucp[7] < 0xff) |
| printf("\n Threshold temperature = %d C [IBM " |
| "extension]", ucp[7]); |
| else |
| printf("\n Treshold temperature = <not " |
| "available>"); |
| } |
| } |
| } |
| } else if (full) { |
| printf(" parameter code = 0x%x, contents in hex:\n", pc); |
| dStrHex((const char *)ucp, extra, 1); |
| } |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } else |
| printf("\n"); |
| } |
| } |
| |
| static int show_protocol_specific_page(unsigned char * resp, int len, |
| int show_pcb) |
| { |
| int k, j, num, param_len, nphys, pcb, t, sz; |
| unsigned char * ucp; |
| unsigned char * vcp; |
| unsigned long long ull; |
| unsigned long ul; |
| char pcb_str[64]; |
| char s[64]; |
| |
| sz = sizeof(s); |
| num = len - 4; |
| for (k = 0, ucp = resp + 4; k < num; ) { |
| pcb = ucp[2]; |
| param_len = ucp[3] + 4; |
| /* each phy has a 48 byte descriptor but since param_len is |
| an 8 bit quantity then only the first 5 phys (of, for example, |
| a 8 phy wide link) can be represented */ |
| if (6 != (0xf & ucp[4])) |
| return 0; /* only decode SAS log page */ |
| printf("SAS Protocol Specific page\n"); |
| printf("relative target port id=%d\n", (ucp[0] << 8) | ucp[1]); |
| nphys = ucp[7]; |
| printf("number of phys = %d\n", nphys); |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } else |
| printf("\n"); |
| |
| for (j = 0, vcp = ucp + 8; j < (param_len - 8); vcp += 48, j += 48 ) { |
| printf(" phy identifier = %d\n", vcp[1]); |
| t = ((0x70 & vcp[4]) >> 4); |
| switch (t) { |
| case 0: snprintf(s, sz, "no device attached"); break; |
| case 1: snprintf(s, sz, "end device"); break; |
| case 2: snprintf(s, sz, "edge expander device"); break; |
| case 3: snprintf(s, sz, "fanout expander device"); break; |
| default: snprintf(s, sz, "reserved [%d]", t); break; |
| } |
| printf(" attached device type: %s\n", s); |
| t = (0xf & vcp[5]); |
| switch (t) { |
| case 0: snprintf(s, sz, "phy enabled; unknown"); |
| break; |
| case 1: snprintf(s, sz, "phy disabled"); break; |
| case 2: snprintf(s, sz, "phy enabled; speed negotiation failed"); |
| break; |
| case 3: snprintf(s, sz, "phy enabled; SATA spinup hold state"); |
| break; |
| case 8: snprintf(s, sz, "phy enabled; 1.5Gbps"); break; |
| case 9: snprintf(s, sz, "phy enabled; 3.0Gbps"); break; |
| default: snprintf(s, sz, "reserved [%d]", t); break; |
| } |
| printf(" negotiated physical link rate: %s\n", s); |
| printf(" attached initiator port: ssp=%d, stp=%d smp=%d\n", |
| !! (vcp[6] & 8), !! (vcp[6] & 4), (vcp[6] & 2)); |
| printf(" attached target port: ssp=%d, stp=%d smp=%d\n", |
| !! (vcp[7] & 8), !! (vcp[7] & 4), (vcp[7] & 2)); |
| ull = vcp[8]; ull <<= 8; ull |= vcp[9]; ull <<= 8; ull |= vcp[10]; |
| ull <<= 8; ull |= vcp[11]; ull <<= 8; ull |= vcp[12]; |
| ull <<= 8; ull |= vcp[13]; ull <<= 8; ull |= vcp[14]; |
| ull <<= 8; ull |= vcp[15]; |
| printf(" SAS address = 0x%llx\n", ull); |
| ull = vcp[16]; ull <<= 8; ull |= vcp[17]; ull <<= 8; ull |= vcp[18]; |
| ull <<= 8; ull |= vcp[19]; ull <<= 8; ull |= vcp[20]; |
| ull <<= 8; ull |= vcp[21]; ull <<= 8; ull |= vcp[22]; |
| ull <<= 8; ull |= vcp[23]; |
| printf(" attached SAS address = 0x%llx\n", ull); |
| printf(" attached phy identifier = %d\n", vcp[24]); |
| ul = (vcp[32] << 24) | (vcp[33] << 16) | (vcp[34] << 8) | vcp[35]; |
| printf(" Invalid DWORD count = %ld\n", ul); |
| ul = (vcp[36] << 24) | (vcp[37] << 16) | (vcp[38] << 8) | vcp[39]; |
| printf(" Running disparity error count = %ld\n", ul); |
| ul = (vcp[40] << 24) | (vcp[41] << 16) | (vcp[42] << 8) | vcp[43]; |
| printf(" Loss of DWORD synchronization = %ld\n", ul); |
| ul = (vcp[44] << 24) | (vcp[45] << 16) | (vcp[46] << 8) | vcp[47]; |
| printf(" Phy reset problem = %ld\n", ul); |
| } |
| k += param_len; |
| ucp += param_len; |
| } |
| return 1; |
| } |
| |
| static void show_format_status_page(unsigned char * resp, int len, |
| int show_pcb) |
| { |
| int k, j, num, pl, pc, pcb, all_ff, counter; |
| unsigned char * ucp; |
| unsigned char * xp; |
| unsigned long long ull; |
| char pcb_str[64]; |
| |
| printf("Format status page (sbc-2) [0x8]\n"); |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| while (num > 3) { |
| pc = (ucp[0] << 8) | ucp[1]; |
| pcb = ucp[2]; |
| pl = ucp[3] + 4; |
| counter = 1; |
| switch (pc) { |
| case 0: printf(" Format data out:\n"); |
| counter = 0; |
| dStrHex((const char *)ucp, pl, 0); |
| break; |
| case 1: printf(" Grown defects during certification"); break; |
| case 2: printf(" Total blocks relocated during format"); break; |
| case 3: printf(" Total new blocks relocated"); break; |
| case 4: printf(" Power on minutes since format"); break; |
| default: |
| printf(" Unknown Format status code = 0x%x\n", pc); |
| counter = 0; |
| dStrHex((const char *)ucp, pl, 0); |
| break; |
| } |
| if (counter) { |
| k = pl - 4; |
| xp = ucp + 4; |
| if (k > (int)sizeof(ull)) { |
| xp += (k - sizeof(ull)); |
| k = sizeof(ull); |
| } |
| ull = 0; |
| for (all_ff = 0, j = 0; j < k; ++j) { |
| if (j > 0) |
| ull <<= 8; |
| else |
| all_ff = 1; |
| ull |= xp[j]; |
| if (0xff != xp[j]) |
| all_ff = 0; |
| } |
| if (all_ff) |
| printf(" <not available>"); |
| else |
| printf(" = %llu", ull); |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } else |
| printf("\n"); |
| } else { |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } |
| } |
| num -= pl; |
| ucp += pl; |
| } |
| } |
| |
| static void show_non_volatile_cache_page(unsigned char * resp, int len, |
| int show_pcb) |
| { |
| int j, num, pl, pc, pcb; |
| unsigned char * ucp; |
| char pcb_str[64]; |
| |
| printf("Non-volatile cache page (sbc-2) [0x17]\n"); |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| while (num > 3) { |
| pc = (ucp[0] << 8) | ucp[1]; |
| pcb = ucp[2]; |
| pl = ucp[3] + 4; |
| switch (pc) { |
| case 0: |
| printf(" Remaining non-volatile time: "); |
| if (3 == ucp[4]) { |
| j = (ucp[5] << 16) + (ucp[6] << 8) + ucp[7]; |
| switch (j) { |
| case 0: |
| printf("0 (i.e. it is now volatile)\n"); |
| break; |
| case 1: |
| printf("<unknown>\n"); |
| break; |
| case 0xffffff: |
| printf("<indefinite>\n"); |
| break; |
| default: |
| printf("%d minutes [%d:%d]\n", j, (j / 60), (j % 60)); |
| break; |
| } |
| } else |
| printf("<unexpected parameter length=%d>\n", ucp[4]); |
| break; |
| case 1: |
| printf(" Maximum non-volatile time: "); |
| if (3 == ucp[4]) { |
| j = (ucp[5] << 16) + (ucp[6] << 8) + ucp[7]; |
| switch (j) { |
| case 0: |
| printf("0 (i.e. it is now volatile)\n"); |
| break; |
| case 1: |
| printf("<reserved>\n"); |
| break; |
| case 0xffffff: |
| printf("<indefinite>\n"); |
| break; |
| default: |
| printf("%d minutes [%d:%d]\n", j, (j / 60), (j % 60)); |
| break; |
| } |
| } else |
| printf("<unexpected parameter length=%d>\n", ucp[4]); |
| break; |
| default: |
| printf(" Unknown Format status code = 0x%x\n", pc); |
| dStrHex((const char *)ucp, pl, 0); |
| break; |
| } |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } |
| num -= pl; |
| ucp += pl; |
| } |
| } |
| |
| static const char * bms_status[] = { |
| "no scans active", |
| "background medium scan is active", |
| "pre-scan is active", |
| "scan halted due to fatal error", |
| "scan halted due to unusual pattern of error", |
| "scan halted due to medium formatted without P-List", |
| "scan halted - vendor specific cause", |
| "scan halted due to temperature out of range", |
| "scan suspended until BMS Interval Time expires", |
| }; |
| |
| static const char * reassign_status[] = { |
| "No assignment needed", |
| "Reassignment pending receipt of Reassign command or Write command", |
| "LBA successfully reassigned by drive", |
| "Reassign status: Reserved [0x3]", |
| "Reassignment failed", |
| "LBA recovered via re-write", |
| }; |
| |
| static void show_background_scan_results_page(unsigned char * resp, int len, |
| int show_pcb, int verbose) |
| { |
| int j, m, num, pl, pc, pcb; |
| unsigned char * ucp; |
| char str[128]; |
| |
| printf("Background scan results page (sbc-2) [0x15]\n"); |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| while (num > 3) { |
| pc = (ucp[0] << 8) | ucp[1]; |
| pcb = ucp[2]; |
| pl = ucp[3] + 4; |
| switch (pc) { |
| case 0: |
| printf(" Power on time: "); |
| j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7]; |
| printf("%d minutes [%d:%d]\n", j, (j / 60), (j % 60)); |
| break; |
| printf(" BMS status: "); |
| j = ucp[9]; |
| if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0]))) |
| printf("%s\n", bms_status[j]); |
| else |
| printf("unknown [0x%x]\n", j); |
| printf(" Number of scan performed: %d\n", |
| (ucp[10] << 8) + ucp[11]); |
| printf(" Progress of medium scan: %d%%\n", |
| ((ucp[12] << 8) + ucp[13]) * 100 / 65536); |
| |
| break; |
| default: |
| printf(" Medium scan parameter # %d\n", pc); |
| if (pl < 24) { |
| fprintf(stderr, " parameter length >= 24 expected, " |
| "got %d\n", pl); |
| break; |
| } |
| printf(" Power on time when error detected: "); |
| j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7]; |
| printf("%d minutes [%d:%d]\n", j, (j / 60), (j % 60)); |
| j = (ucp[8] >> 4) & 0xf; |
| if (j < |
| (int)(sizeof(reassign_status) / sizeof(reassign_status[0]))) |
| printf(" %s\n", reassign_status[j]); |
| else |
| printf(" Reassign status: reserved [0x%x]\n", j); |
| printf(" sense key: %s sk,asc,ascq: 0x%x,0x%x,0x%x\n", |
| sg_get_sense_key_str(ucp[8] & 0xf, sizeof(str), str), |
| ucp[8] & 0xf, ucp[9], ucp[10]); |
| printf(" %s\n", sg_get_asc_ascq_str(ucp[9], ucp[10], |
| sizeof(str), str)); |
| if (verbose) { |
| printf(" vendor bytes [11 -> 15]: "); |
| for (m = 0; m < 5; ++m) |
| printf("0x%02x ", ucp[11 + m]); |
| printf("\n"); |
| } |
| printf(" LBA (of medium error): 0x"); |
| for (m = 0; m < 8; ++m) |
| printf("%02x", ucp[16 + m]); |
| printf("\n"); |
| break; |
| } |
| if (show_pcb) { |
| get_pcb_str(pcb, str, sizeof(str)); |
| printf(" <%s>\n", str); |
| } |
| num -= pl; |
| ucp += pl; |
| } |
| } |
| |
| static void show_sequential_access_page(unsigned char * resp, int len, |
| int show_pcb, int verbose) |
| { |
| int k, j, num, pl, pc, pcb; |
| unsigned char * ucp; |
| unsigned char * xp; |
| unsigned long long ull, gbytes; |
| char pcb_str[64]; |
| |
| printf("Sequential access device page (ssc-3)\n"); |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| while (num > 3) { |
| pc = (ucp[0] << 8) | ucp[1]; |
| pcb = ucp[2]; |
| pl = ucp[3] + 4; |
| k = pl - 4; |
| xp = ucp + 4; |
| if (k > (int)sizeof(ull)) { |
| xp += (k - sizeof(ull)); |
| k = sizeof(ull); |
| } |
| ull = 0; |
| for (j = 0; j < k; ++j) { |
| if (j > 0) |
| ull <<= 8; |
| ull |= xp[j]; |
| } |
| gbytes = ull / 1000000000; |
| switch (pc) { |
| case 0: |
| printf(" Data bytes received with WRITE commands: %llu GB", |
| gbytes); |
| if (verbose) |
| printf(" [%llu bytes]", ull); |
| printf("\n"); |
| break; |
| case 1: |
| printf(" Data bytes written to media by WRITE commands: %llu " |
| "GB", gbytes); |
| if (verbose) |
| printf(" [%llu bytes]", ull); |
| printf("\n"); |
| break; |
| case 2: |
| printf(" Data bytes read from media by READ commands: %llu " |
| "GB", gbytes); |
| if (verbose) |
| printf(" [%llu bytes]", ull); |
| printf("\n"); |
| break; |
| case 3: |
| printf(" Data bytes transferred by READ commands: %llu " |
| "GB", gbytes); |
| if (verbose) |
| printf(" [%llu bytes]", ull); |
| printf("\n"); |
| break; |
| case 4: |
| printf(" Native capacity from BOP to EOD: %llu MB\n", ull); |
| break; |
| case 5: |
| printf(" Native capacity from BOP to EW of current partition: " |
| "%llu MB\n", ull); |
| break; |
| case 6: |
| printf(" Minimum native capacity from EW to EOP of current " |
| "partition: %llu MB\n", ull); |
| break; |
| case 7: |
| printf(" Native capacity from BOP to current position: %llu " |
| "MB\n", ull); |
| break; |
| case 8: |
| printf(" Maximum native capacity in device object buffer: %llu " |
| "MB\n", ull); |
| break; |
| case 0x100: |
| if (ull > 0) |
| printf(" Cleaning action required\n"); |
| else |
| printf(" Cleaning action not required (or completed)\n"); |
| if (verbose) |
| printf(" cleaning value: %llu\n", ull); |
| break; |
| default: |
| if (pc >= 0x8000) |
| printf(" Vendor specific parameter [0x%x] value: %llu\n", |
| pc, ull); |
| else |
| printf(" Reserved parameter [0x%x] value: %llu\n", |
| pc, ull); |
| break; |
| } |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } else |
| printf("\n"); |
| num -= pl; |
| ucp += pl; |
| } |
| } |
| |
| static void show_seagate_cache_page(unsigned char * resp, int len, |
| int show_pcb) |
| { |
| int k, j, num, pl, pc, pcb; |
| unsigned char * ucp; |
| unsigned char * xp; |
| unsigned long long ull; |
| char pcb_str[64]; |
| |
| printf("Seagate cache page [0x37]\n"); |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| while (num > 3) { |
| pc = (ucp[0] << 8) | ucp[1]; |
| pcb = ucp[2]; |
| pl = ucp[3] + 4; |
| switch (pc) { |
| case 0: printf(" Blocks sent to initiator"); break; |
| case 1: printf(" Blocks received from initiator"); break; |
| case 2: printf(" Blocks read from cache and sent to initiator"); break; |
| case 3: printf(" Number of read and write commands whose size " |
| "<= segment size"); break; |
| case 4: printf(" Number of read and write commands whose size " |
| "> segment size"); break; |
| default: printf(" Unknown Seagate parameter code = 0x%x", pc); break; |
| } |
| k = pl - 4; |
| xp = ucp + 4; |
| if (k > (int)sizeof(ull)) { |
| xp += (k - sizeof(ull)); |
| k = sizeof(ull); |
| } |
| ull = 0; |
| for (j = 0; j < k; ++j) { |
| if (j > 0) |
| ull <<= 8; |
| ull |= xp[j]; |
| } |
| printf(" = %llu", ull); |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } else |
| printf("\n"); |
| num -= pl; |
| ucp += pl; |
| } |
| } |
| |
| static void show_seagate_factory_page(unsigned char * resp, int len, |
| int show_pcb) |
| { |
| int k, j, num, pl, pc, pcb; |
| unsigned char * ucp; |
| unsigned char * xp; |
| unsigned long long ull; |
| char pcb_str[64]; |
| |
| printf("Seagate/Hitachi factory page [0x3e]\n"); |
| num = len - 4; |
| ucp = &resp[0] + 4; |
| while (num > 3) { |
| pc = (ucp[0] << 8) | ucp[1]; |
| pcb = ucp[2]; |
| pl = ucp[3] + 4; |
| switch (pc) { |
| case 0: printf(" number of hours powered up"); break; |
| case 8: printf(" number of minutes until next internal SMART test"); |
| break; |
| default: |
| printf(" Unknown Seagate/Hitachi parameter code = 0x%x", pc); |
| break; |
| } |
| k = pl - 4; |
| xp = ucp + 4; |
| if (k > (int)sizeof(ull)) { |
| xp += (k - sizeof(ull)); |
| k = sizeof(ull); |
| } |
| ull = 0; |
| for (j = 0; j < k; ++j) { |
| if (j > 0) |
| ull <<= 8; |
| ull |= xp[j]; |
| } |
| if (0 == pc) |
| printf(" = %.2f", ((double)ull) / 60.0 ); |
| else |
| printf(" = %llu", ull); |
| if (show_pcb) { |
| get_pcb_str(pcb, pcb_str, sizeof(pcb_str)); |
| printf(" <%s>\n", pcb_str); |
| } else |
| printf("\n"); |
| num -= pl; |
| ucp += pl; |
| } |
| } |
| |
| static void show_ascii_page(unsigned char * resp, int len, int show_pcb, |
| struct sg_simple_inquiry_resp * inq_dat, |
| int verbose) |
| { |
| int k, num, done; |
| |
| if (len < 0) { |
| printf("response has bad length\n"); |
| return; |
| } |
| num = len - 4; |
| done = 1; |
| switch (resp[0]) { |
| case 0: |
| printf("Supported pages:\n"); |
| for (k = 0; k < num; ++k) |
| show_page_name((int)resp[4 + k], inq_dat); |
| break; |
| case 0x1: |
| show_buffer_under_overrun_page(resp, len, show_pcb); |
| break; |
| case 0x2: |
| case 0x3: |
| case 0x4: |
| case 0x5: |
| show_error_counter_page(resp, len, show_pcb); |
| break; |
| case 0x6: |
| show_non_medium_error_page(resp, len, show_pcb); |
| break; |
| case 0x7: |
| show_last_n_error_page(resp, len, show_pcb); |
| break; |
| case 0x8: |
| { |
| switch (inq_dat->peripheral_type) { |
| case 0: case 4: case 7: case 0xe: |
| /* disk (direct access) type devices */ |
| show_format_status_page(resp, len, show_pcb); |
| break; |
| default: |
| done = 0; |
| break; |
| } |
| } |
| break; |
| case 0xb: |
| show_last_n_deferred_error_page(resp, len, show_pcb); |
| break; |
| case 0xc: |
| { |
| switch (inq_dat->peripheral_type) { |
| case 1: case 2: case 8: |
| /* tape, (printer) and medium changer type devices */ |
| show_sequential_access_page(resp, len, show_pcb, verbose); |
| break; |
| default: |
| done = 0; |
| break; |
| } |
| } |
| break; |
| case 0xd: |
| show_Temperature_page(resp, len, show_pcb, 1, 1); |
| break; |
| case 0xe: |
| show_Start_Stop_page(resp, len, show_pcb); |
| break; |
| case 0x10: |
| show_self_test_page(resp, len, show_pcb); |
| break; |
| case 0x15: |
| { |
| switch (inq_dat->peripheral_type) { |
| case 0: case 4: case 7: case 0xe: |
| /* disk (direct access) type devices */ |
| show_background_scan_results_page(resp, len, show_pcb, |
| verbose); |
| break; |
| default: |
| done = 0; |
| break; |
| } |
| } |
| break; |
| case 0x17: |
| { |
| switch (inq_dat->peripheral_type) { |
| case 0: case 4: case 7: case 0xe: |
| /* disk (direct access) type devices */ |
| show_non_volatile_cache_page(resp, len, show_pcb); |
| break; |
| default: |
| done = 0; |
| break; |
| } |
| } |
| break; |
| case 0x18: |
| done = show_protocol_specific_page(resp, len, show_pcb); |
| break; |
| case 0x2f: |
| show_IE_page(resp, len, show_pcb, 1); |
| break; |
| case 0x37: |
| { |
| switch (inq_dat->peripheral_type) { |
| case 0: case 4: case 7: case 0xe: |
| /* disk (direct access) type devices */ |
| show_seagate_cache_page(resp, len, show_pcb); |
| break; |
| default: |
| done = 0; |
| break; |
| } |
| } |
| break; |
| case 0x3e: |
| { |
| switch (inq_dat->peripheral_type) { |
| case 0: case 4: case 7: case 0xe: |
| /* disk (direct access) type devices */ |
| show_seagate_factory_page(resp, len, show_pcb); |
| break; |
| case 1: case 2: case 8: |
| /* streaming or medium changer devices */ |
| /* call ssc_device_status_log_page() */ |
| break; |
| default: |
| done = 0; |
| break; |
| } |
| } |
| break; |
| default: |
| done = 0; |
| break; |
| } |
| if (! done) { |
| printf("No ascii information for page = 0x%x, here is hex:\n", |
| resp[0]); |
| if (len > 128) { |
| dStrHex((const char *)resp, 64, 1); |
| printf(" ..... [truncated after 64 of %d bytes (use '-h' to " |
| "see the rest)]\n", len); |
| } |
| else |
| dStrHex((const char *)resp, len, 1); |
| } |
| } |
| |
| static int fetchTemperature(int sg_fd, unsigned char * resp, int max_len, |
| int verbose) |
| { |
| int res = 0; |
| |
| if (0 == do_logs(sg_fd, 0, 0, 1, 0xd, 0, resp, max_len, 0, verbose)) |
| show_Temperature_page(resp, (resp[2] << 8) + resp[3] + 4, 0, 0, 0); |
| else if (0 == do_logs(sg_fd, 0, 0, 1, 0x2f, 0, resp, max_len, 0, verbose)) |
| show_IE_page(resp, (resp[2] << 8) + resp[3] + 4, 0, 0); |
| else { |
| printf("Unable to find temperature in either log page (temperature " |
| "or IE)\n"); |
| res = 1; |
| } |
| close(sg_fd); |
| return res; |
| } |
| |
| |
| int main(int argc, char * argv[]) |
| { |
| int sg_fd, k, num, pg_len, res; |
| char * file_name = 0; |
| char ebuff[EBUFF_SZ]; |
| unsigned char rsp_buff[MX_ALLOC_LEN]; |
| unsigned int u; |
| int pg_code = 0; |
| int pc = 1; /* N.B. some disks only give data for current cumulative */ |
| int paramp = 0; |
| int do_list = 0; |
| int do_pcb = 0; |
| int do_ppc = 0; |
| int do_sp = 0; |
| int do_hex = 0; |
| int do_all = 0; |
| int do_temp = 0; |
| int do_pcreset = 0; |
| int do_verbose = 0; |
| int oflags = O_RDWR | O_NONBLOCK; |
| int oroflags = O_RDONLY | O_NONBLOCK; |
| struct sg_simple_inquiry_resp inq_out; |
| |
| for (k = 1; k < argc; ++k) { |
| if (0 == strcmp("-a", argv[k])) |
| do_all = 1; |
| else if (0 == strncmp("-c=", argv[k], 3)) { |
| num = sscanf(argv[k] + 3, "%x", &u); |
| if ((1 != num) || (u > 3)) { |
| printf("Bad page control after '-c' switch [0..3]\n"); |
| file_name = 0; |
| break; |
| } |
| pc = u; |
| } else if (0 == strcmp("-h", argv[k])) |
| do_hex = 1; |
| else if (0 == strcmp("-l", argv[k])) |
| do_list = 1; |
| else if (0 == strncmp("-p=", argv[k], 3)) { |
| num = sscanf(argv[k] + 3, "%x", &u); |
| if ((1 != num) || (u > 63)) { |
| printf("Bad page code after '-p' switch [0..63]\n"); |
| file_name = 0; |
| break; |
| } |
| pg_code = u; |
| } else if (0 == strncmp("-paramp=", argv[k], 8)) { |
| num = sscanf(argv[k] + 8, "%x", &u); |
| if ((1 != num) || (u > 0xffff)) { |
| printf("Bad parameter pointer after '-paramp' switch\n"); |
| file_name = 0; |
| break; |
| } |
| paramp = u; |
| } else if (0 == strcmp("-pcb", argv[k])) |
| do_pcb = 1; |
| else if (0 == strcmp("-ppc", argv[k])) |
| do_ppc = 1; |
| else if (0 == strcmp("-r", argv[k])) |
| do_pcreset = 1; |
| else if (0 == strcmp("-sp", argv[k])) |
| do_sp = 1; |
| else if (0 == strcmp("-t", argv[k])) |
| do_temp = 1; |
| else if (0 == strcmp("-v", argv[k])) |
| ++do_verbose; |
| else if (0 == strcmp("-vv", argv[k])) |
| do_verbose += 2; |
| else if (0 == strcmp("-vvv", argv[k])) |
| do_verbose += 3; |
| else if (0 == strcmp("-V", argv[k])) { |
| printf("Version string: %s\n", version_str); |
| exit(0); |
| } else if (0 == strcmp("-?", argv[k])) { |
| file_name = 0; |
| break; |
| } else if (*argv[k] == '-') { |
| printf("Unrecognized switch: %s\n", argv[k]); |
| file_name = 0; |
| break; |
| } else if (0 == file_name) |
| file_name = argv[k]; |
| else { |
| printf("too many arguments\n"); |
| file_name = 0; |
| break; |
| } |
| } |
| if (0 == file_name) { |
| usage(); |
| return 1; |
| } |
| |
| if ((sg_fd = open(file_name, oflags)) < 0) { |
| if ((sg_fd = open(file_name, oroflags)) < 0) { |
| snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s", file_name); |
| perror(ebuff); |
| return 1; |
| } |
| } |
| if (do_list || do_all) |
| pg_code = PG_CODE_ALL; |
| pg_len = 0; |
| |
| if (sg_simple_inquiry(sg_fd, &inq_out, 1, do_verbose)) { |
| printf(ME "%s doesn't respond to a SCSI INQUIRY\n", file_name); |
| close(sg_fd); |
| return 1; |
| } else |
| printf(" %.8s %.16s %.4s\n", inq_out.vendor, inq_out.product, |
| inq_out.revision); |
| |
| if (1 == do_temp) |
| return fetchTemperature(sg_fd, rsp_buff, MX_ALLOC_LEN, do_verbose); |
| |
| if (1 == do_pcreset) { |
| k = sg_ll_log_select(sg_fd, 1, do_sp, pc, NULL, 0, 1, do_verbose); |
| if (SG_LIB_CAT_INVALID_OP == k) |
| fprintf(stderr, "log_select: not supported\n"); |
| return k ? 1 : 0; |
| } |
| res = do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, paramp, rsp_buff, |
| MX_ALLOC_LEN, 1, do_verbose); |
| if (0 == res) { |
| pg_len = (rsp_buff[2] << 8) + rsp_buff[3]; |
| if ((pg_len + 4) > MX_ALLOC_LEN) { |
| printf("Only fetched %d bytes of response, truncate output\n", |
| MX_ALLOC_LEN); |
| pg_len = MX_ALLOC_LEN - 4; |
| } |
| if (do_hex) { |
| printf("Returned log page code=0x%x, page len=0x%x\n", |
| rsp_buff[0], pg_len); |
| dStrHex((const char *)rsp_buff, pg_len + 4, 1); |
| } |
| else |
| show_ascii_page(rsp_buff, pg_len + 4, do_pcb, &inq_out, |
| do_verbose); |
| } else if (SG_LIB_CAT_INVALID_OP == res) |
| fprintf(stderr, "log_sense: not supported\n"); |
| else if (SG_LIB_CAT_ILLEGAL_REQ == res) |
| fprintf(stderr, "log_sense: field in cdb illegal\n"); |
| |
| if (do_all && (pg_len > 1)) { |
| int my_len = pg_len - 1; |
| unsigned char parr[256]; |
| |
| memcpy(parr, rsp_buff + 5, my_len); |
| for (k = 0; k < my_len; ++k) { |
| printf("\n"); |
| pg_code = parr[k]; |
| if (0 == do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, paramp, |
| rsp_buff, MX_ALLOC_LEN, 1, do_verbose)) |
| { |
| pg_len = (rsp_buff[2] << 8) + rsp_buff[3]; |
| if ((pg_len + 4) > MX_ALLOC_LEN) { |
| printf("Only fetched %d bytes of response, truncate " |
| "output\n", MX_ALLOC_LEN); |
| pg_len = MX_ALLOC_LEN - 4; |
| } |
| if (do_hex) { |
| printf("Returned log page code=0x%x, page len=0x%x\n", |
| rsp_buff[0], pg_len); |
| dStrHex((const char *)rsp_buff, pg_len + 4, 1); |
| } |
| else |
| show_ascii_page(rsp_buff, pg_len + 4, do_pcb, &inq_out, |
| do_verbose); |
| } |
| } |
| } |
| close(sg_fd); |
| return 0; |
| } |