aboutsummaryrefslogtreecommitdiff
path: root/sg_scan.c
blob: ce97e14d4dc3a03c799223f7da1ce2d7e514ec7b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "sg_include.h"
#include "sg_err.h"

/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
   device driver.
*  Copyright (C) 1999 - 2002 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 scans the "sg" device space (ie actual + simulated SCSI
   generic devices).
   Options: -w   open writable (new driver opens readable unless -i)
            -n   numeric scan: scan /dev/sg0,1,2, ....
            -a   alpha scan: scan /dev/sga,b,c, ....
            -i   do SCSI inquiry on device (implies -w)
            -x   extra information output

   By default this program will look for /dev/sg0 first (i.e. numeric scan)

   Note: This program is written to work under both the original and
   the new sg driver.

   Version 3.93 20020226

   F. Jansen - modification to extend beyond 26 sg devices.

6 byte INQUIRY command:
[0x12][   |lu][pg cde][res   ][al len][cntrl ]
*/

#define ME "sg_scan: "

#define NUMERIC_SCAN_DEF 1   /* change to 0 to make alpha scan default */

#define OFF sizeof(struct sg_header)
#define INQ_REPLY_LEN 96        /* logic assumes >= sizeof(inqCmdBlk) */
#define INQ_CMD_LEN 6
#define MAX_ERRORS 4


#ifdef SG_GET_RESERVED_SIZE
#define OPEN_FLAG O_RDONLY
#else
#define OPEN_FLAG O_RDWR
#endif

#ifndef SG_MAX_SENSE
#define SG_MAX_SENSE 16
#endif

typedef struct my_scsi_idlun {
/* why can't userland see this structure ??? */
    int dev_id;
    int host_unique_id;
} My_scsi_idlun;

typedef struct my_sg_scsi_id {
    int host_no;        /* as in "scsi<n>" where 'n' is one of 0, 1, 2 etc */
    int channel;
    int scsi_id;        /* scsi id of target device */
    int lun;
    int scsi_type;      /* TYPE_... defined in scsi/scsi.h */
    short h_cmd_per_lun;/* host (adapter) maximum commands per lun */
    short d_queue_depth;/* device (or adapter) maximum queue length */
    int unused1;        /* probably find a good use, set 0 for now */
    int unused2;        /* ditto */
} My_sg_scsi_id;

#ifdef SG_IO
int sg3_inq(int sg_fd, unsigned char * inqBuff, int do_extra);
#endif

#define EBUFF_SZ 256
static unsigned char inqCmdBlk [INQ_CMD_LEN] =
                                {0x12, 0, 0, 0, INQ_REPLY_LEN, 0};


void usage()
{
    printf("Usage: 'sg_scan [-a] [-n] [-w] [-i] [-x]'\n");
    printf("    where: -a   do alpha scan (ie sga, sgb, sgc)\n");
    printf("           -n   do numeric scan (ie sg0, sg1...) [default]\n");
    printf("           -w   force open with read/write flag\n");
    printf("           -i   do SCSI INQUIRY, output results\n");
    printf("           -x   extra information output about queuing\n");
}

void make_dev_name(char * fname, int k, int do_numeric)
{
    char buff[64];
    int  big,little;

    strcpy(fname, "/dev/sg");
    if (do_numeric) {
        sprintf(buff, "%d", k);
        strcat(fname, buff);
    }
    else {
        if (k < 26) {
            buff[0] = 'a' + (char)k;
            buff[1] = '\0';
            strcat(fname, buff);
        }
        else if (k <= 255) { /* assumes sequence goes x,y,z,aa,ab,ac etc */
            big    = k/26;
            little = k - (26 * big);
            big    = big - 1;

            buff[0] = 'a' + (char)big;
            buff[1] = 'a' + (char)little;
            buff[2] = '\0';
            strcat(fname, buff);
        }
        else
            strcat(fname, "xxxx");
    }
}


