File: scsi-spin.C

package info (click to toggle)
hwtools 0.5-0.2
  • links: PTS
  • area: main
  • in suites: potato
  • size: 1,304 kB
  • ctags: 1,213
  • sloc: ansic: 9,522; tcl: 2,140; asm: 802; makefile: 295; sh: 262; cpp: 160; csh: 42
file content (219 lines) | stat: -rw-r--r-- 6,132 bytes parent folder | download
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;
}