diff options
-rw-r--r-- | archive/sg_err.h | 4 | ||||
-rw-r--r-- | examples/sg_iovec_tst.c | 194 | ||||
-rw-r--r-- | examples/sg_simple_aio.c | 219 | ||||
-rw-r--r-- | utils/Makefile | 58 | ||||
-rw-r--r-- | utils/hxascdmp.c | 171 |
5 files changed, 644 insertions, 2 deletions
diff --git a/archive/sg_err.h b/archive/sg_err.h index 317767b6..ef57b5ce 100644 --- a/archive/sg_err.h +++ b/archive/sg_err.h @@ -3,7 +3,7 @@ /* Feel free to copy and modify this GPL-ed code into your applications. */ -/* Version 0.89 (20030313) +/* Version 0.90 (20030519) */ @@ -157,6 +157,6 @@ extern int sg_err_category3(struct sg_io_hdr * hp); extern int sg_get_command_size(unsigned char opcode); extern void sg_get_command_name(unsigned char opcode, int buff_len, - char * buff); + char * buff); #endif diff --git a/examples/sg_iovec_tst.c b/examples/sg_iovec_tst.c new file mode 100644 index 00000000..16e7a4d7 --- /dev/null +++ b/examples/sg_iovec_tst.c @@ -0,0 +1,194 @@ +#include <unistd.h> +#include <signal.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) 2003 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.10 (20030529) +*/ + + +#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]; + +/* Returns 0 if everything ok */ +int sg_read(int sg_fd, unsigned char * buff, int num_blocks, int from_block, + int bs) +{ + 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; + int dxfer_len = bs * num_blocks; + int k, pos, rem; + + rdCmd[2] = (unsigned char)((from_block >> 24) & 0xff); + rdCmd[3] = (unsigned char)((from_block >> 16) & 0xff); + rdCmd[4] = (unsigned char)((from_block >> 8) & 0xff); + rdCmd[5] = (unsigned char)(from_block & 0xff); + rdCmd[7] = (unsigned char)((num_blocks >> 8) & 0xff); + rdCmd[8] = (unsigned char)(num_blocks & 0xff); + + for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) { + iovec[k].iov_base = buff + pos; + iovec[k].iov_len = (rem > A_PRIME) ? A_PRIME : rem; + if (rem <= A_PRIME) + break; + pos += A_PRIME; + rem -= A_PRIME; + } + 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 (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_ERR_CAT_CLEAN: + break; + case SG_ERR_CAT_RECOVERED: + fprintf(stderr, "Recovered error while reading block=%d, num=%d\n", + from_block, num_blocks); + break; + case SG_ERR_CAT_MEDIA_CHANGED: + fprintf(stderr, "Media changed\n"); + return -1; + default: + sg_chk_n_print3("reading", &io_hdr); + 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_help = 0; + int blk_size = 512; + int count = 0; + char * sg_file_name = 0; + char * out_file_name = 0; + unsigned char * buffp; + + for (j = 1; j < argc; ++j) { + 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 == 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 ((NULL == sg_file_name) || (NULL == out_file_name) || (0 == count)) { + printf("Usage: sg_iovec_tst [-h] [-b=num] -c=num <generic_device> " + "<output_filename>\n"); + printf(" where: -h this usage message\n"); + printf(" -b=num block size (default 512 Bytes)\n"); + printf(" -c=num count of blocks to transfer\n"); + printf(" reads from <generic_device> and sends to <output_filename>\n"); + return 1; + } + + 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 = malloc(dxfer_len); + if (buffp) { + if (0 == sg_read(sg_fd, buffp, count, 0, blk_size)) { + if (write(fd, buffp, dxfer_len) < 0) + perror(ME "output write failed"); + } + free(buffp); + } + 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; +} diff --git a/examples/sg_simple_aio.c b/examples/sg_simple_aio.c new file mode 100644 index 00000000..127629aa --- /dev/null +++ b/examples/sg_simple_aio.c @@ -0,0 +1,219 @@ +#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 <libaio.h> +#include "sg_include.h" +#include "sg_err.h" + +/* This is a simple program executing a SCSI INQUIRY command and a + TEST UNIT READY command using the SCSI generic (sg) driver + This variant to test async I/O. + +* Copyright (C) 2003 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. + + Invocation: sg_simple_aio [-x] <sg_device> + + Version 0.91 (20031109) + +6 byte INQUIRY command: +[0x12][ |lu][pg cde][res ][al len][cntrl ] + +6 byte TEST UNIT READY command: +[0x00][ |lu][res ][res ][res ][res ] + +*/ + +#define INQ_REPLY_LEN 96 +#define INQ_CMD_LEN 6 +#define TUR_CMD_LEN 6 + +#define EBUFF_SZ 256 + +void my_io_callback(io_context_t ctx, struct iocb *iocb, long res, long res2) +{ + printf("my_io_callback: res=%ld, res2=%ld\n", res, res2); +} + +int main(int argc, char * argv[]) +{ + int sg_fd, k, ok; + unsigned char inqCmdBlk [INQ_CMD_LEN] = + {0x12, 0, 0, 0, INQ_REPLY_LEN, 0}; + unsigned char turCmdBlk [TUR_CMD_LEN] = + {0x00, 0, 0, 0, 0, 0}; + unsigned char inqBuff[INQ_REPLY_LEN]; + sg_io_hdr_t io_hdr; + char * file_name = 0; + char ebuff[EBUFF_SZ]; + unsigned char sense_buffer[32]; + int do_extra = 0; + + for (k = 1; k < argc; ++k) { + if (0 == memcmp("-x", argv[k], 2)) + do_extra = 1; + else if (*argv[k] == '-') { + printf("Unrecognized switch: %s\n", argv[k]); + file_name = 0; + break; + } + else if (0 == file_name) + file_name = argv[k]; + else { + printf("too many arguments\n"); + file_name = 0; + break; + } + } + if (0 == file_name) { + printf("Usage: 'sg_simple_aio [-x] <sg_device>'\n"); + return 1; + } + + /* An access mode of O_RDWR is required for write()/read() interface */ + if ((sg_fd = open(file_name, O_RDWR)) < 0) { + snprintf(ebuff, EBUFF_SZ, + "sg_simple_aio: error opening file: %s", file_name); + perror(ebuff); + return 1; + } + /* Just to be safe, check we have a new sg device by trying an ioctl */ + if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { + printf("sg_simple_aio: %s doesn't seem to be an new sg device\n", + file_name); + close(sg_fd); + return 1; + } + + /* Prepare INQUIRY command */ + memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof(inqCmdBlk); + /* io_hdr.iovec_count = 0; */ /* memset takes care of this */ + 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 */ + /* io_hdr.flags = 0; */ /* take defaults: indirect IO, etc */ + /* io_hdr.pack_id = 0; */ + /* io_hdr.usr_ptr = NULL; */ + +#if 1 + { + struct iocb a_iocb; + struct iocb * iocb_arr[1]; + io_context_t io_ctx; + int res; + + if (0 != (res = io_queue_init(1, &io_ctx))) { + printf("io_queue_init: failed %s\n", strerror(-res)); + close(sg_fd); + return 1; + } + iocb_arr[0] = &a_iocb; + io_prep_pwrite(iocb_arr[0], sg_fd, &io_hdr, sizeof(io_hdr), 0); + io_set_callback(iocb_arr[0], my_io_callback); + res = io_submit(io_ctx, 1, iocb_arr); + if (1 != res) { + printf("io_submit: returned %d\n", res); + close(sg_fd); + return 1; + } + } +#else + if (write(sg_fd, &io_hdr, sizeof(io_hdr)) < 0) { + perror("sg_simple_aio: Inquiry write error"); + close(sg_fd); + return 1; + } +#endif + /* sleep(3); */ + if (read(sg_fd, &io_hdr, sizeof(io_hdr)) < 0) { + perror("sg_simple_aio: Inquiry read error"); + close(sg_fd); + return 1; + } + + /* now for the error processing */ + ok = 0; + switch (sg_err_category3(&io_hdr)) { + case SG_ERR_CAT_CLEAN: + ok = 1; + break; + case SG_ERR_CAT_RECOVERED: + printf("Recovered error on INQUIRY, continuing\n"); + 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("Some of the INQUIRY command's results:\n"); + printf(" %.8s %.16s %.4s ", p + 8, p + 16, p + 32); + printf("[wide=%d sync=%d cmdque=%d sftre=%d]\n", + !!(f & 0x20), !!(f & 0x10), !!(f & 2), !!(f & 1)); + /* Extra info, not necessary to look at */ + if (do_extra) + printf("INQUIRY duration=%u millisecs, resid=%d, msg_status=%d\n", + io_hdr.duration, io_hdr.resid, (int)io_hdr.msg_status); + } + + /* Prepare TEST UNIT READY command */ + memset(&io_hdr, 0, sizeof(sg_io_hdr_t)); + io_hdr.interface_id = 'S'; + io_hdr.cmd_len = sizeof(turCmdBlk); + io_hdr.mx_sb_len = sizeof(sense_buffer); + io_hdr.dxfer_direction = SG_DXFER_NONE; + io_hdr.cmdp = turCmdBlk; + io_hdr.sbp = sense_buffer; + io_hdr.timeout = 20000; /* 20000 millisecs == 20 seconds */ + + if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { + perror("sg_simple_aio: Test Unit Ready SG_IO ioctl error"); + close(sg_fd); + return 1; + } + + /* now for the error processing */ + ok = 0; + switch (sg_err_category3(&io_hdr)) { + case SG_ERR_CAT_CLEAN: + ok = 1; + break; + case SG_ERR_CAT_RECOVERED: + printf("Recovered error on Test Unit Ready, continuing\n"); + ok = 1; + break; + default: /* won't bother decoding other categories */ + sg_chk_n_print3("Test Unit Ready command error", &io_hdr); + break; + } + + if (ok) + printf("Test Unit Ready successful so unit is ready!\n"); + else + printf("Test Unit Ready failed so unit may _not_ be ready!\n"); + + if (do_extra) + printf("TEST UNIT READY duration=%u millisecs, resid=%d, msg_status=%d\n", + io_hdr.duration, io_hdr.resid, (int)io_hdr.msg_status); + + close(sg_fd); + return 0; +} diff --git a/utils/Makefile b/utils/Makefile new file mode 100644 index 00000000..35060548 --- /dev/null +++ b/utils/Makefile @@ -0,0 +1,58 @@ +SHELL = /bin/sh + +PREFIX=/usr/local +INSTDIR=$(DESTDIR)/$(PREFIX)/bin +MANDIR=$(DESTDIR)/$(PREFIX)/man + +CC = gcc +LD = gcc + +EXECS = hxascdmp + +MAN_PGS = +MAN_PREF = man8 + +LARGE_FILE_FLAGS = -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 + +CFLAGS = -g -O2 -Wall -D_REENTRANT $(LARGE_FILE_FLAGS) +# CFLAGS = -g -O2 -Wall -D_REENTRANT -DSG_KERNEL_INCLUDES $(LARGE_FILE_FLAGS) +# CFLAGS = -g -O2 -Wall -pedantic -D_REENTRANT $(LARGE_FILE_FLAGS) + +LDFLAGS = + +all: $(EXECS) + +depend dep: + for i in *.c; do $(CC) $(INCLUDES) $(CFLAGS) -M $$i; \ + done > .depend + +clean: + /bin/rm -f *.o $(EXECS) core .depend + +hxascdmp: hxascdmp.o + $(LD) -o $@ $(LDFLAGS) $^ + + +install: $(EXECS) + install -d $(INSTDIR) + for name in $^; \ + do install -s -o root -g root -m 755 $$name $(INSTDIR); \ + done + install -d $(MANDIR)/$(MAN_PREF) + for mp in $(MAN_PGS); \ + do install -o root -g root -m 644 $$mp $(MANDIR)/$(MAN_PREF); \ + gzip -9f $(MANDIR)/$(MAN_PREF)/$$mp; \ + done + +uninstall: + dists="$(EXECS)"; \ + for name in $$dists; do \ + rm -f $(INSTDIR)/$$name; \ + done + for mp in $(MAN_PGS); do \ + rm -f $(MANDIR)/$(MAN_PREF)/$$mp.gz; \ + done + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/utils/hxascdmp.c b/utils/hxascdmp.c new file mode 100644 index 00000000..467e3a7c --- /dev/null +++ b/utils/hxascdmp.c @@ -0,0 +1,171 @@ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> + +#define DEF_BYTES_PER_LINE 16 + +static int bytes_per_line = DEF_BYTES_PER_LINE; + +#define CHARS_PER_HEX_BYTE 3 +#define BINARY_START_COL 6 +#define MAX_LINE_LENGTH 257 + + +static void dStrHex(const char* str, int len, long start) +{ + const char* p = str; + unsigned char c; + char buff[MAX_LINE_LENGTH]; + long a = start; + const int bpstart = BINARY_START_COL; + const int cpstart = BINARY_START_COL + + ((CHARS_PER_HEX_BYTE * bytes_per_line) + 1) + 5; + int cpos = cpstart; + int bpos = bpstart; + int midline_space = (bytes_per_line / 2) + 1; + int i, k, line_length; + + if (len <= 0) + return; + line_length = BINARY_START_COL + + (bytes_per_line * (1 + CHARS_PER_HEX_BYTE)) + 7; + if (line_length >= MAX_LINE_LENGTH) + { + fprintf(stderr, "bytes_per_line causes maximum line length of %d " + "to be exceeded\n", MAX_LINE_LENGTH); + return; + } + memset(buff, ' ', line_length); + buff[line_length] = '\0'; + k = sprintf(buff + 1, "%.2lx", a); + buff[k + 1] = ' '; + if (bpos >= ((bpstart + (midline_space * CHARS_PER_HEX_BYTE)))) + bpos++; + + for(i = 0; i < len; i++) + { + c = *p++; + bpos += CHARS_PER_HEX_BYTE; + if (bpos == (bpstart + (midline_space * CHARS_PER_HEX_BYTE))) + bpos++; + sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c); + buff[bpos + 2] = ' '; + if ((c < ' ') || (c >= 0x7f)) + c='.'; + buff[cpos++] = c; + if (cpos >= (cpstart + bytes_per_line)) + { + printf("%s\n", buff); + bpos = bpstart; + cpos = cpstart; + a += bytes_per_line; + memset(buff,' ', line_length); + k = sprintf(buff + 1, "%.2lx", a); + buff[k + 1] = ' '; + } + } + if (cpos > cpstart) + printf("%s\n", buff); +} + + +static void usage() +{ + fprintf(stderr, "Usage: hxascdmp [-b=<n>] [-h] [-?] [file+] \n"); + fprintf(stderr, " Sends hex ASCII dump of stdin/file to stdout\n"); + fprintf(stderr, " where:\n"); + fprintf(stderr, " -b=<n> bytes per line to display " + "(def: 16)\n"); + fprintf(stderr, " -h print this usage message\n"); + fprintf(stderr, " -? print this usage message\n"); + fprintf(stderr, " file reads file(s) and outputs it " + "as hex ASCII\n"); + fprintf(stderr, " if no files reads stdin\n"); +} + +int main(int argc, const char ** argv) +{ + char buff[8192]; + int num = 8192; + long start = 0; + int res, k, u; + int inFile = 0; /* stdin */ + int doHelp = 0; + int hasFilename = 0; + + for (k = 1; k < argc; k++) + { + if (0 == strncmp("-b=", argv[k], 3)) + { + res = sscanf(argv[k] + 3, "%d", &u); + if ((1 != res) || (u < 1)) + { + printf("Bad value after '-b' switch\n"); + usage(); + return 1; + } + bytes_per_line = u; + } + else if (0 == strcmp("-h", argv[k])) + doHelp = 1; + else if (0 == strcmp("-?", argv[k])) + doHelp = 1; + else if (*argv[k] == '-') + { + fprintf(stderr, "unknown switch: %s\n", argv[k]); + usage(); + return 1; + } + else + { + hasFilename = 1; + break; + } + } + if (doHelp) + { + usage(); + return 0; + } + + /* Make sure num to fetch is integral multiple of bytes_per_line */ + if (0 != (num % bytes_per_line)) + num = (num / bytes_per_line) * bytes_per_line; + + if (hasFilename) + { + for ( ; k < argc; k++) + { + inFile = open(argv[k], O_RDONLY); + if (inFile < 0) + { + fprintf(stderr, "Couldn't open file: %s\n", argv[k]); + } + else + { + start = 0; + printf("ASCII hex dump of file: %s\n", argv[k]); + while ((res = read(inFile, buff, num)) > 0) + { + dStrHex(buff, res, start); + start += (long)res; + } + } + close(inFile); + printf("\n"); + } + } + else + { + while ((res = read(inFile, buff, num)) > 0) + { + dStrHex(buff, res, start); + start += (long)res; + } + } + return 0; +} |