blob: 7ed63b36802ea961e0b4bf4ab06bc5fd5f645ac0 [file] [log] [blame]
/* sg_test_rwbuf.c */
/*
* Program to test the SCSI host adapter by issueing
* write and read operations on a device's buffer
* and calculating checksums.
* NOTE: If you can not reserve the buffer of the device
* for this purpose (SG_GET_RESERVED_SIZE), you risk
* serious data corruption, if the device is accessed by
* somebody else in the meantime.
* (c) 2000 Kurt Garloff <garloff@suse.de>
* heavily based on Doug Gilbert's sg_rbuf program.
* (c) 1999 Doug 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.
*
* $Id: sg_test_rwbuf.c,v 1.1 2000/03/02 13:50:03 garloff Exp $
*/
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include "sg_include.h"
#include "sg_err.h"
#define BPI (signed)(sizeof(int))
#define OFF sizeof(struct sg_header)
#define RB_MODE_DESC 3
#define RB_MODE_DATA 2
#define RB_DESC_LEN 4
int base = 0x12345678;
int buf_capacity = 0;
int buf_granul = 255;
const unsigned char rbCmdBlk [10] = {READ_BUFFER, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int ln;
char *file_name = 0;
unsigned char *cmpbuf = 0;
/* Options */
char do_quick = 0;
int addwrite = 0;
int addread = 0;
int find_out_about_buffer (int sg_fd)
{
int res;
unsigned char * rbBuff = malloc(OFF + sizeof(rbCmdBlk) + 512);
struct sg_header * rsghp = (struct sg_header *)rbBuff;
int rbInLen = OFF + RB_DESC_LEN;
int rbOutLen = OFF + sizeof(rbCmdBlk);
unsigned char * buffp = rbBuff + OFF;
rsghp->pack_len = 0; /* don't care */
rsghp->pack_id = 0;
rsghp->reply_len = rbInLen;
rsghp->twelve_byte = 0;
rsghp->result = 0;
#ifndef SG_GET_RESERVED_SIZE
rsghp->sense_buffer[0] = 0;
#endif
memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk));
rbBuff[OFF + 1] = RB_MODE_DESC;
rbBuff[OFF + 8] = RB_DESC_LEN;
res = write(sg_fd, rbBuff, rbOutLen);
if (res < 0) {
perror("sg_test_rwbuf: write (desc) error");
if (rbBuff) free(rbBuff);
return 1;
}
if (res < rbOutLen) {
printf("sg_test_rwbuf: wrote less (desc), ask=%d, got=%d\n", rbOutLen, res);
if (rbBuff) free(rbBuff);
return 1;
}
memset(rbBuff + OFF, 0, RB_DESC_LEN);
res = read(sg_fd, rbBuff, rbInLen);
if (res < 0) {
perror("sg_test_rwbuf: read (desc) error");
if (rbBuff) free(rbBuff);
return 1;
}
if (res < rbInLen) {
printf("sg_test_rwbuf: read less (desc), ask=%d, got=%d\n", rbInLen, res);
if (rbBuff) free(rbBuff);
return 1;
}
#ifdef SG_GET_RESERVED_SIZE
if (! sg_chk_n_print("sg_test_rwbuf: desc", rsghp->target_status,
rsghp->host_status, rsghp->driver_status,
rsghp->sense_buffer, SG_MAX_SENSE)) {
printf("sg_test_rwbuf: perhaps %s doesn't support READ BUFFER\n",
file_name);
if (rbBuff) free(rbBuff);
return 1;
}
#else
if ((rsghp->result != 0) || (0 != rsghp->sense_buffer[0])) {
printf("sg_test_rwbuf: read(desc) result=%d\n", rsghp->result);
if (0 != rsghp->sense_buffer[0])
sg_print_sense("sg_test_rwbuf: desc", rsghp->sense_buffer,
SG_MAX_SENSE);
printf("sg_test_rwbuf: perhaps %s doesn't support READ BUFFER\n",
file_name);
if (rbBuff) free(rbBuff);
return 1;
}
#endif
buf_capacity = ((buffp[1] << 16) | (buffp[2] << 8) | buffp[3]);
buf_granul = (unsigned char)buffp[0];
#if 0
printf("READ BUFFER reports: %02x %02x %02x %02x %02x %02x %02x %02x\n",
buffp[0], buffp[1], buffp[2], buffp[3],
buffp[4], buffp[5], buffp[6], buffp[7]);
#endif
printf("READ BUFFER reports: buffer capacity=%d, offset boundary=%d\n",
buf_capacity, buf_granul);
#ifdef SG_DEF_RESERVED_SIZE
res = ioctl(sg_fd, SG_SET_RESERVED_SIZE, &buf_capacity);
if (res < 0)
perror("sg_test_rwbuf: SG_SET_RESERVED_SIZE error");
#endif
return 0;
}
int mymemcmp (unsigned char *bf1, unsigned char *bf2, int len)
{
int df;
for (df = 0; df < len; df++)
if (bf1[df] != bf2[df]) return df;
return 0;
}
int do_checksum (int *buf, int len, int quiet)
{
int sum = base;
int i; int rln = len;
for (i = 0; i < len/BPI; i++)
sum += buf[i];
while (rln%BPI) sum += ((char*)buf)[--rln];
if (sum != 0x12345678) {
if (!quiet) printf ("sg_test_rwbuf: Checksum error (sz=%i): %08x\n",
len, sum);
if (cmpbuf && !quiet) {
int diff = mymemcmp (cmpbuf, (unsigned char*)buf, len);
printf ("Differ at pos %i/%i:\n", diff, len);
for (i = 0; i < 24 && i+diff < len; i++)
printf (" %02x", cmpbuf[i+diff]);
printf ("\n");
for (i = 0; i < 24 && i+diff < len; i++)
printf (" %02x", ((unsigned char*)buf)[i+diff]);
printf ("\n");
}
return 2;
}
else return 0;
}
void do_fill_buffer (int *buf, int len)
{
int sum;
int i; int rln = len;
srand (time (0));
retry:
if (len >= BPI)
base = 0x12345678 + rand ();
else
base = 0x12345678 + (char) rand ();
sum = base;
for (i = 0; i < len/BPI - 1; i++)
{
/* we rely on rand() giving full range of int */
buf[i] = rand ();
sum += buf[i];
}
while (rln%BPI)
{
((char*)buf)[--rln] = rand ();
sum += ((char*)buf)[rln];
}
if (len >= BPI) buf[len/BPI - 1] = 0x12345678 - sum;
else ((char*)buf)[0] = 0x12345678 + ((char*)buf)[0] - sum;
if (do_checksum (buf, len, 1)) {
if (len < BPI) goto retry;
printf ("sg_test_rwbuf: Memory corruption?\n");
exit (1);
}
if (cmpbuf) memcpy (cmpbuf, (char*)buf, len);
}
int read_buffer (int sg_fd, unsigned size)
{
int res;
unsigned char * rbBuff = malloc(OFF + sizeof(rbCmdBlk) + size + addread);
struct sg_header * rsghp = (struct sg_header *)rbBuff;
int rbInLen = OFF + size + addread;
int rbOutLen = OFF + sizeof (rbCmdBlk);
memset(rbBuff, 0, OFF + sizeof(rbCmdBlk) + size + addread);
rsghp->pack_len = 0; /* don't care */
rsghp->reply_len = rbInLen;
rsghp->twelve_byte = 0;
rsghp->result = 0;
memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk));
rbBuff[OFF + 1] = RB_MODE_DATA;
rbBuff[OFF + 6] = 0xff & ((size+addread) >> 16);
rbBuff[OFF + 7] = 0xff & ((size+addread) >> 8);
rbBuff[OFF + 8] = 0xff & (size+addread);
rsghp->pack_id = 2;
res = write(sg_fd, rbBuff, rbOutLen);
if (res < 0) {
perror("sg_test_rwbuf: write (data) error");
if (rbBuff) free(rbBuff);
return 1;
}
if (res < rbOutLen) {
printf("sg_test_rwbuf: wrote less (data), ask=%d, got=%d\n",
rbOutLen, res);
if (rbBuff) free(rbBuff);
return 1;
}
res = read(sg_fd, rbBuff, rbInLen);
if (res < 0) {
perror("sg_test_rwbuf: read (data) error");
if (rbBuff) free(rbBuff);
return 1;
}
if (res < rbInLen) {
printf("sg_test_rwbuf: read less (data), ask=%d, got=%d\n",
rbInLen, res);
if (rbBuff) free(rbBuff);
return 1;
}
res = do_checksum ((int*)(rbBuff + OFF), size, 0);
if (rbBuff) free(rbBuff);
return res;
}
int write_buffer (int sg_fd, unsigned size)
{
int res;
unsigned char * rbBuff = malloc(OFF + sizeof(rbCmdBlk) + size + addwrite);
struct sg_header * rsghp = (struct sg_header *)rbBuff;
//unsigned char * buffp = rbBuff + OFF;
int rbInLen = OFF;
int rbOutLen = OFF + sizeof (rbCmdBlk) + size + addwrite;
if (addwrite) memset(rbBuff + OFF + sizeof(rbCmdBlk) + size, 0x5a, addwrite);
do_fill_buffer ((int*)(rbBuff + OFF + sizeof(rbCmdBlk)), size);
rsghp->pack_len = 0; /* don't care */
rsghp->reply_len = rbInLen;
rsghp->twelve_byte = 0;
rsghp->result = 0;
memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk));
rbBuff[OFF + 0] = WRITE_BUFFER;
rbBuff[OFF + 1] = RB_MODE_DATA;
rbBuff[OFF + 6] = 0xff & ((size+addwrite) >> 16);
rbBuff[OFF + 7] = 0xff & ((size+addwrite) >> 8);
rbBuff[OFF + 8] = 0xff & (size+addwrite);
rsghp->pack_id = 1;
res = write(sg_fd, rbBuff, rbOutLen);
if (res < 0) {
perror("sg_test_rwbuf: write (data) error");
if (rbBuff) free(rbBuff);
return 1;
}
if (res < rbOutLen) {
printf("sg_test_rwbuf: wrote less (data), ask=%d, got=%d\n",
rbOutLen, res);
if (rbBuff) free(rbBuff);
return 1;
}
res = read(sg_fd, rbBuff, rbInLen);
if (res < 0) {
perror("sg_test_rwbuf: read (status) error");
if (rbBuff) free(rbBuff);
return 1;
}
if (rbBuff) free(rbBuff);
return 0;
}
void usage ()
{
printf ("Usage: sg_test_rwbuf /dev/sgX sz [addwr] [addrd]\n");
printf ("sg_test_rwbuf writes and reads back sz bytes to the internal buffer of\n");
printf (" device /dev/sgX. For testing purposes, you can ask it to write\n");
printf (" (addwr) or read (addrd) some more bytes.\n");
printf ("WARNING: If you access the device at the same time, e.g. because it's a\n");
printf (" mounted hard disk, the device's buffer may be used by the device itself\n");
printf (" for other data at the same time, and overwriting it may or may not\n");
printf (" cause data corruption!\n");
printf ("(c) Douglas Gilbert, Kurt Garloff, 2000, GNU GPL\n");
exit (1);
}
void parseargs (int argc, char *argv[])
{
if (argc < 3) usage ();
file_name = argv[1];
ln = atol (argv[2]);
if (argc > 3) addwrite = atol (argv[3]);
if (argc > 4) addread = atol (argv[4]);
}
int main (int argc, char * argv[])
{
int sg_fd; int res;
struct stat a_st;
int block_dev = 0;
parseargs (argc, argv);
sg_fd = open(file_name, O_RDWR);
if (sg_fd < 0) {
perror("sg_test_rwbuf: open error");
return 1;
}
if (fstat(sg_fd, &a_st) < 0) {
fprintf(stderr, "could do fstat() on fd ??\n");
close(sg_fd);
return 1;
}
if (S_ISBLK(a_st.st_mode))
block_dev = 1;
/* Don't worry, being very careful not to write to a none-sg file ... */
if (block_dev || (ioctl(sg_fd, SG_GET_TIMEOUT, 0) < 0)) {
/* perror("ioctl on generic device, error"); */
printf("sg_test_rwbuf: not a sg device, or wrong driver\n");
return 1;
}
if (find_out_about_buffer (sg_fd)) return 1;
if (ln > buf_capacity) {
printf ("sg_test_rwbuf: sz=%i > buf_capacity=%i!\n",
ln, buf_capacity);
exit (2);
}
cmpbuf = malloc (ln);
if (write_buffer (sg_fd, ln)) return 3;
res = read_buffer (sg_fd, ln);
if (res) return (res + 4);
res = close(sg_fd);
if (res < 0) {
perror("sg_test_rwbuf: close error");
return 6;
}
printf ("Success\n");
return 0;
}