1 #include <unistd.h>
2 #include <fcntl.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <sys/ioctl.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include "sg_lib.h"
11 #include "sg_io_linux.h"
12
13 /* This is a simple program executing a SCSI INQUIRY command and a
14 TEST UNIT READY command using the SCSI generic (sg) driver.
15 There is another variant of this program called "sg_simple1".
16 This variant demonstrates using the scatter gather facility in
17 the sg_io_hdr interface to break an INQUIRY response into its
18 component parts.
19
20 * Copyright (C) 1999-2016 D. Gilbert
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2, or (at your option)
24 * any later version.
25
26 Invocation: sg_simple3 [-x] <sg_device>
27
28 Version 03.59 (20160528)
29
30 6 byte INQUIRY command:
31 [0x12][ |lu][pg cde][res ][al len][cntrl ]
32
33 6 byte TEST UNIT READY command:
34 [0x00][ |lu][res ][res ][res ][res ]
35
36 */
37
38 #define INQ_REPLY_BASE_LEN 8
39 #define INQ_REPLY_VID_LEN 8
40 #define INQ_REPLY_PID_LEN 16
41 #define INQ_REPLY_PREV_LEN 4
42 #define INQ_REPLY_IOVEC_COUNT 4
43 #define INQ_CMD_LEN 6
44 #define TUR_CMD_LEN 6
45
46 #define EBUFF_SZ 256
47
main(int argc,char * argv[])48 int main(int argc, char * argv[])
49 {
50 int sg_fd, k, ok;
51 unsigned char inq_cdb[INQ_CMD_LEN] = {0x12, 0, 0, 0,
52 INQ_REPLY_BASE_LEN + INQ_REPLY_VID_LEN +
53 INQ_REPLY_PID_LEN + INQ_REPLY_PREV_LEN, 0};
54 unsigned char tur_cdb[TUR_CMD_LEN] = {0x00, 0, 0, 0, 0, 0};
55 sg_iovec_t iovec[INQ_REPLY_IOVEC_COUNT];
56 unsigned char inqBaseBuff[INQ_REPLY_BASE_LEN];
57 char inqVidBuff[INQ_REPLY_VID_LEN];
58 char inqPidBuff[INQ_REPLY_PID_LEN];
59 char inqPRevBuff[INQ_REPLY_PREV_LEN];
60 sg_io_hdr_t io_hdr;
61 char * file_name = 0;
62 char ebuff[EBUFF_SZ];
63 unsigned char sense_buffer[32];
64 int do_extra = 0;
65
66 for (k = 1; k < argc; ++k) {
67 if (0 == memcmp("-x", argv[k], 2))
68 do_extra = 1;
69 else if (*argv[k] == '-') {
70 printf("Unrecognized switch: %s\n", argv[k]);
71 file_name = 0;
72 break;
73 }
74 else if (0 == file_name)
75 file_name = argv[k];
76 else {
77 printf("too many arguments\n");
78 file_name = 0;
79 break;
80 }
81 }
82 if (0 == file_name) {
83 printf("Usage: 'sg_simple3 [-x] <sg_device>'\n");
84 return 1;
85 }
86
87 /* N.B. An access mode of O_RDWR is required for some SCSI commands */
88 if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
89 snprintf(ebuff, EBUFF_SZ,
90 "sg_simple3: error opening file: %s", file_name);
91 perror(ebuff);
92 return 1;
93 }
94 /* Just to be safe, check we have a new sg device by trying an ioctl */
95 if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
96 printf("sg_simple3: %s doesn't seem to be an new sg device\n",
97 file_name);
98 close(sg_fd);
99 return 1;
100 }
101
102 /* Prepare INQUIRY command */
103 memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
104 io_hdr.interface_id = 'S';
105 io_hdr.cmd_len = sizeof(inq_cdb);
106 io_hdr.iovec_count = INQ_REPLY_IOVEC_COUNT;
107 io_hdr.mx_sb_len = sizeof(sense_buffer);
108 io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
109 io_hdr.dxfer_len = INQ_REPLY_BASE_LEN + INQ_REPLY_VID_LEN +
110 INQ_REPLY_PID_LEN + INQ_REPLY_PREV_LEN;
111 iovec[0].iov_base = inqBaseBuff;
112 iovec[0].iov_len = INQ_REPLY_BASE_LEN;
113 iovec[1].iov_base = inqVidBuff;
114 iovec[1].iov_len = INQ_REPLY_VID_LEN;
115 iovec[2].iov_base = inqPidBuff;
116 iovec[2].iov_len = INQ_REPLY_PID_LEN;
117 iovec[3].iov_base = inqPRevBuff;
118 iovec[3].iov_len = INQ_REPLY_PREV_LEN;
119 io_hdr.dxferp = iovec;
120 io_hdr.cmdp = inq_cdb;
121 io_hdr.sbp = sense_buffer;
122 io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
123 /* io_hdr.flags = 0; */ /* take defaults: indirect IO, etc */
124 /* io_hdr.pack_id = 0; */
125 /* io_hdr.usr_ptr = NULL; */
126
127 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
128 perror("sg_simple3: Inquiry SG_IO ioctl error");
129 close(sg_fd);
130 return 1;
131 }
132
133 /* now for the error processing */
134 ok = 0;
135 switch (sg_err_category3(&io_hdr)) {
136 case SG_LIB_CAT_CLEAN:
137 ok = 1;
138 break;
139 case SG_LIB_CAT_RECOVERED:
140 printf("Recovered error on INQUIRY, continuing\n");
141 ok = 1;
142 break;
143 default: /* won't bother decoding other categories */
144 sg_chk_n_print3("INQUIRY command error", &io_hdr, 1);
145 break;
146 }
147
148 if (ok) { /* output result if it is available */
149 char * p = (char *)inqBaseBuff;
150 int f = (int)*(p + 7);
151 printf("Some of the INQUIRY command's results:\n");
152 printf(" %.8s %.16s %.4s ", inqVidBuff, inqPidBuff, inqPRevBuff);
153 printf("[wide=%d sync=%d cmdque=%d sftre=%d]\n",
154 !!(f & 0x20), !!(f & 0x10), !!(f & 2), !!(f & 1));
155 /* Extra info, not necessary to look at */
156 if (do_extra)
157 printf("INQUIRY duration=%u millisecs, resid=%d, msg_status=%d\n",
158 io_hdr.duration, io_hdr.resid, (int)io_hdr.msg_status);
159 }
160
161
162 /* Prepare TEST UNIT READY command */
163 memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
164 io_hdr.interface_id = 'S';
165 io_hdr.cmd_len = sizeof(tur_cdb);
166 io_hdr.mx_sb_len = sizeof(sense_buffer);
167 io_hdr.dxfer_direction = SG_DXFER_NONE;
168 io_hdr.cmdp = tur_cdb;
169 io_hdr.sbp = sense_buffer;
170 io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */
171
172 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
173 perror("sg_simple3: Test Unit Ready SG_IO ioctl error");
174 close(sg_fd);
175 return 1;
176 }
177
178 /* now for the error processing */
179 ok = 0;
180 switch (sg_err_category3(&io_hdr)) {
181 case SG_LIB_CAT_CLEAN:
182 ok = 1;
183 break;
184 case SG_LIB_CAT_RECOVERED:
185 printf("Recovered error on Test Unit Ready, continuing\n");
186 ok = 1;
187 break;
188 default: /* won't bother decoding other categories */
189 sg_chk_n_print3("Test Unit Ready command error", &io_hdr, 1);
190 break;
191 }
192
193 if (ok)
194 printf("Test Unit Ready successful so unit is ready!\n");
195 else
196 printf("Test Unit Ready failed so unit may _not_ be ready!\n");
197
198 if (do_extra)
199 printf("TEST UNIT READY duration=%u millisecs, resid=%d, "
200 "msg_status=%d\n", io_hdr.duration, io_hdr.resid,
201 (int)io_hdr.msg_status);
202
203 close(sg_fd);
204 return 0;
205 }
206