int main(int argc, char * argv[])
{
    int sg_fd, res, k, f;
    unsigned char inqBuff[OFF + INQ_REPLY_LEN];
    int inqInLen = OFF + sizeof(inqCmdBlk);
    int inqOutLen = OFF + INQ_REPLY_LEN;
    unsigned char * buffp = inqBuff + OFF;
    struct sg_header * isghp = (struct sg_header *)inqBuff;
    int do_numeric = NUMERIC_SCAN_DEF;
    int do_inquiry = 0;
    int do_extra = 0;
    int writeable = 0;
    int num_errors = 0;
    int num_silent = 0;
    int eacces_err = 0;
    char fname[64];
    char ebuff[EBUFF_SZ];
    My_scsi_idlun my_idlun;
    int host_no;
    int flags;
    int emul;

    for (k = 1; k < argc; ++k) {
        if (0 == strcmp("-n", argv[k]))
            do_numeric = 1;
        else if (0 == strcmp("-a", argv[k]))
            do_numeric = 0;
        else if (0 == strcmp("-w", argv[k]))
            writeable = 1;
        else if (0 == strcmp("-i", argv[k])) {
#ifndef SG_IO
            writeable = 1;
#endif
            do_inquiry = 1;
        }
        else if (0 == strcmp("-x", argv[k]))
            do_extra = 1;
        else if ((0 == strcmp("-?", argv[k])) ||
                 (0 == strncmp("-h", argv[k], 2))) {
            printf("Scan sg device names and optionally do an INQUIRY\n\n");
            usage();
            return 1;
        }
        else if (*argv[k] == '-') {
            printf("Unknown switch: %s\n", argv[k]);
            usage();
            return 1;
        }
        else if (*argv[k] != '-') {
            printf("Unknown argument\n");
            usage();
            return 1;
        }
    }

    flags = writeable ? O_RDWR : OPEN_FLAG;

    for (k = 0, res = 0; (k < 1000)  && (num_errors < MAX_ERRORS);
         ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
        if (res < 0) {
            snprintf(ebuff, EBUFF_SZ, ME "Error closing %s ", fname);
            perror(ME "close error");
            return 1;
        }
        make_dev_name(fname, k, do_numeric);

        sg_fd = open(fname, flags | O_NONBLOCK);
        if (sg_fd < 0) {
            if (EBUSY == errno) {
                printf("%s: device busy (O_EXCL lock), skipping\n", fname);
                continue;
            }
            else if ((ENODEV == errno) || (ENOENT == errno) ||
                     (ENXIO == errno)) {
                ++num_errors;
                ++num_silent;
                continue;
            }
            else {
                if (EACCES == errno)
                    eacces_err = 1;
                snprintf(ebuff, EBUFF_SZ, ME "Error opening %s ", fname);
                perror(ebuff);
                ++num_errors;
                continue;
            }
        }
        res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
        if (res < 0) {
            snprintf(ebuff, EBUFF_SZ,
	    	     ME "device %s failed on scsi ioctl, skip", fname);
            perror(ebuff);
            ++num_errors;
            continue;
        }
        res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
        if (res < 0) {
            snprintf(ebuff, EBUFF_SZ, ME "device %s failed on scsi "
	    	     "ioctl(2), skip", fname);
            perror(ebuff);
            ++num_errors;
            continue;
        }
#ifdef SG_EMULATED_HOST
        res = ioctl(sg_fd, SG_EMULATED_HOST, &emul);
        if (res < 0) {
            snprintf(ebuff, EBUFF_SZ, 
	    	     ME "device %s failed on sg ioctl(3), skip", fname);
            perror(ebuff);
            ++num_errors;
            continue;
        }
#else
        emul = 0;
#endif
        printf("%s: scsi%d channel=%d id=%d lun=%d", fname, host_no,
               (my_idlun.dev_id >> 16) & 0xff, my_idlun.dev_id & 0xff,
               (my_idlun.dev_id >> 8) & 0xff);
        if (emul)
            printf(" [em]");
#if 0
        printf(", huid=%d", my_idlun.host_unique_id);
#endif
#ifdef SG_GET_RESERVED_SIZE
        {
            My_sg_scsi_id m_id; /* compatible with sg_scsi_id_t in sg.h */

            res = ioctl(sg_fd, SG_GET_SCSI_ID, &m_id);
            if (res < 0) {
                snprintf(ebuff, EBUFF_SZ, ME "device %s ioctls(4), skip",
			 fname);
                perror(ebuff);
                ++num_errors;
                continue;
            }
            printf("  type=%d", m_id.scsi_type);
            if (do_extra)
                printf(" cmd_per_lun=%hd queue_depth=%hd\n",
                       m_id.h_cmd_per_lun, m_id.d_queue_depth);
            else
                printf("\n");
        }
#else
        printf("\n");
#endif
        if (! do_inquiry)
            continue;

#ifdef SG_IO
        if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &f) >= 0) && (f >= 30000)) {
            res = sg3_inq(sg_fd, inqBuff, do_extra);
            continue;
        }
