1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
/*
File: scsi-spin.c
A simple program to manually spin up and down a scsi device.
Copyright 1998 Rob Browning <rlb@cs.utexas.edu>
This source is covered by the terms the GNU Public License.
Some of the original code came from
The Linux SCSI programming HOWTO
Heiko Ei<DF>feldt heiko@colossus.escape.de
v1.5, 7 May 1996
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <scsi/sg.h>
#include <scsi/scsi.h>
#include <linux/major.h>
#include <sys/sysmacros.h>
#include <sys/stat.h>
static const int SCSI_CMD_SIZE = 18;
static const unsigned int scsi_off = sizeof(struct sg_header);
static unsigned char cmd[scsi_off + SCSI_CMD_SIZE]; /* SCSI command buffer */
/* process a complete SCSI cmd. Use the generic SCSI interface. */
static int handle_SCSI_cmd(const int fd,
const unsigned cmd_len, /* command length */
const unsigned in_size, /* input data size */
const unsigned char *i_buff, /* input buffer */
unsigned out_size, /* output data size */
unsigned char *o_buff /* output buffer */
) {
int status = 0;
struct sg_header *sg_hd;
/* safety checks */
if (!cmd_len) return -1; /* need a cmd_len != 0 */
if (!i_buff) return -1; /* need an input buffer != NULL */
#ifdef SG_BIG_BUFF
if (scsi_off + cmd_len + in_size > SG_BIG_BUFF) return -1;
if (scsi_off + out_size > SG_BIG_BUFF) return -1;
#else
if (scsi_off + cmd_len + in_size > 4096) return -1;
if (scsi_off + out_size > 4096) return -1;
#endif
if (!o_buff) out_size = 0; /* no output buffer, no output size */
/* generic SCSI device header construction */
sg_hd = (struct sg_header *) i_buff;
sg_hd->reply_len = scsi_off + out_size;
sg_hd->twelve_byte = cmd_len == 12;
sg_hd->result = 0;
#if 0
sg_hd->pack_len = scsi_off + cmd_len + in_size; /* not necessary */
sg_hd->pack_id; /* not used */
sg_hd->other_flags; /* not used */
#endif
/* send command */
status = write( fd, i_buff, scsi_off + cmd_len + in_size );
if ( status < 0 || status != scsi_off + cmd_len + in_size ||
sg_hd->result ) {
/* some error happened */
fprintf( stderr, "write(generic) result = 0x%x cmd = 0x%x\n",
sg_hd->result, i_buff[scsi_off] );
perror("");
return status;
}
if(!o_buff) o_buff = (unsigned char *) i_buff; /* buffer pointer check */
/* retrieve result */
status = read( fd, o_buff, scsi_off + out_size);
if ( status < 0 || status != scsi_off + out_size || sg_hd->result ) {
/* some error happened */
fprintf( stderr, "read(generic) status = 0x%x, result = 0x%x, "
"cmd = 0x%x\n",
status, sg_hd->result, o_buff[scsi_off] );
fprintf( stderr, "read(generic) sense "
"%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n",
sg_hd->sense_buffer[0], sg_hd->sense_buffer[1],
sg_hd->sense_buffer[2], sg_hd->sense_buffer[3],
sg_hd->sense_buffer[4], sg_hd->sense_buffer[5],
sg_hd->sense_buffer[6], sg_hd->sense_buffer[7],
sg_hd->sense_buffer[8], sg_hd->sense_buffer[9],
sg_hd->sense_buffer[10], sg_hd->sense_buffer[11],
sg_hd->sense_buffer[12], sg_hd->sense_buffer[13],
sg_hd->sense_buffer[14], sg_hd->sense_buffer[15]);
if (status < 0)
perror("");
}
/* Look if we got what we expected to get */
if (status == scsi_off + out_size) status = 0; /* got them all */
return status; /* 0 means no error */
}
#define START_STOP_CMDLEN 6
static void
scsi_spin(const int fd, const char desired_state) {
unsigned char cmdblk [ START_STOP_CMDLEN ] =
{ START_STOP, /* command */
0, /* lun(3 bits)/reserved(4 bits)/immed(1 bit) */
0, /* reserved */
0, /* reserved */
desired_state ? 1 : 0, /* reserved(6)/LoEj(1)/Start(1)*/
0 };/* reserved/flag/link */
memcpy(cmd + scsi_off, cmdblk, sizeof(cmdblk));
/*
* +------------------+
* | struct sg_header | <- cmd
* +------------------+
* | copy of cmdblk | <- cmd + scsi_off
* +------------------+
*/
if (handle_SCSI_cmd(fd, sizeof(cmdblk), 0, cmd, 0, NULL )) {
fprintf( stderr, "Inquiry failed\n" );
exit(2);
}
}
static void
usage() {
static char usage_string[] =
"usage: scsi-spin ( --up | --down ) device\n"
"--up spin up device.\n"
"--up spin down device.\n";
fputs(usage_string, stderr);
}
int
main(int argc, char *argv[]) {
int result = 0;
int fd;
int opt_up = 0;
int opt_down = 0;
struct option cmd_line_opts[] = {
{"up", 0, &opt_up, 1},
{"down", 0, &opt_down, 1},
{0, 0, 0, 0},
};
char c;
while((c = getopt_long(argc, argv, "", cmd_line_opts, NULL)) != EOF) {
if(c == ':') {
usage();
exit(1);
}
if(c == '?') {
usage();
exit(1);
}
}
if(opt_up && opt_down) {
fputs("scsi-spin: specified both --up and --down. "
"Is this some kind of test?\n", stderr);
usage();
exit(1);
}
if(!(opt_up || opt_down)) {
fputs("scsi-spin: must specify --up or --down.\n", stderr);
usage();
exit(1);
}
if(optind != (argc - 1)) {
usage();
exit(1);
}
fd = open(argv[optind], O_RDWR);
if (fd < 0) {
fprintf(stderr, "Failure opening %s\n", argv[optind]);
exit(1);
}
{
struct stat devstat;
if(stat(argv[optind], &devstat) == -1) {
fprintf(stderr, "stat on %s failed.\n", argv[optind]);
close(fd);
exit(1);
}
if(!S_ISCHR(devstat.st_mode) ||
major(devstat.st_rdev) != SCSI_GENERIC_MAJOR) {
fprintf(stderr, "%s is not a generic SCSI device.\n", argv[optind]);
close(fd);
exit(1);
}
}
if(opt_up)
scsi_spin(fd, 1);
else
scsi_spin(fd, 0);
close(fd);
return result;
}
|