blob: 35362a73d7c6e9f3b0c7d142a384a5d65942577c [file] [log] [blame]
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include "sg_include.h"
#include "sg_err.h"
/* This program is modeled on the example code in the SCSI Programming
HOWTO V1.5 by Heiko Eissfeldt dated 7 May 1996.
*
* Copyright (C) 1999-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.
Since this code has been used in the past to form the backbone of
some Linux apps based on the "sg" device driver, it has been
strengthened.
Start/Stop parameter by Kurt Garloff <garloff@suse.de>, 6/2000
Sync cache parameter by Kurt Garloff <garloff@suse.de>, 1/2001
Guard block device answering sg's ioctls. <dgilbert@interlog.com> 12/2002
Convert to SG_IO ioctl so can use sg or block devices in 2.5.* 3/2003
*/
static char * version_str = "0.35 20030506";
static int debug = 0;
#define START_STOP 0x1b
#define SYNCHRONIZE_CACHE 0x35
#define DEF_TIMEOUT 120000 /* 120,000 millisecs == 2 minutes */
static void do_start_stop(int fd, int start, int immed, int loej,
int power_conditions)
{
unsigned char cmdblk [6] = {
START_STOP, /* Command */
0, /* Resvd/Immed */
0, /* Reserved */
0, /* Reserved */
0, /* PowCond/Resvd/LoEj/Start */
0 }; /* Reserved/Flag/Link */
unsigned char sense_b[32];
sg_io_hdr_t io_hdr;
int k, res;
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
cmdblk[1] = immed & 1;
cmdblk[4] = ((power_conditions & 0xf) << 4) |
((loej & 1) << 1) | (start & 1);
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof(cmdblk);
io_hdr.mx_sb_len = sizeof(sense_b);
io_hdr.dxfer_direction = SG_DXFER_NONE;
io_hdr.dxfer_len = 0;
io_hdr.dxferp = NULL;
io_hdr.cmdp = cmdblk;
io_hdr.sbp = sense_b;
io_hdr.timeout = DEF_TIMEOUT;
if (debug) {
printf(" Start/Stop command:");
for (k = 0; k < 6; ++k)
printf (" %02x", cmdblk[k]);
printf("\n");
}
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
perror("start_stop (SG_IO) error");
return;
}
res = sg_err_category3(&io_hdr);
if (SG_ERR_CAT_MEDIA_CHANGED == res) {
fprintf(stderr, "media change report, try start_stop again\n");
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
perror("start_stop (SG_IO) error");
return;
}
}
if (SG_ERR_CAT_CLEAN != res) {
sg_chk_n_print3("start_stop", &io_hdr);
return;
}
if (debug)
fprintf(stderr, "start_stop [%s] successful\n",
start ? "start" : "stop");
}
static void do_sync_cache(int fd)
{
unsigned char cmdblk [ 10 ] = {
SYNCHRONIZE_CACHE, /* Command */
0, /* Immed (2) */
0, 0, 0, 0, /* LBA */
0, /* Reserved */
0, 0, /* No of blocks */
0 }; /* Reserved/Flag/Link */
unsigned char sense_b[32];
sg_io_hdr_t io_hdr;
int res;
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof(cmdblk);
io_hdr.mx_sb_len = sizeof(sense_b);
io_hdr.dxfer_direction = SG_DXFER_NONE;
io_hdr.dxfer_len = 0;
io_hdr.dxferp = NULL;
io_hdr.cmdp = cmdblk;
io_hdr.sbp = sense_b;
io_hdr.timeout = DEF_TIMEOUT;
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
perror("sync_cache (SG_IO) error");
return;
}
res = sg_err_category3(&io_hdr);
if (SG_ERR_CAT_MEDIA_CHANGED == res) {
fprintf(stderr, "media change report, try sync_cache again\n");
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
perror("sync_cache (SG_IO) error");
return;
}
}
if (SG_ERR_CAT_CLEAN != res) {
sg_chk_n_print3("sync_cache", &io_hdr);
return;
}
if (debug)
fprintf(stderr, "synchronize cache successful\n");
}
void usage ()
{
fprintf(stderr, "Usage: sg_start [-d] [-imm=0|1] [-loej] [-pc=<n>] "
"[-s] [0|1] <device>\n"
" -d: output debug\n"
" -imm=0|1: 0->await completion, 1->return "
"immediately(def)\n"
" -loej: load on start, eject of stop\n"
" -pc=<n>: power conditions (in hex, default 0)\n"
" -s: send the synchronize cache command before start/stop\n"
" -V: print version string then exit\n"
" 0: stop (spin-down)\n"
" 1: start (spin-up)\n"
" <device> sg or block device (latter in lk 2.5.*)\n"
" Example: sg_start /dev/sg2 1\n");
exit (1);
}
int main(int argc, char * argv[])
{
char **argptr;
int startstop = -1, synccache = 0;
char * file_name = 0;
int k, fd, num;
unsigned int u;
int immed = 1;
int loej = 0;
int power_conds = 0;
if (argc < 2)
usage ();
for (k = 1; k < argc; ++k) {
argptr = argv + k;
if (!strcmp (*argptr, "-d"))
debug = 1;
else if (!strcmp (*argptr, "-loej"))
loej = 1;
else if (!strcmp (*argptr, "-s"))
synccache = 1;
else if (0 == strncmp("-imm=", argv[k], 5)) {
num = sscanf(argv[k] + 5, "%x", &u);
if ((1 != num) || (u > 1)) {
printf("Bad value after '-imm' switch\n");
file_name = 0;
break;
}
immed = u;
}
else if (0 == strncmp("-pc=", argv[k], 4)) {
num = sscanf(argv[k] + 4, "%x", &u);
if ((1 != num) || (u > 15)) {
printf("Bad value after '-pc' switch\n");
file_name = 0;
break;
}
power_conds = u;
}
else if (!strcmp (*argptr, "-V")) {
printf("Version string: %s\n", version_str);
exit(0);
}
else if (!strcmp (*argptr, "0"))
startstop = 0;
else if (!strcmp (*argptr, "1"))
startstop = 1;
else if (*argv[k] == '-') {
fprintf(stderr, "Unrecognized switch: %s\n", argv[k]);
file_name = 0;
break;
}
else if (0 == file_name)
file_name = argv[k];
else {
fprintf(stderr, "too many arguments\n");
file_name = 0;
break;
}
}
if (0 == file_name) {
usage();
return 1;
}
if (!synccache && (startstop == -1) && (0 == power_conds))
usage ();
fd = open(file_name, O_RDWR | O_NONBLOCK);
if (fd < 0) {
fprintf(stderr, "Error trying to open %s\n", file_name);
perror("");
usage();
return 2;
}
if (ioctl(fd, SG_GET_TIMEOUT, 0) < 0) {
fprintf( stderr, "Given file not block or SCSI "
"generic device\n" );
close(fd);
return 3;
}
if (synccache)
do_sync_cache(fd);
if (power_conds > 0)
do_start_stop(fd, 0, immed, 0, power_conds);
else if (startstop != -1)
do_start_stop(fd, startstop, immed, loej, 0);
close (fd);
return 0;
}