blob: 6a0848da80588f63e6505356f59fbdb5a5e1cf68 [file] [log] [blame]
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "sg_lib.h"
#include "sg_io_linux.h"
#include "sg_unaligned.h"
/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
device driver.
* Copyright (C) 2003-2015 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 will read a certain number of blocks of a given block size
from a given sg device node and write what is retrieved out to a
normal file. The purpose is to test the sg_iovec mechanism within the
sg_io_hdr structure.
Version 0.14 (20151209)
*/
#define ME "sg_iovec_tst: "
#define A_PRIME 509
#define IOVEC_ELEMS 2048
#define SENSE_BUFF_LEN 32
#define DEF_TIMEOUT 40000 /* 40,000 milliseconds */
struct sg_iovec iovec[IOVEC_ELEMS];
static void
usage(void)
{
printf("Usage: sg_iovec_tst [-a] [-b=bs] -c=num [-e=es] [-h]\n"
" <generic_device> <output_filename>\n");
printf(" where: -a async sg use (def: use ioctl(SGIO) )\n");
printf(" -b=bs block size (default 512 Bytes)\n");
printf(" -c=num count of blocks to transfer\n");
printf(" -e=es iovec element size (def: 509)\n");
printf(" -h this usage message\n");
printf(" reads from <generic_device> and sends to "
"<output_filename>\nUses iovec (a scatter list) in linear "
"mode\n");
}
/* Returns 0 if everything ok */
static int sg_read(int sg_fd, unsigned char * buff, int num_blocks,
int from_block, int bs, int elem_size, int async)
{
unsigned char rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned char senseBuff[SENSE_BUFF_LEN];
struct sg_io_hdr io_hdr;
struct pollfd a_poll;
int dxfer_len = bs * num_blocks;
int k, pos, rem, res;
sg_put_unaligned_be32((uint32_t)from_block, rdCmd + 2);
sg_put_unaligned_be16((uint16_t)from_block, rdCmd + 7);
for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) {
iovec[k].iov_base = buff + pos;
iovec[k].iov_len = (rem > elem_size) ? elem_size : rem;
if (rem <= elem_size)
break;
pos += elem_size;
rem -= elem_size;
}
if (k >= IOVEC_ELEMS) {
fprintf(stderr, "Can't fit dxfer_len=%d bytes in iovec\n", dxfer_len);
return -1;
}
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof(rdCmd);
io_hdr.cmdp = rdCmd;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = dxfer_len;
io_hdr.iovec_count = k + 1;
io_hdr.dxferp = iovec;
io_hdr.mx_sb_len = SENSE_BUFF_LEN;
io_hdr.sbp = senseBuff;
io_hdr.timeout = DEF_TIMEOUT;
io_hdr.pack_id = from_block;
if (async) {
res = write(sg_fd, &io_hdr, sizeof(io_hdr));
if (res < 0) {
perror("write(<sg_device>), error");
return -1;
} else if (res < (int)sizeof(io_hdr)) {
fprintf(stderr, "write(<sg_device>) returned %d, expected %d\n",
res, (int)sizeof(io_hdr));
return -1;
}
a_poll.fd = sg_fd;
a_poll.events = POLLIN;
a_poll.revents = 0;
res = poll(&a_poll, 1, 2000 /* millisecs */ );
if (res < 0) {
perror("poll error on <sg_device>");
return -1;
}
if (0 == (POLLIN & a_poll.revents)) {
fprintf(stderr, "strange, poll() completed without data to "
"read\n");
return -1;
}
res = read(sg_fd, &io_hdr, sizeof(io_hdr));
if (res < 0) {
perror("read(<sg_device>), error");
return -1;
} else if (res < (int)sizeof(io_hdr)) {
fprintf(stderr, "read(<sg_device>) returned %d, expected %d\n",
res, (int)sizeof(io_hdr));
return -1;
}
} else if (ioctl(sg_fd, SG_IO, &io_hdr)) {
perror("reading (SG_IO) on sg device, error");
return -1;
}
switch (sg_err_category3(&io_hdr)) {
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
from_block, num_blocks);
break;
case SG_LIB_CAT_UNIT_ATTENTION:
fprintf(stderr, "Unit attention\n");
return -1;
default:
sg_chk_n_print3("reading", &io_hdr, 1);
return -1;
}
return 0;
}
int main(int argc, char * argv[])
{
int sg_fd, fd, res, j, m, dxfer_len;
unsigned int k, num;
int do_async = 0;
int do_help = 0;
int blk_size = 512;
int elem_size = A_PRIME;
int count = 0;
char * sg_file_name = 0;
char * out_file_name = 0;
unsigned char * buffp;
for (j = 1; j < argc; ++j) {
if (0 == strcmp("-a", argv[j]))
do_async = 1;
else if (0 == strncmp("-b=", argv[j], 3)) {
m = 3;
num = sscanf(argv[j] + m, "%d", &blk_size);
if ((1 != num) || (blk_size <= 0)) {
printf("Couldn't decode number after '-b' switch\n");
sg_file_name = 0;
break;
}
} else if (0 == strncmp("-c=", argv[j], 3)) {
m = 3;
num = sscanf(argv[j] + m, "%d", &count);
if (1 != num) {
printf("Couldn't decode number after '-c' switch\n");
sg_file_name = 0;
break;
}
} else if (0 == strncmp("-e=", argv[j], 3)) {
m = 3;
num = sscanf(argv[j] + m, "%d", &elem_size);
if (1 != num) {
printf("Couldn't decode number after '-e' switch\n");
sg_file_name = 0;
break;
}
} else if (0 == strcmp("-h", argv[j]))
do_help = 1;
else if (*argv[j] == '-') {
printf("Unrecognized switch: %s\n", argv[j]);
sg_file_name = 0;
break;
} else if (NULL == sg_file_name)
sg_file_name = argv[j];
else
out_file_name = argv[j];
}
if (do_help) {
usage();
return 0;
}
if (NULL == sg_file_name) {
printf(">>> need sg node name (e.g. /dev/sg3)\n\n");
usage();
return 1;
}
if (NULL == out_file_name) {
printf(">>> need out filename (to place what is fetched by READ\n\n");
usage();
return 1;
}
if (0 == count) {
printf(">>> need count of blocks to READ\n\n");
usage();
return 1;
}
if (do_async)
sg_fd = open(sg_file_name, O_RDWR);
else
sg_fd = open(sg_file_name, O_RDONLY);
if (sg_fd < 0) {
perror(ME "sg device node open error");
return 1;
}
/* Don't worry, being very careful not to write to a none-sg file ... */
res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k);
if ((res < 0) || (k < 30000)) {
printf(ME "not a sg device, or driver prior to 3.x\n");
return 1;
}
fd = open(out_file_name, O_WRONLY | O_CREAT, 0666);
if (fd < 0) {
perror(ME "output file open error");
return 1;
}
dxfer_len = count * blk_size;
buffp = (unsigned char *)malloc(dxfer_len);
if (buffp) {
if (0 == sg_read(sg_fd, buffp, count, 0, blk_size, elem_size,
do_async)) {
if (write(fd, buffp, dxfer_len) < 0)
perror(ME "output write failed");
}
free(buffp);
} else
fprintf(stderr, "user space malloc for %d bytes failed\n",
dxfer_len);
res = close(fd);
if (res < 0) {
perror(ME "output file close error");
close(sg_fd);
return 1;
}
res = close(sg_fd);
if (res < 0) {
perror(ME "sg device close error");
return 1;
}
return 0;
}