blob: 810a63cd252bc4afe8efb4fd85c96b3a5dbd4996 [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 code is does a SCSI READ CAPACITY command on the given device
and outputs the result.
* 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.
This program will only work with Linux 2.4 kernels and later (i.e.
those that support the SG_IO ioctl). Another version of this program
that should work on the 2.0, 2.2 and 2.4 series of Linux kernels no
matter which of those environments it was compiled and built under
can be found in the sg_utils package (e.g. sg_utils-1.02).
*/
static char * version_str = "3.60 20030430";
#define ME "sg_readcap: "
#define READCAP_TIMEOUT 60000 /* 60,000 milliseconds == 1 minute */
#define SENSE_BUFF_SZ 64
#define RCAP_REPLY_LEN 8
#define EBUFF_SZ 256
/* Performs a 10 byte READ CAPACITY command and fetches response. There is
* evidently a 16 byte READ CAPACITY command coming.
* Return of 0 -> success, -1 -> failure */
int do_readcap_10(int sg_fd, int pmi, unsigned int lba,
unsigned int * last_sect, unsigned int * sect_sz)
{
int res;
unsigned char rcCmdBlk[10] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned char rcBuff[RCAP_REPLY_LEN];
unsigned char sense_b[SENSE_BUFF_SZ];
sg_io_hdr_t io_hdr;
if (pmi) { /* lbs only valid when pmi set */
rcCmdBlk[8] |= 1;
rcCmdBlk[2] = (lba >> 24) & 0xff;
rcCmdBlk[3] = (lba >> 16) & 0xff;
rcCmdBlk[4] = (lba >> 8) & 0xff;
rcCmdBlk[5] = lba & 0xff;
}
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof(rcCmdBlk);
io_hdr.mx_sb_len = sizeof(sense_b);
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = sizeof(rcBuff);
io_hdr.dxferp = rcBuff;
io_hdr.cmdp = rcCmdBlk;
io_hdr.sbp = sense_b;
io_hdr.timeout = 60000;
while (1) {
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
perror("read_capacity (SG_IO) error");
return -1;
}
res = sg_err_category3(&io_hdr);
if (SG_ERR_CAT_MEDIA_CHANGED == res)
continue;
else if (SG_ERR_CAT_CLEAN != res) {
sg_chk_n_print3("READ CAPACITY command error", &io_hdr);
return -1;
}
else
break;
}
*last_sect = ((rcBuff[0] << 24) | (rcBuff[1] << 16) |
(rcBuff[2] << 8) | rcBuff[3]);
*sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) |
(rcBuff[6] << 8) | rcBuff[7];
return 0;
}
void usage ()
{
fprintf(stderr, "Usage: sg_readcap [-lba=<block>] [-pmi] [-V] "
"<device>\n"
" where -lba=<block>: yields the last block prior to (head "
"movement) delay\n"
" after <block> [in hex (def: 0) "
"valid with -pmi]\n"
" -pmi: partial medium indicator (without this switch "
"shows total\n"
" disk capacity\n"
" -V: output version string and exit\n");
}
int main(int argc, char * argv[])
{
int sg_fd, k, res, num;
unsigned int lba = 0;
int pmi = 0;
unsigned int last_blk_addr, block_size, u;
char ebuff[EBUFF_SZ];
const char * file_name = 0;
for (k = 1; k < argc; ++k) {
if (0 == strcmp("-pmi", argv[k]))
pmi = 1;
else if (0 == strncmp("-lba=", argv[k], 5)) {
num = sscanf(argv[k] + 5, "%x", &u);
if (1 != num) {
printf("Bad value after '-lba' switch\n");
file_name = 0;
break;
}
lba = u;
}
else if (0 == strcmp("-V", argv[k])) {
printf("Version string: %s\n", version_str);
exit(0);
}
else if (*argv[k] == '-') {
printf("Unrecognized switch: %s\n", argv[k]);
break;
}
else
file_name = argv[k];
}
if (0 == file_name) {
usage();
return 1;
}
if ((0 == pmi) && (lba > 0)) {
fprintf(stderr, ME "lba can only be non-zero when pmi is set\n");
usage();
return 1;
}
if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
snprintf(ebuff, EBUFF_SZ, ME "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( ME "%s doesn't seem to be a version 3 sg device\n",
file_name);
close(sg_fd);
return 1;
}
res = do_readcap_10(sg_fd, pmi, lba, &last_blk_addr, &block_size);
if (0 == res) {
printf("Read Capacity results:\n");
if (pmi)
printf(" PMI mode: given lba=0x%x, last block before "
"delay=0x%x\n", lba, last_blk_addr);
else
printf(" Last block address=%u (0x%x), Number of blocks=%u\n",
last_blk_addr, last_blk_addr, last_blk_addr + 1);
printf(" Block size = %u bytes\n", block_size);
}
close(sg_fd);
return 0;
}