1 /*
2  * Copyright (c) 2006-2007 Douglas Gilbert.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <sys/ioctl.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include "sg_lib.h"
40 #include "sg_io_linux.h"
41 
42 /* This program performs a ATA PASS-THROUGH (16) SCSI command in order
43    to perform an ATA SMART/READ DATA command. See http://www.t10.org
44    SAT draft at time of writing: sat-r08.pdf
45 
46    Invocation: sg_sat_smart_rd_data [-v] [-V] <device>
47 
48 */
49 
50 #define SAT_ATA_PASS_THROUGH16 0x85
51 #define SAT_ATA_PASS_THROUGH16_LEN 16
52 #define SAT_ATA_RETURN_DESC 9  /* ATA Return (sense) Descriptor */
53 
54 #define ATA_SMART 0xb0
55 #define ATA_SMART_READ_DATA 0xd0
56 #define SMART_READ_DATA_RESPONSE_LEN 512
57 
58 #define EBUFF_SZ 256
59 
60 static char * version_str = "1.02 20070130";
61 
main(int argc,char * argv[])62 int main(int argc, char * argv[])
63 {
64     int sg_fd, k, ok;
65     unsigned char aptCmdBlk[SAT_ATA_PASS_THROUGH16_LEN] =
66                 {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
67                  0, 0, 0, 0, 0, 0, 0, 0};
68     sg_io_hdr_t io_hdr;
69     char * file_name = 0;
70     char ebuff[EBUFF_SZ];
71     unsigned char inBuff[SMART_READ_DATA_RESPONSE_LEN];
72     unsigned char sense_buffer[32];
73     int verbose = 0;
74     int extend = 0;
75     int chk_cond = 0;   /* set to 1 to read register(s) back */
76     int protocol = 4;   /* PIO data-in */
77     int t_dir = 1;      /* 0 -> to device, 1 -> from device */
78     int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
79     int t_length = 2;   /* 0 -> no data transferred, 2 -> sector count */
80     const unsigned char * ucp = NULL;
81 
82     for (k = 1; k < argc; ++k) {
83         if (0 == strcmp(argv[k], "-v"))
84             ++verbose;
85         else if (0 == strcmp(argv[k], "-vv"))
86             verbose += 2;
87         else if (0 == strcmp(argv[k], "-vvv"))
88             verbose += 3;
89         else if (0 == strcmp(argv[k], "-V")) {
90             fprintf(stderr, "version: %s\n", version_str);
91             exit(0);
92         } else if (*argv[k] == '-') {
93             printf("Unrecognized switch: %s\n", argv[k]);
94             file_name = 0;
95             break;
96         }
97         else if (0 == file_name)
98             file_name = argv[k];
99         else {
100             printf("too many arguments\n");
101             file_name = 0;
102             break;
103         }
104     }
105     if (0 == file_name) {
106         printf("Usage: 'sg_sat_smart_rd_data [-v] [-V] <device>'\n");
107         return 1;
108     }
109 
110     if ((sg_fd = open(file_name, O_RDWR)) < 0) {
111         snprintf(ebuff, EBUFF_SZ,
112                  "sg_sat_smart_rd_data: error opening file: %s", file_name);
113         perror(ebuff);
114         return 1;
115     }
116 
117     /* Prepare ATA PASS-THROUGH COMMAND (16) command */
118     aptCmdBlk[4] = ATA_SMART_READ_DATA;   /* feature (7:0) */
119     aptCmdBlk[6] = 1;   /* number of block (sector count) */
120     aptCmdBlk[10] = 0x4f;    /* lba_mid (7:0) */
121     aptCmdBlk[12] = 0xc2;    /* lba_high (7:0) */
122     aptCmdBlk[14] = ATA_SMART;
123     aptCmdBlk[1] = (protocol << 1) | extend;
124     aptCmdBlk[2] = (chk_cond << 5) | (t_dir << 3) |
125                    (byte_block << 2) | t_length;
126     if (verbose) {
127         fprintf(stderr, "    ata pass through(16) cdb: ");
128         for (k = 0; k < SAT_ATA_PASS_THROUGH16_LEN; ++k)
129             fprintf(stderr, "%02x ", aptCmdBlk[k]);
130         fprintf(stderr, "\n");
131     }
132 
133     memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
134     io_hdr.interface_id = 'S';
135     io_hdr.cmd_len = sizeof(aptCmdBlk);
136     /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
137     io_hdr.mx_sb_len = sizeof(sense_buffer);
138     io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
139     io_hdr.dxfer_len = SMART_READ_DATA_RESPONSE_LEN;
140     io_hdr.dxferp = inBuff;
141     io_hdr.cmdp = aptCmdBlk;
142     io_hdr.sbp = sense_buffer;
143     io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
144     /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
145     /* io_hdr.pack_id = 0; */
146     /* io_hdr.usr_ptr = NULL; */
147 
148     if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
149         perror("sg_sat_smart_rd_data: SG_IO ioctl error");
150         close(sg_fd);
151         return 1;
152     }
153 
154     /* now for the error processing */
155     ok = 0;
156     switch (sg_err_category3(&io_hdr)) {
157     case SG_LIB_CAT_CLEAN:
158         ok = 1;
159         break;
160     case SG_LIB_CAT_RECOVERED:
161         ucp = sg_scsi_sense_desc_find(sense_buffer, sizeof(sense_buffer),
162                                       SAT_ATA_RETURN_DESC);
163         if (NULL == ucp) {
164             if (verbose > 1)
165                 printf("ATA Return Descriptor expected in sense but not "
166                        "found\n");
167             sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
168         } else if (verbose)
169             sg_chk_n_print3("ATA Return Descriptor", &io_hdr, 1);
170         if (ucp && ucp[3])
171             printf("error=0x%x, status=0x%x\n", ucp[3], ucp[13]);
172         else
173             ok = 1;
174         break;
175     default: /* won't bother decoding other categories */
176         sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
177         break;
178     }
179 
180     if (ok) { /* output result if it is available */
181         printf("Response:\n");
182         dWordHex((const unsigned short *)inBuff, 256, 0,
183                  sg_is_big_endian());
184     }
185 
186     close(sg_fd);
187     return 0;
188 }
189