xref: /aosp_15_r20/external/sg3_utils/src/sg_scan_linux.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1*44704f69SBart Van Assche /* A utility program originally written for the Linux OS SCSI subsystem.
2*44704f69SBart Van Assche  *  Copyright (C) 1999 - 2022 D. Gilbert
3*44704f69SBart Van Assche  *  This program is free software; you can redistribute it and/or modify
4*44704f69SBart Van Assche  *  it under the terms of the GNU General Public License as published by
5*44704f69SBart Van Assche  *  the Free Software Foundation; either version 2, or (at your option)
6*44704f69SBart Van Assche  *  any later version.
7*44704f69SBart Van Assche  *
8*44704f69SBart Van Assche  * SPDX-License-Identifier: GPL-2.0-or-later
9*44704f69SBart Van Assche  *
10*44704f69SBart Van Assche  * This program scans the "sg" device space (ie actual + simulated SCSI
11*44704f69SBart Van Assche  * generic devices). Optionally sg_scan can be given other device names
12*44704f69SBart Van Assche  * to scan (in place of the sg devices).
13*44704f69SBart Van Assche  * Options: -a   alpha scan: scan /dev/sga,b,c, ....
14*44704f69SBart Van Assche  *          -i   do SCSI inquiry on device (implies -w)
15*44704f69SBart Van Assche  *          -n   numeric scan: scan /dev/sg0,1,2, ....
16*44704f69SBart Van Assche  *          -V   output version string and exit
17*44704f69SBart Van Assche  *          -w   open writable (new driver opens readable unless -i)
18*44704f69SBart Van Assche  *          -x   extra information output
19*44704f69SBart Van Assche  *
20*44704f69SBart Van Assche  * By default this program will look for /dev/sg0 first (i.e. numeric scan)
21*44704f69SBart Van Assche  *
22*44704f69SBart Van Assche  * Note: This program is written to work under both the original and
23*44704f69SBart Van Assche  * the new sg driver.
24*44704f69SBart Van Assche  *
25*44704f69SBart Van Assche  * F. Jansen - modification to extend beyond 26 sg devices.
26*44704f69SBart Van Assche  */
27*44704f69SBart Van Assche 
28*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
29*44704f69SBart Van Assche #include "config.h"
30*44704f69SBart Van Assche #endif
31*44704f69SBart Van Assche 
32*44704f69SBart Van Assche 
33*44704f69SBart Van Assche #ifndef _GNU_SOURCE
34*44704f69SBart Van Assche #define _GNU_SOURCE 1
35*44704f69SBart Van Assche #endif
36*44704f69SBart Van Assche 
37*44704f69SBart Van Assche #include <unistd.h>
38*44704f69SBart Van Assche #include <fcntl.h>
39*44704f69SBart Van Assche #include <stdio.h>
40*44704f69SBart Van Assche #include <stdlib.h>
41*44704f69SBart Van Assche #include <string.h>
42*44704f69SBart Van Assche #include <ctype.h>
43*44704f69SBart Van Assche #include <errno.h>
44*44704f69SBart Van Assche #include <dirent.h>
45*44704f69SBart Van Assche #include <libgen.h>
46*44704f69SBart Van Assche #include <sys/ioctl.h>
47*44704f69SBart Van Assche #include <sys/types.h>
48*44704f69SBart Van Assche #include <sys/stat.h>
49*44704f69SBart Van Assche #include <scsi/scsi_ioctl.h>
50*44704f69SBart Van Assche 
51*44704f69SBart Van Assche #include "sg_lib.h"
52*44704f69SBart Van Assche #include "sg_io_linux.h"
53*44704f69SBart Van Assche #include "sg_pr2serr.h"
54*44704f69SBart Van Assche 
55*44704f69SBart Van Assche 
56*44704f69SBart Van Assche static const char * version_str = "4.18 20220118";
57*44704f69SBart Van Assche 
58*44704f69SBart Van Assche #define ME "sg_scan: "
59*44704f69SBart Van Assche 
60*44704f69SBart Van Assche #define NUMERIC_SCAN_DEF true /* change to false to make alpha scan default */
61*44704f69SBart Van Assche 
62*44704f69SBart Van Assche #define INQ_REPLY_LEN 36
63*44704f69SBart Van Assche #define INQ_CMD_LEN 6
64*44704f69SBart Van Assche #define MAX_ERRORS 4
65*44704f69SBart Van Assche 
66*44704f69SBart Van Assche #define EBUFF_SZ 256
67*44704f69SBart Van Assche #define FNAME_SZ 64
68*44704f69SBart Van Assche #define PRESENT_ARRAY_SIZE 8192
69*44704f69SBart Van Assche 
70*44704f69SBart Van Assche static const char * sysfs_sg_dir = "/sys/class/scsi_generic";
71*44704f69SBart Van Assche static int * gen_index_arr;
72*44704f69SBart Van Assche 
73*44704f69SBart Van Assche typedef struct my_scsi_idlun {
74*44704f69SBart Van Assche /* why can't userland see this structure ??? */
75*44704f69SBart Van Assche     int dev_id;
76*44704f69SBart Van Assche     int host_unique_id;
77*44704f69SBart Van Assche } My_scsi_idlun;
78*44704f69SBart Van Assche 
79*44704f69SBart Van Assche typedef struct my_sg_scsi_id {
80*44704f69SBart Van Assche     int host_no;        /* as in "scsi<n>" where 'n' is one of 0, 1, 2 etc */
81*44704f69SBart Van Assche     int channel;
82*44704f69SBart Van Assche     int scsi_id;        /* scsi id of target device */
83*44704f69SBart Van Assche     int lun;
84*44704f69SBart Van Assche     int scsi_type;      /* TYPE_... defined in scsi/scsi.h */
85*44704f69SBart Van Assche     short h_cmd_per_lun;/* host (adapter) maximum commands per lun */
86*44704f69SBart Van Assche     short d_queue_depth;/* device (or adapter) maximum queue length */
87*44704f69SBart Van Assche     int unused1;        /* probably find a good use, set 0 for now */
88*44704f69SBart Van Assche     int unused2;        /* ditto */
89*44704f69SBart Van Assche } My_sg_scsi_id;
90*44704f69SBart Van Assche 
91*44704f69SBart Van Assche int sg3_inq(int sg_fd, uint8_t * inqBuff, bool do_extra);
92*44704f69SBart Van Assche int scsi_inq(int sg_fd, uint8_t * inqBuff);
93*44704f69SBart Van Assche int try_ata_identity(const char * file_namep, int ata_fd, bool do_inq);
94*44704f69SBart Van Assche 
95*44704f69SBart Van Assche static uint8_t inq_cdb[INQ_CMD_LEN] =
96*44704f69SBart Van Assche                                 {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};
97*44704f69SBart Van Assche 
98*44704f69SBart Van Assche 
usage()99*44704f69SBart Van Assche void usage()
100*44704f69SBart Van Assche {
101*44704f69SBart Van Assche     printf("Usage: sg_scan [-a] [-i] [-n] [-v] [-V] [-w] [-x] "
102*44704f69SBart Van Assche            "[DEVICE]*\n");
103*44704f69SBart Van Assche     printf("  where:\n");
104*44704f69SBart Van Assche     printf("    -a    do alpha scan (ie sga, sgb, sgc)\n");
105*44704f69SBart Van Assche     printf("    -i    do SCSI INQUIRY, output results\n");
106*44704f69SBart Van Assche     printf("    -n    do numeric scan (ie sg0, sg1...) [default]\n");
107*44704f69SBart Van Assche     printf("    -v    increase verbosity\n");
108*44704f69SBart Van Assche     printf("    -V    output version string then exit\n");
109*44704f69SBart Van Assche     printf("    -w    force open with read/write flag\n");
110*44704f69SBart Van Assche     printf("    -x    extra information output about queuing\n");
111*44704f69SBart Van Assche     printf("   DEVICE    name of device\n");
112*44704f69SBart Van Assche }
113*44704f69SBart Van Assche 
scandir_select(const struct dirent * s)114*44704f69SBart Van Assche static int scandir_select(const struct dirent * s)
115*44704f69SBart Van Assche {
116*44704f69SBart Van Assche     int k;
117*44704f69SBart Van Assche 
118*44704f69SBart Van Assche     if (1 == sscanf(s->d_name, "sg%d", &k)) {
119*44704f69SBart Van Assche         if ((k >= 0) && (k < PRESENT_ARRAY_SIZE)) {
120*44704f69SBart Van Assche             gen_index_arr[k] = 1;
121*44704f69SBart Van Assche             return 1;
122*44704f69SBart Van Assche         }
123*44704f69SBart Van Assche     }
124*44704f69SBart Van Assche     return 0;
125*44704f69SBart Van Assche }
126*44704f69SBart Van Assche 
sysfs_sg_scan(const char * dir_name)127*44704f69SBart Van Assche static int sysfs_sg_scan(const char * dir_name)
128*44704f69SBart Van Assche {
129*44704f69SBart Van Assche     struct dirent ** namelist;
130*44704f69SBart Van Assche     int num, k;
131*44704f69SBart Van Assche 
132*44704f69SBart Van Assche     num = scandir(dir_name, &namelist, scandir_select, NULL);
133*44704f69SBart Van Assche     if (num < 0)
134*44704f69SBart Van Assche         return -errno;
135*44704f69SBart Van Assche     for (k = 0; k < num; ++k)
136*44704f69SBart Van Assche         free(namelist[k]);
137*44704f69SBart Van Assche     free(namelist);
138*44704f69SBart Van Assche     return num;
139*44704f69SBart Van Assche }
140*44704f69SBart Van Assche 
make_dev_name(char * fname,int k,bool do_numeric)141*44704f69SBart Van Assche void make_dev_name(char * fname, int k, bool do_numeric)
142*44704f69SBart Van Assche {
143*44704f69SBart Van Assche     char buff[FNAME_SZ];
144*44704f69SBart Van Assche     int  big,little;
145*44704f69SBart Van Assche 
146*44704f69SBart Van Assche     strcpy(fname, "/dev/sg");
147*44704f69SBart Van Assche     if (do_numeric) {
148*44704f69SBart Van Assche         snprintf(buff, sizeof(buff), "%d", k);
149*44704f69SBart Van Assche         strcat(fname, buff);
150*44704f69SBart Van Assche     }
151*44704f69SBart Van Assche     else {
152*44704f69SBart Van Assche         if (k < 26) {
153*44704f69SBart Van Assche             buff[0] = 'a' + (char)k;
154*44704f69SBart Van Assche             buff[1] = '\0';
155*44704f69SBart Van Assche             strcat(fname, buff);
156*44704f69SBart Van Assche         }
157*44704f69SBart Van Assche         else if (k <= 255) { /* assumes sequence goes x,y,z,aa,ab,ac etc */
158*44704f69SBart Van Assche             big    = k/26;
159*44704f69SBart Van Assche             little = k - (26 * big);
160*44704f69SBart Van Assche             big    = big - 1;
161*44704f69SBart Van Assche 
162*44704f69SBart Van Assche             buff[0] = 'a' + (char)big;
163*44704f69SBart Van Assche             buff[1] = 'a' + (char)little;
164*44704f69SBart Van Assche             buff[2] = '\0';
165*44704f69SBart Van Assche             strcat(fname, buff);
166*44704f69SBart Van Assche         }
167*44704f69SBart Van Assche         else
168*44704f69SBart Van Assche             strcat(fname, "xxxx");
169*44704f69SBart Van Assche     }
170*44704f69SBart Van Assche }
171*44704f69SBart Van Assche 
172*44704f69SBart Van Assche 
main(int argc,char * argv[])173*44704f69SBart Van Assche int main(int argc, char * argv[])
174*44704f69SBart Van Assche {
175*44704f69SBart Van Assche     bool do_extra = false;
176*44704f69SBart Van Assche     bool do_inquiry = false;
177*44704f69SBart Van Assche     bool do_numeric = NUMERIC_SCAN_DEF;
178*44704f69SBart Van Assche     bool eacces_err = false;
179*44704f69SBart Van Assche     bool has_file_args = false;
180*44704f69SBart Van Assche     bool has_sysfs_sg = false;
181*44704f69SBart Van Assche     bool jmp_out;
182*44704f69SBart Van Assche     bool sg_ver3 = false;
183*44704f69SBart Van Assche     bool sg_ver3_set = false;
184*44704f69SBart Van Assche     bool writeable = false;
185*44704f69SBart Van Assche     int sg_fd, res, k, j, f, plen;
186*44704f69SBart Van Assche     int emul = -1;
187*44704f69SBart Van Assche     int flags;
188*44704f69SBart Van Assche     int host_no;
189*44704f69SBart Van Assche     const int max_file_args = PRESENT_ARRAY_SIZE;
190*44704f69SBart Van Assche     int num_errors = 0;
191*44704f69SBart Van Assche     int num_silent = 0;
192*44704f69SBart Van Assche     int verbose = 0;
193*44704f69SBart Van Assche     char * file_namep;
194*44704f69SBart Van Assche     const char * cp;
195*44704f69SBart Van Assche     char fname[FNAME_SZ];
196*44704f69SBart Van Assche     char ebuff[EBUFF_SZ];
197*44704f69SBart Van Assche     uint8_t inqBuff[INQ_REPLY_LEN];
198*44704f69SBart Van Assche     My_scsi_idlun my_idlun;
199*44704f69SBart Van Assche     struct stat a_stat;
200*44704f69SBart Van Assche 
201*44704f69SBart Van Assche     if (NULL == (gen_index_arr =
202*44704f69SBart Van Assche                  (int *)calloc(max_file_args + 1, sizeof(int)))) {
203*44704f69SBart Van Assche         printf(ME "Out of memory\n");
204*44704f69SBart Van Assche         return SG_LIB_CAT_OTHER;
205*44704f69SBart Van Assche     }
206*44704f69SBart Van Assche     strcpy(fname, "<null>");
207*44704f69SBart Van Assche 
208*44704f69SBart Van Assche     for (k = 1, j = 0; k < argc; ++k) {
209*44704f69SBart Van Assche         cp = argv[k];
210*44704f69SBart Van Assche         plen = strlen(cp);
211*44704f69SBart Van Assche         if (plen <= 0)
212*44704f69SBart Van Assche             continue;
213*44704f69SBart Van Assche         if ('-' == *cp) {
214*44704f69SBart Van Assche             for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) {
215*44704f69SBart Van Assche                 switch (*cp) {
216*44704f69SBart Van Assche                 case 'a':
217*44704f69SBart Van Assche                     do_numeric = false;
218*44704f69SBart Van Assche                     break;
219*44704f69SBart Van Assche                 case 'h':
220*44704f69SBart Van Assche                 case '?':
221*44704f69SBart Van Assche                     printf("Scan sg device names and optionally do an "
222*44704f69SBart Van Assche                            "INQUIRY\n\n");
223*44704f69SBart Van Assche                     usage();
224*44704f69SBart Van Assche                     return 0;
225*44704f69SBart Van Assche                 case 'i':
226*44704f69SBart Van Assche                     do_inquiry = true;
227*44704f69SBart Van Assche                     break;
228*44704f69SBart Van Assche                 case 'n':
229*44704f69SBart Van Assche                     do_numeric = true;
230*44704f69SBart Van Assche                     break;
231*44704f69SBart Van Assche                 case 'v':
232*44704f69SBart Van Assche                     ++verbose;
233*44704f69SBart Van Assche                     break;
234*44704f69SBart Van Assche                 case 'V':
235*44704f69SBart Van Assche                     pr2serr("Version string: %s\n", version_str);
236*44704f69SBart Van Assche                     exit(0);
237*44704f69SBart Van Assche                 case 'w':
238*44704f69SBart Van Assche                     writeable = true;
239*44704f69SBart Van Assche                     break;
240*44704f69SBart Van Assche                 case 'x':
241*44704f69SBart Van Assche                     do_extra = true;
242*44704f69SBart Van Assche                     break;
243*44704f69SBart Van Assche                 default:
244*44704f69SBart Van Assche                     jmp_out = true;
245*44704f69SBart Van Assche                     break;
246*44704f69SBart Van Assche                 }
247*44704f69SBart Van Assche                 if (jmp_out)
248*44704f69SBart Van Assche                     break;
249*44704f69SBart Van Assche             }
250*44704f69SBart Van Assche             if (plen <= 0)
251*44704f69SBart Van Assche                 continue;
252*44704f69SBart Van Assche             if (jmp_out) {
253*44704f69SBart Van Assche                 pr2serr("Unrecognized option: %s\n", cp);
254*44704f69SBart Van Assche                 usage();
255*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
256*44704f69SBart Van Assche             }
257*44704f69SBart Van Assche         } else {
258*44704f69SBart Van Assche             if (j < max_file_args) {
259*44704f69SBart Van Assche                 has_file_args = true;
260*44704f69SBart Van Assche                 gen_index_arr[j++] = k;
261*44704f69SBart Van Assche             } else {
262*44704f69SBart Van Assche                 printf("Too many command line arguments\n");
263*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
264*44704f69SBart Van Assche             }
265*44704f69SBart Van Assche         }
266*44704f69SBart Van Assche     }
267*44704f69SBart Van Assche 
268*44704f69SBart Van Assche     if ((! has_file_args) && (stat(sysfs_sg_dir, &a_stat) >= 0) &&
269*44704f69SBart Van Assche         (S_ISDIR(a_stat.st_mode)))
270*44704f69SBart Van Assche         has_sysfs_sg = !! sysfs_sg_scan(sysfs_sg_dir);
271*44704f69SBart Van Assche 
272*44704f69SBart Van Assche     flags = O_NONBLOCK | (writeable ? O_RDWR : O_RDONLY);
273*44704f69SBart Van Assche 
274*44704f69SBart Van Assche     for (k = 0, res = 0, j = 0, sg_fd = -1;
275*44704f69SBart Van Assche          (k < max_file_args)  && (has_file_args || (num_errors < MAX_ERRORS));
276*44704f69SBart Van Assche          ++k, res = ((sg_fd >= 0) ? close(sg_fd) : 0)) {
277*44704f69SBart Van Assche         if (res < 0) {
278*44704f69SBart Van Assche             snprintf(ebuff, EBUFF_SZ, ME "Error closing %s ", fname);
279*44704f69SBart Van Assche             perror(ebuff);
280*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
281*44704f69SBart Van Assche         }
282*44704f69SBart Van Assche         if (has_file_args) {
283*44704f69SBart Van Assche             if (gen_index_arr[j])
284*44704f69SBart Van Assche                 file_namep = argv[gen_index_arr[j++]];
285*44704f69SBart Van Assche             else
286*44704f69SBart Van Assche                 break;
287*44704f69SBart Van Assche         } else if (has_sysfs_sg) {
288*44704f69SBart Van Assche             if (0 == gen_index_arr[k]) {
289*44704f69SBart Van Assche                 sg_fd = -1;
290*44704f69SBart Van Assche                 continue;
291*44704f69SBart Van Assche             }
292*44704f69SBart Van Assche             make_dev_name(fname, k, 1);
293*44704f69SBart Van Assche             file_namep = fname;
294*44704f69SBart Van Assche         } else {
295*44704f69SBart Van Assche             make_dev_name(fname, k, do_numeric);
296*44704f69SBart Van Assche             file_namep = fname;
297*44704f69SBart Van Assche         }
298*44704f69SBart Van Assche 
299*44704f69SBart Van Assche         sg_fd = open(file_namep, flags);
300*44704f69SBart Van Assche         if (sg_fd < 0) {
301*44704f69SBart Van Assche             if (EBUSY == errno) {
302*44704f69SBart Van Assche                 printf("%s: device busy (O_EXCL lock), skipping\n",
303*44704f69SBart Van Assche                        file_namep);
304*44704f69SBart Van Assche                 continue;
305*44704f69SBart Van Assche             }
306*44704f69SBart Van Assche             else if ((ENODEV == errno) || (ENOENT == errno) ||
307*44704f69SBart Van Assche                      (ENXIO == errno)) {
308*44704f69SBart Van Assche                 if (verbose)
309*44704f69SBart Van Assche                     pr2serr("Unable to open: %s, errno=%d\n", file_namep,
310*44704f69SBart Van Assche                             errno);
311*44704f69SBart Van Assche                 ++num_errors;
312*44704f69SBart Van Assche                 ++num_silent;
313*44704f69SBart Van Assche                 continue;
314*44704f69SBart Van Assche             }
315*44704f69SBart Van Assche             else {
316*44704f69SBart Van Assche                 if (EACCES == errno)
317*44704f69SBart Van Assche                     eacces_err = true;
318*44704f69SBart Van Assche                 snprintf(ebuff, EBUFF_SZ, ME "Error opening %s ", file_namep);
319*44704f69SBart Van Assche                 perror(ebuff);
320*44704f69SBart Van Assche                 ++num_errors;
321*44704f69SBart Van Assche                 continue;
322*44704f69SBart Van Assche             }
323*44704f69SBart Van Assche         }
324*44704f69SBart Van Assche         res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
325*44704f69SBart Van Assche         if (res < 0) {
326*44704f69SBart Van Assche             res = try_ata_identity(file_namep, sg_fd, do_inquiry);
327*44704f69SBart Van Assche             if (res == 0)
328*44704f69SBart Van Assche                 continue;
329*44704f69SBart Van Assche             snprintf(ebuff, EBUFF_SZ, ME "device %s failed on scsi+ata "
330*44704f69SBart Van Assche                      "ioctl, skip", file_namep);
331*44704f69SBart Van Assche             perror(ebuff);
332*44704f69SBart Van Assche             ++num_errors;
333*44704f69SBart Van Assche             continue;
334*44704f69SBart Van Assche         }
335*44704f69SBart Van Assche         res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
336*44704f69SBart Van Assche         if (res < 0) {
337*44704f69SBart Van Assche             snprintf(ebuff, EBUFF_SZ, ME "device %s failed on scsi "
338*44704f69SBart Van Assche                      "ioctl(2), skip", file_namep);
339*44704f69SBart Van Assche             perror(ebuff);
340*44704f69SBart Van Assche             ++num_errors;
341*44704f69SBart Van Assche             continue;
342*44704f69SBart Van Assche         }
343*44704f69SBart Van Assche         res = ioctl(sg_fd, SG_EMULATED_HOST, &emul);
344*44704f69SBart Van Assche         if (res < 0)
345*44704f69SBart Van Assche             emul = -1;
346*44704f69SBart Van Assche         printf("%s: scsi%d channel=%d id=%d lun=%d", file_namep, host_no,
347*44704f69SBart Van Assche                (my_idlun.dev_id >> 16) & 0xff, my_idlun.dev_id & 0xff,
348*44704f69SBart Van Assche                (my_idlun.dev_id >> 8) & 0xff);
349*44704f69SBart Van Assche         if (1 == emul)
350*44704f69SBart Van Assche             printf(" [em]");
351*44704f69SBart Van Assche #if 0
352*44704f69SBart Van Assche         printf(", huid=%d", my_idlun.host_unique_id);
353*44704f69SBart Van Assche #endif
354*44704f69SBart Van Assche         if (! has_file_args) {
355*44704f69SBart Van Assche             My_sg_scsi_id m_id; /* compatible with sg_scsi_id_t in sg.h */
356*44704f69SBart Van Assche 
357*44704f69SBart Van Assche             res = ioctl(sg_fd, SG_GET_SCSI_ID, &m_id);
358*44704f69SBart Van Assche             if (res < 0) {
359*44704f69SBart Van Assche                 snprintf(ebuff, EBUFF_SZ, ME "device %s failed "
360*44704f69SBart Van Assche                          "SG_GET_SCSI_ID ioctl(4), skip", file_namep);
361*44704f69SBart Van Assche                 perror(ebuff);
362*44704f69SBart Van Assche                 ++num_errors;
363*44704f69SBart Van Assche                 continue;
364*44704f69SBart Van Assche             }
365*44704f69SBart Van Assche             /* printf("  type=%d", m_id.scsi_type); */
366*44704f69SBart Van Assche             if (do_extra)
367*44704f69SBart Van Assche                 printf("  cmd_per_lun=%hd queue_depth=%hd\n",
368*44704f69SBart Van Assche                        m_id.h_cmd_per_lun, m_id.d_queue_depth);
369*44704f69SBart Van Assche             else
370*44704f69SBart Van Assche                 printf("\n");
371*44704f69SBart Van Assche         }
372*44704f69SBart Van Assche         else
373*44704f69SBart Van Assche             printf("\n");
374*44704f69SBart Van Assche         if (do_inquiry) {
375*44704f69SBart Van Assche             if (! sg_ver3_set) {
376*44704f69SBart Van Assche                 sg_ver3 = false;
377*44704f69SBart Van Assche                 sg_ver3_set = true;
378*44704f69SBart Van Assche                 if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &f) >= 0) &&
379*44704f69SBart Van Assche                     (f >= 30000))
380*44704f69SBart Van Assche                     sg_ver3 = true;
381*44704f69SBart Van Assche             }
382*44704f69SBart Van Assche             if (sg_ver3) {
383*44704f69SBart Van Assche                 res = sg3_inq(sg_fd, inqBuff, do_extra);
384*44704f69SBart Van Assche                 if (res)
385*44704f69SBart Van Assche                     ++num_errors;
386*44704f69SBart Van Assche             }
387*44704f69SBart Van Assche         }
388*44704f69SBart Van Assche     }
389*44704f69SBart Van Assche     if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors) &&
390*44704f69SBart Van Assche         (! has_file_args)) {
391*44704f69SBart Van Assche         printf("Stopping because there are too many error\n");
392*44704f69SBart Van Assche         if (eacces_err)
393*44704f69SBart Van Assche             printf("    root access may be required\n");
394*44704f69SBart Van Assche     }
395*44704f69SBart Van Assche     return 0;
396*44704f69SBart Van Assche }
397*44704f69SBart Van Assche 
sg3_inq(int sg_fd,uint8_t * inqBuff,bool do_extra)398*44704f69SBart Van Assche int sg3_inq(int sg_fd, uint8_t * inqBuff, bool do_extra)
399*44704f69SBart Van Assche {
400*44704f69SBart Van Assche     bool ok;
401*44704f69SBart Van Assche     int err, sg_io;
402*44704f69SBart Van Assche     uint8_t sense_buffer[32] SG_C_CPP_ZERO_INIT;
403*44704f69SBart Van Assche     struct sg_io_hdr io_hdr SG_C_CPP_ZERO_INIT;
404*44704f69SBart Van Assche 
405*44704f69SBart Van Assche     memset(inqBuff, 0, INQ_REPLY_LEN);
406*44704f69SBart Van Assche     inqBuff[0] = 0x7f;
407*44704f69SBart Van Assche     io_hdr.interface_id = 'S';
408*44704f69SBart Van Assche     io_hdr.cmd_len = sizeof(inq_cdb);
409*44704f69SBart Van Assche     io_hdr.mx_sb_len = sizeof(sense_buffer);
410*44704f69SBart Van Assche     io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
411*44704f69SBart Van Assche     io_hdr.dxfer_len = INQ_REPLY_LEN;
412*44704f69SBart Van Assche     io_hdr.dxferp = inqBuff;
413*44704f69SBart Van Assche     io_hdr.cmdp = inq_cdb;
414*44704f69SBart Van Assche     io_hdr.sbp = sense_buffer;
415*44704f69SBart Van Assche     io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
416*44704f69SBart Van Assche 
417*44704f69SBart Van Assche     ok = true;
418*44704f69SBart Van Assche     sg_io = 0;
419*44704f69SBart Van Assche     if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
420*44704f69SBart Van Assche         if ((err = scsi_inq(sg_fd, inqBuff)) < 0) {
421*44704f69SBart Van Assche             perror(ME "Inquiry SG_IO + SCSI_IOCTL_SEND_COMMAND ioctl error");
422*44704f69SBart Van Assche             return 1;
423*44704f69SBart Van Assche         } else if (err) {
424*44704f69SBart Van Assche             printf(ME "SCSI_IOCTL_SEND_COMMAND ioctl error=0x%x\n", err);
425*44704f69SBart Van Assche             return 1;
426*44704f69SBart Van Assche         }
427*44704f69SBart Van Assche     } else {
428*44704f69SBart Van Assche         sg_io = 1;
429*44704f69SBart Van Assche         /* now for the error processing */
430*44704f69SBart Van Assche         switch (sg_err_category3(&io_hdr)) {
431*44704f69SBart Van Assche         case SG_LIB_CAT_RECOVERED:
432*44704f69SBart Van Assche             sg_chk_n_print3("Inquiry, continuing", &io_hdr, true);
433*44704f69SBart Van Assche #if defined(__GNUC__)
434*44704f69SBart Van Assche #if (__GNUC__ >= 7)
435*44704f69SBart Van Assche             __attribute__((fallthrough));
436*44704f69SBart Van Assche             /* FALL THROUGH */
437*44704f69SBart Van Assche #endif
438*44704f69SBart Van Assche #endif
439*44704f69SBart Van Assche         case SG_LIB_CAT_CLEAN:
440*44704f69SBart Van Assche             break;
441*44704f69SBart Van Assche         default: /* won't bother decoding other categories */
442*44704f69SBart Van Assche             ok = false;
443*44704f69SBart Van Assche             sg_chk_n_print3("INQUIRY command error", &io_hdr, true);
444*44704f69SBart Van Assche             break;
445*44704f69SBart Van Assche         }
446*44704f69SBart Van Assche     }
447*44704f69SBart Van Assche 
448*44704f69SBart Van Assche     if (ok) { /* output result if it is available */
449*44704f69SBart Van Assche         char * p = (char *)inqBuff;
450*44704f69SBart Van Assche 
451*44704f69SBart Van Assche         printf("    %.8s  %.16s  %.4s ", p + 8, p + 16, p + 32);
452*44704f69SBart Van Assche         printf("[rmb=%d cmdq=%d pqual=%d pdev=0x%x] ",
453*44704f69SBart Van Assche                !!(p[1] & 0x80), !!(p[7] & 2), (p[0] & 0xe0) >> 5,
454*44704f69SBart Van Assche                (p[0] & PDT_MASK));
455*44704f69SBart Van Assche         if (do_extra && sg_io)
456*44704f69SBart Van Assche             printf("dur=%ums\n", io_hdr.duration);
457*44704f69SBart Van Assche         else
458*44704f69SBart Van Assche             printf("\n");
459*44704f69SBart Van Assche     }
460*44704f69SBart Van Assche     return 0;
461*44704f69SBart Van Assche }
462*44704f69SBart Van Assche 
463*44704f69SBart Van Assche struct lscsi_ioctl_command {
464*44704f69SBart Van Assche         unsigned int inlen;  /* _excluding_ scsi command length */
465*44704f69SBart Van Assche         unsigned int outlen;
466*44704f69SBart Van Assche         uint8_t data[1];  /* was 0 but that's not ISO C!! */
467*44704f69SBart Van Assche                 /* on input, scsi command starts here then opt. data */
468*44704f69SBart Van Assche };
469*44704f69SBart Van Assche 
470*44704f69SBart Van Assche /* fallback INQUIRY using scsi mid-level's SCSI_IOCTL_SEND_COMMAND ioctl */
scsi_inq(int sg_fd,uint8_t * inqBuff)471*44704f69SBart Van Assche int scsi_inq(int sg_fd, uint8_t * inqBuff)
472*44704f69SBart Van Assche {
473*44704f69SBart Van Assche     int res;
474*44704f69SBart Van Assche     uint8_t buff[1024];
475*44704f69SBart Van Assche     struct lscsi_ioctl_command * sicp = (struct lscsi_ioctl_command *)buff;
476*44704f69SBart Van Assche 
477*44704f69SBart Van Assche     memset(buff, 0, sizeof(buff));
478*44704f69SBart Van Assche     sicp->inlen = 0;
479*44704f69SBart Van Assche     sicp->outlen = INQ_REPLY_LEN;
480*44704f69SBart Van Assche     memcpy(sicp->data, inq_cdb, INQ_CMD_LEN);
481*44704f69SBart Van Assche     res = ioctl(sg_fd, SCSI_IOCTL_SEND_COMMAND, sicp);
482*44704f69SBart Van Assche     if (0 == res)
483*44704f69SBart Van Assche         memcpy(inqBuff, sicp->data, INQ_REPLY_LEN);
484*44704f69SBart Van Assche     return res;
485*44704f69SBart Van Assche }
486*44704f69SBart Van Assche 
487*44704f69SBart Van Assche /* Following code permits ATA IDENTIFY commands to be performed on
488*44704f69SBart Van Assche    ATA non "Packet Interface" devices (e.g. ATA disks).
489*44704f69SBart Van Assche    GPL-ed code borrowed from smartmontools (smartmontools.sf.net).
490*44704f69SBart Van Assche    Copyright (C) 2002-4 Bruce Allen
491*44704f69SBart Van Assche                 <[email protected]>
492*44704f69SBart Van Assche  */
493*44704f69SBart Van Assche #ifndef ATA_IDENTIFY_DEVICE
494*44704f69SBart Van Assche #define ATA_IDENTIFY_DEVICE 0xec
495*44704f69SBart Van Assche #endif
496*44704f69SBart Van Assche #ifndef HDIO_DRIVE_CMD
497*44704f69SBart Van Assche #define HDIO_DRIVE_CMD    0x031f
498*44704f69SBart Van Assche #endif
499*44704f69SBart Van Assche 
500*44704f69SBart Van Assche /* Needed parts of the ATA DRIVE IDENTIFY Structure. Those labeled
501*44704f69SBart Van Assche  * word* are NOT used.
502*44704f69SBart Van Assche  */
503*44704f69SBart Van Assche struct ata_identify_device {
504*44704f69SBart Van Assche   unsigned short words000_009[10];
505*44704f69SBart Van Assche   uint8_t  serial_no[20];
506*44704f69SBart Van Assche   unsigned short words020_022[3];
507*44704f69SBart Van Assche   uint8_t  fw_rev[8];
508*44704f69SBart Van Assche   uint8_t  model[40];
509*44704f69SBart Van Assche   unsigned short words047_079[33];
510*44704f69SBart Van Assche   unsigned short major_rev_num;
511*44704f69SBart Van Assche   unsigned short minor_rev_num;
512*44704f69SBart Van Assche   unsigned short command_set_1;
513*44704f69SBart Van Assche   unsigned short command_set_2;
514*44704f69SBart Van Assche   unsigned short command_set_extension;
515*44704f69SBart Van Assche   unsigned short cfs_enable_1;
516*44704f69SBart Van Assche   unsigned short word086;
517*44704f69SBart Van Assche   unsigned short csf_default;
518*44704f69SBart Van Assche   unsigned short words088_255[168];
519*44704f69SBart Van Assche };
520*44704f69SBart Van Assche 
521*44704f69SBart Van Assche /* Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents
522*44704f69SBart Van Assche  * bytes.
523*44704f69SBart Van Assche  */
swapbytes(char * out,const char * in,size_t n)524*44704f69SBart Van Assche void swapbytes(char *out, const char *in, size_t n)
525*44704f69SBart Van Assche {
526*44704f69SBart Van Assche     size_t k;
527*44704f69SBart Van Assche 
528*44704f69SBart Van Assche     if (n > 1) {
529*44704f69SBart Van Assche         for (k = 0; k < (n - 1); k += 2) {
530*44704f69SBart Van Assche             out[k] = in[k + 1];
531*44704f69SBart Van Assche             out[k + 1] = in[k];
532*44704f69SBart Van Assche         }
533*44704f69SBart Van Assche     }
534*44704f69SBart Van Assche }
535*44704f69SBart Van Assche 
536*44704f69SBart Van Assche /* Copies in to out, but removes leading and trailing whitespace. */
trim(char * out,const char * in)537*44704f69SBart Van Assche void trim(char *out, const char *in)
538*44704f69SBart Van Assche {
539*44704f69SBart Van Assche     int k, first, last, num;
540*44704f69SBart Van Assche 
541*44704f69SBart Van Assche     /* Find the first non-space character (maybe none). */
542*44704f69SBart Van Assche     first = -1;
543*44704f69SBart Van Assche     for (k = 0; in[k]; k++) {
544*44704f69SBart Van Assche         if (! isspace((int)in[k])) {
545*44704f69SBart Van Assche             first = k;
546*44704f69SBart Van Assche             break;
547*44704f69SBart Van Assche         }
548*44704f69SBart Van Assche     }
549*44704f69SBart Van Assche 
550*44704f69SBart Van Assche     if (first == -1) {
551*44704f69SBart Van Assche         /* There are no non-space characters. */
552*44704f69SBart Van Assche         out[0] = '\0';
553*44704f69SBart Van Assche         return;
554*44704f69SBart Van Assche     }
555*44704f69SBart Van Assche 
556*44704f69SBart Van Assche     /* Find the last non-space character. */
557*44704f69SBart Van Assche     for (k = strlen(in) - 1; k >= first && isspace((int)in[k]); k--)
558*44704f69SBart Van Assche         ;
559*44704f69SBart Van Assche     last = k;
560*44704f69SBart Van Assche     num = last - first + 1;
561*44704f69SBart Van Assche     for (k = 0; k < num; ++k)
562*44704f69SBart Van Assche         out[k] = in[first + k];
563*44704f69SBart Van Assche     out[num] = '\0';
564*44704f69SBart Van Assche }
565*44704f69SBart Van Assche 
566*44704f69SBart Van Assche /* Convenience function for formatting strings from ata_identify_device */
formatdriveidstring(char * out,const char * in,int n)567*44704f69SBart Van Assche void formatdriveidstring(char *out, const char *in, int n)
568*44704f69SBart Van Assche {
569*44704f69SBart Van Assche     char tmp[65];
570*44704f69SBart Van Assche 
571*44704f69SBart Van Assche     n = n > 64 ? 64 : n;
572*44704f69SBart Van Assche     swapbytes(tmp, in, n);
573*44704f69SBart Van Assche     tmp[n] = '\0';
574*44704f69SBart Van Assche     trim(out, tmp);
575*44704f69SBart Van Assche }
576*44704f69SBart Van Assche 
577*44704f69SBart Van Assche /* Function for printing ASCII byte-swapped strings, skipping white
578*44704f69SBart Van Assche  * space. Please note that this is needed on both big- and
579*44704f69SBart Van Assche  * little-endian hardware.
580*44704f69SBart Van Assche  */
printswap(char * output,char * in,unsigned int n)581*44704f69SBart Van Assche void printswap(char *output, char *in, unsigned int n)
582*44704f69SBart Van Assche {
583*44704f69SBart Van Assche     formatdriveidstring(output, in, n);
584*44704f69SBart Van Assche     if (*output)
585*44704f69SBart Van Assche         printf("%.*s   ", (int)n, output);
586*44704f69SBart Van Assche     else
587*44704f69SBart Van Assche         printf("%.*s   ", (int)n, "[No Information Found]\n");
588*44704f69SBart Van Assche }
589*44704f69SBart Van Assche 
590*44704f69SBart Van Assche #define ATA_IDENTIFY_BUFF_SZ  sizeof(struct ata_identify_device)
591*44704f69SBart Van Assche #define HDIO_DRIVE_CMD_OFFSET 4
592*44704f69SBart Van Assche 
ata_command_interface(int device,char * data)593*44704f69SBart Van Assche int ata_command_interface(int device, char *data)
594*44704f69SBart Van Assche {
595*44704f69SBart Van Assche     uint8_t buff[ATA_IDENTIFY_BUFF_SZ + HDIO_DRIVE_CMD_OFFSET];
596*44704f69SBart Van Assche     int retval;
597*44704f69SBart Van Assche 
598*44704f69SBart Van Assche     buff[0] = ATA_IDENTIFY_DEVICE;
599*44704f69SBart Van Assche     buff[3] = 1;
600*44704f69SBart Van Assche     /* We are now doing the HDIO_DRIVE_CMD type ioctl. */
601*44704f69SBart Van Assche     if ((retval = ioctl(device, HDIO_DRIVE_CMD, buff)))
602*44704f69SBart Van Assche         return retval;
603*44704f69SBart Van Assche 
604*44704f69SBart Van Assche     /* if the command returns data, copy it back */
605*44704f69SBart Van Assche     memcpy(data, buff + HDIO_DRIVE_CMD_OFFSET, ATA_IDENTIFY_BUFF_SZ);
606*44704f69SBart Van Assche     return 0;
607*44704f69SBart Van Assche }
608*44704f69SBart Van Assche 
try_ata_identity(const char * file_namep,int ata_fd,bool do_inq)609*44704f69SBart Van Assche int try_ata_identity(const char * file_namep, int ata_fd, bool do_inq)
610*44704f69SBart Van Assche {
611*44704f69SBart Van Assche     struct ata_identify_device ata_ident;
612*44704f69SBart Van Assche     char model[64];
613*44704f69SBart Van Assche     char serial[64];
614*44704f69SBart Van Assche     char firm[64];
615*44704f69SBart Van Assche     int res;
616*44704f69SBart Van Assche 
617*44704f69SBart Van Assche     res = ata_command_interface(ata_fd, (char *)&ata_ident);
618*44704f69SBart Van Assche     if (res)
619*44704f69SBart Van Assche         return res;
620*44704f69SBart Van Assche     printf("%s: ATA device\n", file_namep);
621*44704f69SBart Van Assche     if (do_inq) {
622*44704f69SBart Van Assche         printf("    ");
623*44704f69SBart Van Assche         printswap(model, (char *)ata_ident.model, 40);
624*44704f69SBart Van Assche         printswap(serial, (char *)ata_ident.serial_no, 20);
625*44704f69SBart Van Assche         printswap(firm, (char *)ata_ident.fw_rev, 8);
626*44704f69SBart Van Assche         printf("\n");
627*44704f69SBart Van Assche     }
628*44704f69SBart Van Assche     return res;
629*44704f69SBart Van Assche }
630