blob: ce97e14d4dc3a03c799223f7da1ce2d7e514ec7b [file] [log] [blame]
#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