1*44704f69SBart Van Assche /*
2*44704f69SBart Van Assche * Copyright (C) 2003-2021 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 * Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
11*44704f69SBart Van Assche * device driver.
12*44704f69SBart Van Assche * This C++ program will read a certain number of blocks of a given block
13*44704f69SBart Van Assche * size from a given sg device node using struct sg_iovec and write what is
14*44704f69SBart Van Assche * retrieved out to a normal file. The purpose is to test the sg_iovec
15*44704f69SBart Van Assche * mechanism within the sg_io_hdr and sg_io_v4 structures.
16*44704f69SBart Van Assche *
17*44704f69SBart Van Assche * struct sg_iovec and struct iovec [in include/uapi/uio.h] are basically
18*44704f69SBart Van Assche * the same thing: a pointer followed by a length (of type size_t). If
19*44704f69SBart Van Assche * applied to a disk then the pointer will hold a LBA and 'length' will
20*44704f69SBart Van Assche * be a number of logical blocks (which usually cannot exceed 2**32-1 .
21*44704f69SBart Van Assche *
22*44704f69SBart Van Assche */
23*44704f69SBart Van Assche
24*44704f69SBart Van Assche #include <unistd.h>
25*44704f69SBart Van Assche #include <signal.h>
26*44704f69SBart Van Assche #include <fcntl.h>
27*44704f69SBart Van Assche #include <stdio.h>
28*44704f69SBart Van Assche #include <stdlib.h>
29*44704f69SBart Van Assche #include <string.h>
30*44704f69SBart Van Assche #include <getopt.h>
31*44704f69SBart Van Assche #include <errno.h>
32*44704f69SBart Van Assche #include <poll.h>
33*44704f69SBart Van Assche #include <limits.h>
34*44704f69SBart Van Assche #include <time.h>
35*44704f69SBart Van Assche #define __STDC_FORMAT_MACROS 1
36*44704f69SBart Van Assche #include <inttypes.h>
37*44704f69SBart Van Assche #include <sys/ioctl.h>
38*44704f69SBart Van Assche #include <sys/types.h>
39*44704f69SBart Van Assche #include <sys/stat.h>
40*44704f69SBart Van Assche
41*44704f69SBart Van Assche #include <linux/bsg.h>
42*44704f69SBart Van Assche
43*44704f69SBart Van Assche #ifndef HAVE_LINUX_SG_V4_HDR
44*44704f69SBart Van Assche
45*44704f69SBart Van Assche /* Kernel uapi header contain __user decorations on user space pointers
46*44704f69SBart Van Assche * to indicate they are unsafe in the kernel space. However glibc takes
47*44704f69SBart Van Assche * all those __user decorations out from headers in /usr/include/linux .
48*44704f69SBart Van Assche * So to stop compile errors when directly importing include/uapi/scsi/sg.h
49*44704f69SBart Van Assche * undef __user before doing that include. */
50*44704f69SBart Van Assche #define __user
51*44704f69SBart Van Assche
52*44704f69SBart Van Assche /* Want to block the original sg.h header from also being included. That
53*44704f69SBart Van Assche * causes lots of multiple definition errors. This will only work if this
54*44704f69SBart Van Assche * header is included _before_ the original sg.h header. */
55*44704f69SBart Van Assche #define _SCSI_GENERIC_H /* original kernel header guard */
56*44704f69SBart Van Assche #define _SCSI_SG_H /* glibc header guard */
57*44704f69SBart Van Assche
58*44704f69SBart Van Assche #include "uapi_sg.h" /* local copy of include/uapi/scsi/sg.h */
59*44704f69SBart Van Assche
60*44704f69SBart Van Assche #else
61*44704f69SBart Van Assche #define __user
62*44704f69SBart Van Assche #endif /* end of: ifndef HAVE_LINUX_SG_V4_HDR */
63*44704f69SBart Van Assche
64*44704f69SBart Van Assche #include "sg_lib.h"
65*44704f69SBart Van Assche #include "sg_io_linux.h"
66*44704f69SBart Van Assche #include "sg_unaligned.h"
67*44704f69SBart Van Assche
68*44704f69SBart Van Assche // C++ local header
69*44704f69SBart Van Assche #include "sg_scat_gath.h"
70*44704f69SBart Van Assche
71*44704f69SBart Van Assche static const char * version_str = "1.08 20210214";
72*44704f69SBart Van Assche
73*44704f69SBart Van Assche #define ME "sg_iovec_tst: "
74*44704f69SBart Van Assche
75*44704f69SBart Van Assche #define IOVEC_ELEMS 1024 /* match current UIO_MAXIOV in <linux/uio.h> */
76*44704f69SBart Van Assche
77*44704f69SBart Van Assche #define DEF_BLK_SZ 512
78*44704f69SBart Van Assche #define SENSE_BUFF_LEN 32
79*44704f69SBart Van Assche #define DEF_TIMEOUT 40000 /* 40,000 milliseconds */
80*44704f69SBart Van Assche
81*44704f69SBart Van Assche static struct sg_iovec iovec[IOVEC_ELEMS];
82*44704f69SBart Van Assche
83*44704f69SBart Van Assche static int verbose;
84*44704f69SBart Van Assche
85*44704f69SBart Van Assche static struct option long_options[] = {
86*44704f69SBart Van Assche {"async", no_argument, 0, 'a'},
87*44704f69SBart Van Assche {"bs", required_argument, 0, 'b'},
88*44704f69SBart Van Assche {"elem_size", required_argument, 0, 'e'},
89*44704f69SBart Van Assche {"elem-size", required_argument, 0, 'e'},
90*44704f69SBart Van Assche {"elemsz", required_argument, 0, 'e'},
91*44704f69SBart Van Assche {"fill", required_argument, 0, 'f'},
92*44704f69SBart Van Assche {"from_skip", no_argument, 0, 'F'},
93*44704f69SBart Van Assche {"from-skip", no_argument, 0, 'F'},
94*44704f69SBart Van Assche {"help", no_argument, 0, 'h'},
95*44704f69SBart Van Assche {"num", required_argument, 0, 'n'},
96*44704f69SBart Van Assche {"num_blks", required_argument, 0, 'n'},
97*44704f69SBart Van Assche {"num-blks", required_argument, 0, 'n'},
98*44704f69SBart Van Assche {"sgl", required_argument, 0, 'S'},
99*44704f69SBart Van Assche {"sgv4", no_argument, 0, '4'},
100*44704f69SBart Van Assche {"skip", required_argument, 0, 's'},
101*44704f69SBart Van Assche {"verbose", no_argument, 0, 'v'},
102*44704f69SBart Van Assche {"version", no_argument, 0, 'V'},
103*44704f69SBart Van Assche {0, 0, 0, 0},
104*44704f69SBart Van Assche };
105*44704f69SBart Van Assche
106*44704f69SBart Van Assche
107*44704f69SBart Van Assche static void
usage(void)108*44704f69SBart Van Assche usage(void)
109*44704f69SBart Van Assche {
110*44704f69SBart Van Assche printf("Usage: sg_iovec_tst [--async] [--bs=BS] [--elem_sz=ES] "
111*44704f69SBart Van Assche "[--fill=F_ELEMS]\n"
112*44704f69SBart Van Assche " [from_skip] [--help] --num=NUM [--sgl=SFN] "
113*44704f69SBart Van Assche "[--sgv4]\n"
114*44704f69SBart Van Assche " [--skip=SKIP] [--verbose] [--version] "
115*44704f69SBart Van Assche "SG_DEV OUT_F\n");
116*44704f69SBart Van Assche printf("where:\n"
117*44704f69SBart Van Assche " --async|-a async sg usage (def: use ioctl(SG_IO) )\n");
118*44704f69SBart Van Assche printf(" --bs=BS|-b BS logical block size of SG_DEV (def: 512 "
119*44704f69SBart Van Assche "bytes)\n");
120*44704f69SBart Van Assche printf(" --elem_sz=ES|-e ES iovec element size (def: BS bytes)\n");
121*44704f69SBart Van Assche printf(" --fill=F_ELEMS|-f F_ELEMS append F_ELEMS*ES zero bytes "
122*44704f69SBart Van Assche "onto OUT_F\n"
123*44704f69SBart Van Assche " after each iovec element (def: "
124*44704f69SBart Van Assche "0)\n");
125*44704f69SBart Van Assche printf(" --from_skip|-F sgl output starts from SKIP (def: 0)\n");
126*44704f69SBart Van Assche printf(" --help|-h this usage message\n");
127*44704f69SBart Van Assche printf(" --num=NUM|-n NUM number of blocks to read from SG_DEV\n");
128*44704f69SBart Van Assche printf(" --sgl=SFN|-S SFN Sgl FileName (SFN) that is written to, "
129*44704f69SBart Van Assche "with\n"
130*44704f69SBart Van Assche " addresses and lengths having ES as "
131*44704f69SBart Van Assche "their unit\n");
132*44704f69SBart Van Assche printf(" --sgv4|-4 use the sg v4 interface (def: v3 "
133*44704f69SBart Van Assche "interface)\n");
134*44704f69SBart Van Assche printf(" --skip=SKIP|-s SKIP SKIP blocks before reading S_DEV "
135*44704f69SBart Van Assche "(def: 0)\n");
136*44704f69SBart Van Assche printf(" --verbose|-v increase verbosity\n");
137*44704f69SBart Van Assche printf(" --version|-V print version and exit\n\n");
138*44704f69SBart Van Assche printf("Reads from SG_DEV and writes that data to OUT_F in binary. Uses "
139*44704f69SBart Van Assche "iovec\n(a scatter gather list) in linear mode (i.e. it cuts up "
140*44704f69SBart Van Assche "a contiguous\nbuffer). Example:\n"
141*44704f69SBart Van Assche " sg_iovec_tst -n 8k -e 4k /dev/sg3 out.bin\n");
142*44704f69SBart Van Assche }
143*44704f69SBart Van Assche
144*44704f69SBart Van Assche /* Returns 0 if everything ok */
145*44704f69SBart Van Assche static int
sg_read(int sg_fd,uint8_t * buff,int num_blocks,int from_block,int bs,int elem_size,int async)146*44704f69SBart Van Assche sg_read(int sg_fd, uint8_t * buff, int num_blocks, int from_block, int bs,
147*44704f69SBart Van Assche int elem_size, int async)
148*44704f69SBart Van Assche {
149*44704f69SBart Van Assche uint8_t rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
150*44704f69SBart Van Assche uint8_t senseBuff[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
151*44704f69SBart Van Assche struct sg_io_hdr io_hdr;
152*44704f69SBart Van Assche struct pollfd a_poll;
153*44704f69SBart Van Assche int dxfer_len = bs * num_blocks;
154*44704f69SBart Van Assche int k, pos, rem;
155*44704f69SBart Van Assche
156*44704f69SBart Van Assche sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2);
157*44704f69SBart Van Assche sg_put_unaligned_be16((uint16_t)num_blocks, rdCmd + 7);
158*44704f69SBart Van Assche
159*44704f69SBart Van Assche for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) {
160*44704f69SBart Van Assche iovec[k].iov_base = buff + pos;
161*44704f69SBart Van Assche iovec[k].iov_len = (rem > elem_size) ? elem_size : rem;
162*44704f69SBart Van Assche if (rem <= elem_size)
163*44704f69SBart Van Assche break;
164*44704f69SBart Van Assche pos += elem_size;
165*44704f69SBart Van Assche rem -= elem_size;
166*44704f69SBart Van Assche }
167*44704f69SBart Van Assche if (k >= IOVEC_ELEMS) {
168*44704f69SBart Van Assche fprintf(stderr, "Can't fit dxfer_len=%d bytes in %d iovec elements "
169*44704f69SBart Van Assche "(would need %d)\n", dxfer_len, IOVEC_ELEMS,
170*44704f69SBart Van Assche dxfer_len / elem_size);
171*44704f69SBart Van Assche fprintf(stderr, "Try expanding elem_size which is currently %d "
172*44704f69SBart Van Assche "bytes\n", elem_size);
173*44704f69SBart Van Assche return -1;
174*44704f69SBart Van Assche }
175*44704f69SBart Van Assche memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
176*44704f69SBart Van Assche io_hdr.interface_id = 'S';
177*44704f69SBart Van Assche io_hdr.cmd_len = sizeof(rdCmd);
178*44704f69SBart Van Assche io_hdr.cmdp = rdCmd;
179*44704f69SBart Van Assche io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
180*44704f69SBart Van Assche io_hdr.dxfer_len = dxfer_len;
181*44704f69SBart Van Assche io_hdr.iovec_count = k + 1;
182*44704f69SBart Van Assche io_hdr.dxferp = iovec;
183*44704f69SBart Van Assche io_hdr.mx_sb_len = SENSE_BUFF_LEN;
184*44704f69SBart Van Assche io_hdr.sbp = senseBuff;
185*44704f69SBart Van Assche io_hdr.timeout = DEF_TIMEOUT;
186*44704f69SBart Van Assche io_hdr.pack_id = from_block;
187*44704f69SBart Van Assche if (verbose) {
188*44704f69SBart Van Assche char b[128];
189*44704f69SBart Van Assche
190*44704f69SBart Van Assche fprintf(stderr, "cdb: %s\n", sg_get_command_str(rdCmd, 10, true,
191*44704f69SBart Van Assche sizeof(b), b));
192*44704f69SBart Van Assche }
193*44704f69SBart Van Assche
194*44704f69SBart Van Assche if (async) {
195*44704f69SBart Van Assche int res = write(sg_fd, &io_hdr, sizeof(io_hdr));
196*44704f69SBart Van Assche
197*44704f69SBart Van Assche if (res < 0) {
198*44704f69SBart Van Assche perror("write(<sg_device>), error");
199*44704f69SBart Van Assche return -1;
200*44704f69SBart Van Assche } else if (res < (int)sizeof(io_hdr)) {
201*44704f69SBart Van Assche fprintf(stderr, "write(<sg_device>) returned %d, expected %d\n",
202*44704f69SBart Van Assche res, (int)sizeof(io_hdr));
203*44704f69SBart Van Assche return -1;
204*44704f69SBart Van Assche }
205*44704f69SBart Van Assche a_poll.fd = sg_fd;
206*44704f69SBart Van Assche a_poll.events = POLLIN;
207*44704f69SBart Van Assche a_poll.revents = 0;
208*44704f69SBart Van Assche res = poll(&a_poll, 1, 2000 /* millisecs */ );
209*44704f69SBart Van Assche if (res < 0) {
210*44704f69SBart Van Assche perror("poll error on <sg_device>");
211*44704f69SBart Van Assche return -1;
212*44704f69SBart Van Assche }
213*44704f69SBart Van Assche if (0 == (POLLIN & a_poll.revents)) {
214*44704f69SBart Van Assche fprintf(stderr, "strange, poll() completed without data to "
215*44704f69SBart Van Assche "read\n");
216*44704f69SBart Van Assche return -1;
217*44704f69SBart Van Assche }
218*44704f69SBart Van Assche res = read(sg_fd, &io_hdr, sizeof(io_hdr));
219*44704f69SBart Van Assche if (res < 0) {
220*44704f69SBart Van Assche perror("read(<sg_device>), error");
221*44704f69SBart Van Assche return -1;
222*44704f69SBart Van Assche } else if (res < (int)sizeof(io_hdr)) {
223*44704f69SBart Van Assche fprintf(stderr, "read(<sg_device>) returned %d, expected %d\n",
224*44704f69SBart Van Assche res, (int)sizeof(io_hdr));
225*44704f69SBart Van Assche return -1;
226*44704f69SBart Van Assche }
227*44704f69SBart Van Assche } else if (ioctl(sg_fd, SG_IO, &io_hdr)) {
228*44704f69SBart Van Assche perror("reading (SG_IO) on sg device, error");
229*44704f69SBart Van Assche return -1;
230*44704f69SBart Van Assche }
231*44704f69SBart Van Assche switch (sg_err_category3(&io_hdr)) {
232*44704f69SBart Van Assche case SG_LIB_CAT_CLEAN:
233*44704f69SBart Van Assche break;
234*44704f69SBart Van Assche case SG_LIB_CAT_RECOVERED:
235*44704f69SBart Van Assche fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
236*44704f69SBart Van Assche from_block, num_blocks);
237*44704f69SBart Van Assche break;
238*44704f69SBart Van Assche case SG_LIB_CAT_UNIT_ATTENTION:
239*44704f69SBart Van Assche fprintf(stderr, "Unit attention\n");
240*44704f69SBart Van Assche return -1;
241*44704f69SBart Van Assche default:
242*44704f69SBart Van Assche sg_chk_n_print3("reading", &io_hdr, 1);
243*44704f69SBart Van Assche return -1;
244*44704f69SBart Van Assche }
245*44704f69SBart Van Assche return 0;
246*44704f69SBart Van Assche }
247*44704f69SBart Van Assche
248*44704f69SBart Van Assche /* Returns 0 if everything ok */
249*44704f69SBart Van Assche static int
sg_read_v4(int sg_fd,uint8_t * buff,int num_blocks,int from_block,int bs,int elem_size,int async)250*44704f69SBart Van Assche sg_read_v4(int sg_fd, uint8_t * buff, int num_blocks, int from_block, int bs,
251*44704f69SBart Van Assche int elem_size, int async)
252*44704f69SBart Van Assche {
253*44704f69SBart Van Assche uint8_t rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
254*44704f69SBart Van Assche uint8_t senseBuff[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
255*44704f69SBart Van Assche struct sg_io_v4 io_hdr;
256*44704f69SBart Van Assche struct pollfd a_poll;
257*44704f69SBart Van Assche int dxfer_len = bs * num_blocks;
258*44704f69SBart Van Assche int k, pos, rem, res;
259*44704f69SBart Van Assche
260*44704f69SBart Van Assche sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2);
261*44704f69SBart Van Assche sg_put_unaligned_be16((uint16_t)num_blocks, rdCmd + 7);
262*44704f69SBart Van Assche
263*44704f69SBart Van Assche for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) {
264*44704f69SBart Van Assche iovec[k].iov_base = buff + pos;
265*44704f69SBart Van Assche iovec[k].iov_len = (rem > elem_size) ? elem_size : rem;
266*44704f69SBart Van Assche if (rem <= elem_size)
267*44704f69SBart Van Assche break;
268*44704f69SBart Van Assche pos += elem_size;
269*44704f69SBart Van Assche rem -= elem_size;
270*44704f69SBart Van Assche }
271*44704f69SBart Van Assche if (k >= IOVEC_ELEMS) {
272*44704f69SBart Van Assche fprintf(stderr, "Can't fit dxfer_len=%d bytes in %d iovec elements "
273*44704f69SBart Van Assche "(would need %d)\n", dxfer_len, IOVEC_ELEMS,
274*44704f69SBart Van Assche dxfer_len / elem_size);
275*44704f69SBart Van Assche fprintf(stderr, "Try expanding elem_size which is currently %d "
276*44704f69SBart Van Assche "bytes\n", elem_size);
277*44704f69SBart Van Assche return -1;
278*44704f69SBart Van Assche }
279*44704f69SBart Van Assche memset(&io_hdr, 0, sizeof(struct sg_io_v4));
280*44704f69SBart Van Assche io_hdr.guard = 'Q';
281*44704f69SBart Van Assche io_hdr.request_len = sizeof(rdCmd);
282*44704f69SBart Van Assche io_hdr.request = (uint64_t)(uintptr_t)rdCmd;
283*44704f69SBart Van Assche io_hdr.din_xfer_len = dxfer_len;
284*44704f69SBart Van Assche io_hdr.din_xferp = (uint64_t)(uintptr_t)iovec;
285*44704f69SBart Van Assche io_hdr.din_iovec_count = k + 1;
286*44704f69SBart Van Assche io_hdr.max_response_len = SG_DXFER_FROM_DEV;
287*44704f69SBart Van Assche io_hdr.response = (uint64_t)(uintptr_t)senseBuff;
288*44704f69SBart Van Assche io_hdr.timeout = DEF_TIMEOUT;
289*44704f69SBart Van Assche io_hdr.request_extra = from_block; /* pack_id */
290*44704f69SBart Van Assche if (verbose) {
291*44704f69SBart Van Assche char b[128];
292*44704f69SBart Van Assche
293*44704f69SBart Van Assche fprintf(stderr, "cdb: %s\n", sg_get_command_str(rdCmd, 10, true,
294*44704f69SBart Van Assche sizeof(b), b));
295*44704f69SBart Van Assche }
296*44704f69SBart Van Assche if (async) {
297*44704f69SBart Van Assche res = ioctl(sg_fd, SG_IOSUBMIT, &io_hdr);
298*44704f69SBart Van Assche if (res < 0) {
299*44704f69SBart Van Assche perror("ioctl(SG_IOSUBMIT <sg_device>), error");
300*44704f69SBart Van Assche return -1;
301*44704f69SBart Van Assche }
302*44704f69SBart Van Assche a_poll.fd = sg_fd;
303*44704f69SBart Van Assche a_poll.events = POLLIN;
304*44704f69SBart Van Assche a_poll.revents = 0;
305*44704f69SBart Van Assche res = poll(&a_poll, 1, 2000 /* millisecs */ );
306*44704f69SBart Van Assche if (res < 0) {
307*44704f69SBart Van Assche perror("poll error on <sg_device>");
308*44704f69SBart Van Assche return -1;
309*44704f69SBart Van Assche }
310*44704f69SBart Van Assche if (0 == (POLLIN & a_poll.revents)) {
311*44704f69SBart Van Assche fprintf(stderr, "strange, poll() completed without data to "
312*44704f69SBart Van Assche "read\n");
313*44704f69SBart Van Assche return -1;
314*44704f69SBart Van Assche }
315*44704f69SBart Van Assche res = ioctl(sg_fd, SG_IORECEIVE, &io_hdr);
316*44704f69SBart Van Assche if (res < 0) {
317*44704f69SBart Van Assche perror("ioctl(SG_IORECEIVE <sg_device>), error");
318*44704f69SBart Van Assche return -1;
319*44704f69SBart Van Assche }
320*44704f69SBart Van Assche } else if (ioctl(sg_fd, SG_IO, &io_hdr)) {
321*44704f69SBart Van Assche perror("ioctl(SG_IO) on sg device, error");
322*44704f69SBart Van Assche return -1;
323*44704f69SBart Van Assche }
324*44704f69SBart Van Assche
325*44704f69SBart Van Assche res = sg_err_category_new(io_hdr.device_status, io_hdr.transport_status,
326*44704f69SBart Van Assche io_hdr.driver_status,
327*44704f69SBart Van Assche (const uint8_t *)(unsigned long)io_hdr.response,
328*44704f69SBart Van Assche io_hdr.response_len);
329*44704f69SBart Van Assche switch (res) {
330*44704f69SBart Van Assche case SG_LIB_CAT_CLEAN:
331*44704f69SBart Van Assche break;
332*44704f69SBart Van Assche case SG_LIB_CAT_RECOVERED:
333*44704f69SBart Van Assche fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
334*44704f69SBart Van Assche from_block, num_blocks);
335*44704f69SBart Van Assche break;
336*44704f69SBart Van Assche case SG_LIB_CAT_UNIT_ATTENTION:
337*44704f69SBart Van Assche fprintf(stderr, "Unit attention\n");
338*44704f69SBart Van Assche return -1;
339*44704f69SBart Van Assche default:
340*44704f69SBart Van Assche sg_linux_sense_print("reading", io_hdr.device_status,
341*44704f69SBart Van Assche io_hdr.transport_status, io_hdr.driver_status,
342*44704f69SBart Van Assche senseBuff, io_hdr.response_len, true);
343*44704f69SBart Van Assche return -1;
344*44704f69SBart Van Assche }
345*44704f69SBart Van Assche return 0;
346*44704f69SBart Van Assche }
347*44704f69SBart Van Assche
348*44704f69SBart Van Assche
349*44704f69SBart Van Assche int
main(int argc,char * argv[])350*44704f69SBart Van Assche main(int argc, char * argv[])
351*44704f69SBart Van Assche {
352*44704f69SBart Van Assche bool do_sgv4 = false;
353*44704f69SBart Van Assche bool do_async = false;
354*44704f69SBart Van Assche bool do_help = false;
355*44704f69SBart Van Assche bool from_skip = false;
356*44704f69SBart Van Assche bool blk_size_given = false;
357*44704f69SBart Van Assche bool elem_size_given = false;
358*44704f69SBart Van Assche int sg_fd, fd, c, res, res2, err, dxfer_len;
359*44704f69SBart Van Assche unsigned int k;
360*44704f69SBart Van Assche int blk_size = DEF_BLK_SZ;
361*44704f69SBart Van Assche int elem_size = blk_size;
362*44704f69SBart Van Assche int num_blks = 0;
363*44704f69SBart Van Assche int f_elems = 0;
364*44704f69SBart Van Assche int64_t start_blk = 0;
365*44704f69SBart Van Assche char * sg_dev_name = 0;
366*44704f69SBart Van Assche char * out_file_name = 0;
367*44704f69SBart Van Assche char * sgl_fn = 0;
368*44704f69SBart Van Assche uint8_t * buffp;
369*44704f69SBart Van Assche uint8_t * fillp = NULL;
370*44704f69SBart Van Assche FILE * fp = NULL;
371*44704f69SBart Van Assche
372*44704f69SBart Van Assche while (1) {
373*44704f69SBart Van Assche int option_index = 0;
374*44704f69SBart Van Assche
375*44704f69SBart Van Assche c = getopt_long(argc, argv, "4ab:e:f:Fhn:s:S:vV",
376*44704f69SBart Van Assche long_options, &option_index);
377*44704f69SBart Van Assche if (c == -1)
378*44704f69SBart Van Assche break;
379*44704f69SBart Van Assche
380*44704f69SBart Van Assche switch (c) {
381*44704f69SBart Van Assche case '4':
382*44704f69SBart Van Assche do_sgv4 = true;
383*44704f69SBart Van Assche break;
384*44704f69SBart Van Assche case 'a':
385*44704f69SBart Van Assche do_async = true;
386*44704f69SBart Van Assche break;
387*44704f69SBart Van Assche case 'b':
388*44704f69SBart Van Assche blk_size = sg_get_num(optarg);
389*44704f69SBart Van Assche if (blk_size < 1) {
390*44704f69SBart Van Assche printf("Couldn't decode positive number after '--bs=' "
391*44704f69SBart Van Assche "option\n");
392*44704f69SBart Van Assche sg_dev_name = 0;
393*44704f69SBart Van Assche } else
394*44704f69SBart Van Assche blk_size_given = true;
395*44704f69SBart Van Assche break;
396*44704f69SBart Van Assche case 'e':
397*44704f69SBart Van Assche elem_size = sg_get_num(optarg);
398*44704f69SBart Van Assche if (elem_size < 1) {
399*44704f69SBart Van Assche printf("Couldn't decode positive number after '--elem_size=' "
400*44704f69SBart Van Assche "option\n");
401*44704f69SBart Van Assche sg_dev_name = 0;
402*44704f69SBart Van Assche } else
403*44704f69SBart Van Assche elem_size_given = true;
404*44704f69SBart Van Assche break;
405*44704f69SBart Van Assche case 'f':
406*44704f69SBart Van Assche f_elems = sg_get_num(optarg);
407*44704f69SBart Van Assche if (f_elems < 0) {
408*44704f69SBart Van Assche printf("Couldn't decode number after '--fill=' option\n");
409*44704f69SBart Van Assche sg_dev_name = 0;
410*44704f69SBart Van Assche }
411*44704f69SBart Van Assche break;
412*44704f69SBart Van Assche case 'F':
413*44704f69SBart Van Assche from_skip = true;
414*44704f69SBart Van Assche break;
415*44704f69SBart Van Assche case 'h':
416*44704f69SBart Van Assche do_help = true;
417*44704f69SBart Van Assche break;
418*44704f69SBart Van Assche case 'n':
419*44704f69SBart Van Assche num_blks = sg_get_num(optarg);
420*44704f69SBart Van Assche if (num_blks < 1) {
421*44704f69SBart Van Assche printf("Couldn't decode positive number after '--num=' "
422*44704f69SBart Van Assche "option\n");
423*44704f69SBart Van Assche sg_dev_name = 0;
424*44704f69SBart Van Assche }
425*44704f69SBart Van Assche break;
426*44704f69SBart Van Assche case 's':
427*44704f69SBart Van Assche start_blk = sg_get_llnum(optarg);
428*44704f69SBart Van Assche if ((start_blk < 0) || (start_blk > INT_MAX)) {
429*44704f69SBart Van Assche printf("Couldn't decode number after '--skip=' option\n");
430*44704f69SBart Van Assche sg_dev_name = 0;
431*44704f69SBart Van Assche }
432*44704f69SBart Van Assche break;
433*44704f69SBart Van Assche case 'S':
434*44704f69SBart Van Assche if (sgl_fn) {
435*44704f69SBart Van Assche printf("Looks like --sgl=SFN has been given twice\n");
436*44704f69SBart Van Assche sg_dev_name = 0;
437*44704f69SBart Van Assche } else
438*44704f69SBart Van Assche sgl_fn = optarg;
439*44704f69SBart Van Assche break;
440*44704f69SBart Van Assche case 'v':
441*44704f69SBart Van Assche ++verbose;
442*44704f69SBart Van Assche break;
443*44704f69SBart Van Assche case 'V':
444*44704f69SBart Van Assche printf("Version: %s\n", version_str);
445*44704f69SBart Van Assche return 0;
446*44704f69SBart Van Assche default:
447*44704f69SBart Van Assche fprintf(stderr, "unrecognised option code 0x%x ??\n", c);
448*44704f69SBart Van Assche usage();
449*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
450*44704f69SBart Van Assche }
451*44704f69SBart Van Assche }
452*44704f69SBart Van Assche if (optind < argc) {
453*44704f69SBart Van Assche if (NULL == sg_dev_name) {
454*44704f69SBart Van Assche sg_dev_name = argv[optind];
455*44704f69SBart Van Assche ++optind;
456*44704f69SBart Van Assche }
457*44704f69SBart Van Assche if (optind < argc) {
458*44704f69SBart Van Assche if (sg_dev_name) {
459*44704f69SBart Van Assche out_file_name = argv[optind];
460*44704f69SBart Van Assche ++optind;
461*44704f69SBart Van Assche }
462*44704f69SBart Van Assche if (optind < argc) {
463*44704f69SBart Van Assche for (; optind < argc; ++optind)
464*44704f69SBart Van Assche fprintf(stderr, "Unexpected extra argument: %s\n",
465*44704f69SBart Van Assche argv[optind]);
466*44704f69SBart Van Assche usage();
467*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
468*44704f69SBart Van Assche }
469*44704f69SBart Van Assche }
470*44704f69SBart Van Assche }
471*44704f69SBart Van Assche if (do_help) {
472*44704f69SBart Van Assche usage();
473*44704f69SBart Van Assche return 0;
474*44704f69SBart Van Assche }
475*44704f69SBart Van Assche if (NULL == sg_dev_name) {
476*44704f69SBart Van Assche printf(">>> need sg node name (e.g. /dev/sg3)\n\n");
477*44704f69SBart Van Assche usage();
478*44704f69SBart Van Assche return 1;
479*44704f69SBart Van Assche }
480*44704f69SBart Van Assche if (NULL == out_file_name) {
481*44704f69SBart Van Assche printf(">>> need out filename (to place what is fetched by READ\n\n");
482*44704f69SBart Van Assche usage();
483*44704f69SBart Van Assche return 1;
484*44704f69SBart Van Assche }
485*44704f69SBart Van Assche if (0 == num_blks) {
486*44704f69SBart Van Assche printf(">>> need number of blocks to READ\n\n");
487*44704f69SBart Van Assche usage();
488*44704f69SBart Van Assche return 1;
489*44704f69SBart Van Assche }
490*44704f69SBart Van Assche
491*44704f69SBart Van Assche if ((! elem_size_given) && blk_size_given)
492*44704f69SBart Van Assche elem_size = blk_size;
493*44704f69SBart Van Assche
494*44704f69SBart Van Assche if (do_async)
495*44704f69SBart Van Assche sg_fd = open(sg_dev_name, O_RDWR);
496*44704f69SBart Van Assche else
497*44704f69SBart Van Assche sg_fd = open(sg_dev_name, O_RDONLY);
498*44704f69SBart Van Assche if (sg_fd < 0) {
499*44704f69SBart Van Assche perror(ME "sg device node open error");
500*44704f69SBart Van Assche return 1;
501*44704f69SBart Van Assche }
502*44704f69SBart Van Assche /* Don't worry, being very careful not to write to a none-sg file ... */
503*44704f69SBart Van Assche res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k);
504*44704f69SBart Van Assche if ((res < 0) || (k < 30000)) {
505*44704f69SBart Van Assche printf(ME "not a sg device, or driver prior to 3.x\n");
506*44704f69SBart Van Assche return 1;
507*44704f69SBart Van Assche }
508*44704f69SBart Van Assche fd = open(out_file_name, O_WRONLY | O_CREAT, 0666);
509*44704f69SBart Van Assche if (fd < 0) {
510*44704f69SBart Van Assche perror(ME "output file open error");
511*44704f69SBart Van Assche return 1;
512*44704f69SBart Van Assche }
513*44704f69SBart Van Assche if (f_elems > 0) {
514*44704f69SBart Van Assche fillp = (uint8_t *)calloc(f_elems, elem_size);
515*44704f69SBart Van Assche if (NULL == fillp) {
516*44704f69SBart Van Assche fprintf(stderr, "fill calloc for %d bytes failed\n",
517*44704f69SBart Van Assche f_elems * elem_size);
518*44704f69SBart Van Assche goto fini;
519*44704f69SBart Van Assche }
520*44704f69SBart Van Assche }
521*44704f69SBart Van Assche if (sgl_fn) {
522*44704f69SBart Van Assche time_t t = time(NULL);
523*44704f69SBart Van Assche struct tm *tm = localtime(&t);
524*44704f69SBart Van Assche char s[128];
525*44704f69SBart Van Assche
526*44704f69SBart Van Assche fp = fopen(sgl_fn, "w");
527*44704f69SBart Van Assche if (NULL == fp) {
528*44704f69SBart Van Assche err = errno;
529*44704f69SBart Van Assche fprintf(stderr, "Unable to open %s, error: %s\n", sgl_fn,
530*44704f69SBart Van Assche strerror(err));
531*44704f69SBart Van Assche res = sg_convert_errno(err);
532*44704f69SBart Van Assche goto fini;
533*44704f69SBart Van Assche }
534*44704f69SBart Van Assche strftime(s, sizeof(s), "%c", tm);
535*44704f69SBart Van Assche fprintf(fp, "# Scatter gather list generated by sg_iovec_tst "
536*44704f69SBart Van Assche "%s\n#\n", s);
537*44704f69SBart Van Assche }
538*44704f69SBart Van Assche
539*44704f69SBart Van Assche dxfer_len = num_blks * blk_size;
540*44704f69SBart Van Assche buffp = (uint8_t *)calloc(num_blks, blk_size);
541*44704f69SBart Van Assche if (buffp) {
542*44704f69SBart Van Assche int dx_len;
543*44704f69SBart Van Assche int64_t curr_blk = from_skip ? start_blk : 0;
544*44704f69SBart Van Assche
545*44704f69SBart Van Assche if (do_sgv4) {
546*44704f69SBart Van Assche if (sg_read(sg_fd, buffp, num_blks, (int)start_blk, blk_size,
547*44704f69SBart Van Assche elem_size, do_async))
548*44704f69SBart Van Assche goto free_buff;
549*44704f69SBart Van Assche } else {
550*44704f69SBart Van Assche if (sg_read_v4(sg_fd, buffp, num_blks, (int)start_blk, blk_size,
551*44704f69SBart Van Assche elem_size, do_async))
552*44704f69SBart Van Assche goto free_buff;
553*44704f69SBart Van Assche }
554*44704f69SBart Van Assche if (f_elems > 0) {
555*44704f69SBart Van Assche int fill_len = f_elems * elem_size;
556*44704f69SBart Van Assche
557*44704f69SBart Van Assche for (dx_len = 0; dx_len < dxfer_len; dx_len += elem_size) {
558*44704f69SBart Van Assche if (write(fd, buffp + dx_len, elem_size) < 0) {
559*44704f69SBart Van Assche perror(ME "partial dxfer output write failed");
560*44704f69SBart Van Assche break;
561*44704f69SBart Van Assche }
562*44704f69SBart Van Assche if (sgl_fn) {
563*44704f69SBart Van Assche fprintf(fp, "%" PRId64 ",1\n", curr_blk);
564*44704f69SBart Van Assche curr_blk += f_elems + 1;
565*44704f69SBart Van Assche }
566*44704f69SBart Van Assche if (write(fd, fillp, fill_len) < 0) {
567*44704f69SBart Van Assche perror(ME "partial fill output write failed");
568*44704f69SBart Van Assche break;
569*44704f69SBart Van Assche }
570*44704f69SBart Van Assche }
571*44704f69SBart Van Assche } else if (write(fd, buffp, dxfer_len) < 0)
572*44704f69SBart Van Assche perror(ME "full output write failed");
573*44704f69SBart Van Assche else if (sgl_fn) {
574*44704f69SBart Van Assche for (dx_len = 0; dx_len < dxfer_len; dx_len += elem_size)
575*44704f69SBart Van Assche fprintf(fp, "%" PRId64 ",1\n", curr_blk++);
576*44704f69SBart Van Assche }
577*44704f69SBart Van Assche free_buff:
578*44704f69SBart Van Assche free(buffp);
579*44704f69SBart Van Assche } else
580*44704f69SBart Van Assche fprintf(stderr, "user space calloc for %d bytes failed\n",
581*44704f69SBart Van Assche dxfer_len);
582*44704f69SBart Van Assche res = close(fd);
583*44704f69SBart Van Assche if (res < 0) {
584*44704f69SBart Van Assche perror(ME "output file close error");
585*44704f69SBart Van Assche close(sg_fd);
586*44704f69SBart Van Assche return 1;
587*44704f69SBart Van Assche }
588*44704f69SBart Van Assche fini:
589*44704f69SBart Van Assche res2 = close(sg_fd);
590*44704f69SBart Van Assche if (res2 < 0) {
591*44704f69SBart Van Assche err = errno;
592*44704f69SBart Van Assche perror(ME "sg device close error");
593*44704f69SBart Van Assche if (0 == res)
594*44704f69SBart Van Assche res = sg_convert_errno(err);
595*44704f69SBart Van Assche }
596*44704f69SBart Van Assche if (fp)
597*44704f69SBart Van Assche fclose(fp);
598*44704f69SBart Van Assche return res;
599*44704f69SBart Van Assche }
600