#endif
        memset(isghp, 0, sizeof(struct sg_header));
        isghp->reply_len = inqOutLen;
        memcpy(inqBuff + OFF, inqCmdBlk, INQ_CMD_LEN);
        
        if (O_RDWR == (flags & O_ACCMODE)) { /* turn on blocking */
        f = fcntl(sg_fd, F_GETFL);
            fcntl(sg_fd, F_SETFL, f & (~ O_NONBLOCK)); 
        }
        else {
            close(sg_fd);
            sg_fd = open(fname, O_RDWR);
        }

        res = write(sg_fd, inqBuff, inqInLen);
        if (res < 0) {
            snprintf(ebuff, EBUFF_SZ, ME "device %s writing, skip", fname);
            perror(ebuff);
            ++num_errors;
            continue;
        }
        res = read(sg_fd, inqBuff, inqOutLen);
        if (res < 0) {
            snprintf(ebuff, EBUFF_SZ, ME "device %s reading, skip", fname);
            perror(ebuff);
            ++num_errors;
            continue;
        }
#ifdef SG_GET_RESERVED_SIZE
        if (! sg_chk_n_print("Error from Inquiry", isghp->target_status,
                             isghp->host_status, isghp->driver_status,
                             isghp->sense_buffer, SG_MAX_SENSE))
            continue;
#else
        if ((isghp->result != 0) || (0 != isghp->sense_buffer[0])) {
            printf("Error from Inquiry: result=%d\n", isghp->result);
            if (0 != isghp->sense_buffer[0])
                sg_print_sense("Error from Inquiry", isghp->sense_buffer,
			       SG_MAX_SENSE);
            continue;
        }
#endif
        f = (int)*(buffp + 7);
        printf("    %.8s  %.16s  %.4s ", buffp + 8, buffp + 16,
               buffp + 32);
        printf("[wide=%d sync=%d cmdq=%d sftre=%d pq=0x%x]\n",
               !!(f & 0x20), !!(f & 0x10), !!(f & 2), !!(f & 1),
               (*buffp & 0xe0) >> 5);
    }
    if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors)) {
        printf("Stopping because there are too many error\n");
        if (eacces_err)
            printf("    root access may be required\n");
    }
    return 0;
}

#ifdef SG_IO
int sg3_inq(int sg_fd, unsigned char * inqBuff, int do_extra)
{
    sg_io_hdr_t io_hdr;
    unsigned char sense_buffer[32];
    int ok;

    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
    io_hdr.interface_id = 'S';
    io_hdr.cmd_len = sizeof(inqCmdBlk);
    io_hdr.mx_sb_len = sizeof(sense_buffer);
    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
    io_hdr.dxfer_len = INQ_REPLY_LEN;
    io_hdr.dxferp = inqBuff;
    io_hdr.cmdp = inqCmdBlk;
    io_hdr.sbp = sense_buffer;
    io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */

    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
        perror(ME "Inquiry SG_IO ioctl error");
        return 1;
    }

    /* now for the error processing */
    ok = 0;
    switch (sg_err_category3(&io_hdr)) {
    case SG_ERR_CAT_CLEAN:
    case SG_ERR_CAT_RECOVERED:
        ok = 1;
        break;
    default: /* won't bother decoding other categories */
        sg_chk_n_print3("INQUIRY command error", &io_hdr);
        break;
    }

    if (ok) { /* output result if it is available */
        char * p = (char *)inqBuff;
        int f = (int)*(p + 7);
        printf("    %.8s  %.16s  %.4s ", p + 8, p + 16, p + 32);
        printf("[wide=%d sync=%d cmdq=%d sftre=%d pq=0x%x] ",
               !!(f & 0x20), !!(f & 0x10), !!(f & 2), !!(f & 1),
               (*p & 0xe0) >> 5);
        if (do_extra)
            printf("dur=%ums\n", io_hdr.duration);
        else
            printf("\n");
    }
    return 0;
}
#endif