3 changed files with 874 additions and 0 deletions
@ -0,0 +1,152 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 06/2010 by Olaf Rempel * |
||||
* razzor@kopf-tisch.de * |
||||
* * |
||||
* 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 of the License, or * |
||||
* (at your option) any later version. * |
||||
* * |
||||
* This program is distributed in the hope that it will be useful, * |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of * |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
||||
* GNU General Public License for more details. * |
||||
* * |
||||
* You should have received a copy of the GNU General Public License * |
||||
* along with this program; if not, write to the * |
||||
* Free Software Foundation, Inc., * |
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
||||
***************************************************************************/ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <unistd.h> |
||||
#include <string.h> |
||||
#include <stdint.h> |
||||
|
||||
#include <sys/types.h> |
||||
#include <sys/stat.h> |
||||
#include <fcntl.h> |
||||
|
||||
#include "configfile.h" |
||||
#include "logging.h" |
||||
#include "plugins.h" |
||||
#include "probe.h" |
||||
|
||||
#include "sgio.h" |
||||
int verbose = 0; |
||||
int prefer_ata12 = 0; |
||||
|
||||
|
||||
struct sammler_plugin plugin; |
||||
|
||||
struct device_entry { |
||||
struct list_head list; |
||||
char *devpath; |
||||
}; |
||||
|
||||
static LIST_HEAD(device_list); |
||||
|
||||
static const char *ds_def = { |
||||
"DS:active:GAUGE:90:0:1 " |
||||
}; |
||||
|
||||
static const char * get_ds(int ds_id) |
||||
{ |
||||
return ds_def; |
||||
} |
||||
|
||||
static int probe(void) |
||||
{ |
||||
struct device_entry *entry; |
||||
list_for_each_entry(entry, &device_list, list) { |
||||
|
||||
int fd = open(entry->devpath, O_RDONLY|O_NONBLOCK); |
||||
if (fd < 0) { |
||||
log_print(LOG_ERROR, "%s: failed to open %s", plugin.name, entry->devpath); |
||||
continue; |
||||
} |
||||
|
||||
uint8_t args[4] = { ATA_OP_CHECKPOWERMODE1, 0, 0, 0 }; |
||||
|
||||
if (do_drive_cmd(fd, args)) { |
||||
args[0] = ATA_OP_CHECKPOWERMODE2; |
||||
if (do_drive_cmd(fd, args)) { |
||||
log_print(LOG_ERROR, "%s: failed to query %s", plugin.name, entry->devpath); |
||||
close(fd); |
||||
continue; |
||||
} |
||||
} |
||||
/*
|
||||
switch (args[2]) { |
||||
case 0x00: state = "standby"; break; |
||||
case 0x40: state = "NVcache_spindown"; break; |
||||
case 0x41: state = "NVcache_spinup"; break; |
||||
case 0x80: state = "idle"; break; |
||||
case 0xff: state = "active/idle"; break; |
||||
default: state = "unknown"; break; |
||||
} |
||||
*/ |
||||
char *dev = strrchr(entry->devpath, '/'); |
||||
if (dev == NULL) { |
||||
dev = entry->devpath; |
||||
} else { |
||||
dev++; |
||||
} |
||||
|
||||
char filename[32]; |
||||
int len = snprintf(filename, sizeof(filename), "diskstandby-%s.rrd", dev); |
||||
if (len < 0 || len >= sizeof(filename)) |
||||
continue; |
||||
|
||||
probe_submit(&plugin, filename, 0, "%u", (args[2] == 0xff) ? 1 : 0); |
||||
close(fd); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int init_cb(struct strtoken *tokens, void *privdata) |
||||
{ |
||||
if (tokens->count != 1) { |
||||
log_print(LOG_ERROR, "%s: parse error", plugin.name); |
||||
return -1; |
||||
} |
||||
|
||||
struct device_entry *entry = malloc(sizeof(struct device_entry)); |
||||
if (entry == NULL) { |
||||
log_print(LOG_ERROR, "%s: out of memory", plugin.name); |
||||
return -1; |
||||
} |
||||
|
||||
entry->devpath = strdup(tokens->field[0]); |
||||
|
||||
log_print(LOG_INFO, "%s: added server '%s'", plugin.name, entry->devpath); |
||||
list_add_tail(&entry->list, &device_list); |
||||
return 0; |
||||
} |
||||
|
||||
static int init(void) |
||||
{ |
||||
config_get_strtokens("p_diskstandby", "device", ",", 1, init_cb, NULL); |
||||
return 0; |
||||
} |
||||
|
||||
static int fini(void) |
||||
{ |
||||
struct device_entry *entry, *tmp; |
||||
list_for_each_entry_safe(entry, tmp, &device_list, list) { |
||||
free(entry->devpath); |
||||
free(entry); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
struct sammler_plugin plugin = { |
||||
.name = "diskstandby", |
||||
.interval = 60, |
||||
.init = &init, |
||||
.fini = &fini, |
||||
.probe = &probe, |
||||
.get_ds = &get_ds, |
||||
}; |
@ -0,0 +1,492 @@
|
||||
/* sgio.c - by Mark Lord (C) 2007 -- freely distributable */ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <unistd.h> |
||||
#include <fcntl.h> |
||||
#include <errno.h> |
||||
#include <string.h> |
||||
#include <sys/ioctl.h> |
||||
#include <sys/stat.h> |
||||
#include <sys/types.h> |
||||
|
||||
#include <scsi/scsi.h> |
||||
#include <scsi/sg.h> |
||||
|
||||
#include "sgio.h" |
||||
//#include "hdparm.h"
|
||||
|
||||
#include <linux/hdreg.h> |
||||
|
||||
extern int verbose; |
||||
extern int prefer_ata12; |
||||
|
||||
/*
|
||||
* Taskfile layout for SG_ATA_16 cdb: |
||||
* |
||||
* LBA48: |
||||
* cdb[ 3] = hob_feat |
||||
* cdb[ 5] = hob_nsect |
||||
* cdb[ 7] = hob_lbal |
||||
* cdb[ 9] = hob_lbam |
||||
* cdb[11] = hob_lbah |
||||
* |
||||
* LBA28/LBA48: |
||||
* cdb[ 4] = feat |
||||
* cdb[ 6] = nsect |
||||
* cdb[ 8] = lbal |
||||
* cdb[10] = lbam |
||||
* cdb[12] = lbah |
||||
* cdb[13] = device |
||||
* cdb[14] = command |
||||
* |
||||
* Taskfile layout for SG_ATA_12 cdb: |
||||
* |
||||
* cdb[ 3] = feat |
||||
* cdb[ 4] = nsect |
||||
* cdb[ 5] = lbal |
||||
* cdb[ 6] = lbam |
||||
* cdb[ 7] = lbah |
||||
* cdb[ 8] = device |
||||
* cdb[ 9] = command |
||||
* |
||||
* dxfer_direction choices: |
||||
* SG_DXFER_TO_DEV, SG_DXFER_FROM_DEV, SG_DXFER_NONE |
||||
*/ |
||||
|
||||
static inline int needs_lba48 (__u8 ata_op, __u64 lba, unsigned int nsect) |
||||
{ |
||||
const __u64 lba28_limit = (1<<28) - 1; |
||||
|
||||
switch (ata_op) { |
||||
case ATA_OP_READ_PIO_EXT: |
||||
case ATA_OP_READ_DMA_EXT: |
||||
case ATA_OP_WRITE_PIO_EXT: |
||||
case ATA_OP_WRITE_DMA_EXT: |
||||
case ATA_OP_READ_VERIFY_EXT: |
||||
case ATA_OP_WRITE_UNC_EXT: |
||||
case ATA_OP_READ_NATIVE_MAX_EXT: |
||||
case ATA_OP_SET_MAX_EXT: |
||||
case ATA_OP_FLUSHCACHE_EXT: |
||||
return 1; |
||||
} |
||||
if (lba >= lba28_limit) |
||||
return 1; |
||||
if (nsect) { |
||||
if (nsect > 0xff) |
||||
return 1; |
||||
if ((lba + nsect - 1) >= lba28_limit) |
||||
return 1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
void tf_init (struct ata_tf *tf, __u8 ata_op, __u64 lba, unsigned int nsect) |
||||
{ |
||||
memset(tf, 0, sizeof(*tf)); |
||||
tf->command = ata_op; |
||||
tf->dev = ATA_USING_LBA; |
||||
tf->lob.lbal = lba; |
||||
tf->lob.lbam = lba >> 8; |
||||
tf->lob.lbah = lba >> 16; |
||||
tf->lob.nsect = nsect; |
||||
if (needs_lba48(ata_op, lba, nsect)) { |
||||
tf->is_lba48 = 1; |
||||
tf->hob.nsect = nsect >> 8; |
||||
tf->hob.lbal = lba >> 24; |
||||
tf->hob.lbam = lba >> 32; |
||||
tf->hob.lbah = lba >> 40; |
||||
} else { |
||||
tf->dev |= (lba >> 24) & 0x0f; |
||||
} |
||||
} |
||||
|
||||
#ifdef SG_IO |
||||
|
||||
__u64 tf_to_lba (struct ata_tf *tf) |
||||
{ |
||||
__u32 lba24, lbah; |
||||
__u64 lba64; |
||||
|
||||
lba24 = (tf->lob.lbah << 16) | (tf->lob.lbam << 8) | (tf->lob.lbal); |
||||
if (tf->is_lba48) |
||||
lbah = (tf->hob.lbah << 16) | (tf->hob.lbam << 8) | (tf->hob.lbal); |
||||
else |
||||
lbah = (tf->dev & 0x0f); |
||||
lba64 = (((__u64)lbah) << 24) | (__u64)lba24; |
||||
return lba64; |
||||
} |
||||
|
||||
enum { |
||||
SG_CDB2_TLEN_NODATA = 0 << 0, |
||||
SG_CDB2_TLEN_FEAT = 1 << 0, |
||||
SG_CDB2_TLEN_NSECT = 2 << 0, |
||||
|
||||
SG_CDB2_TLEN_BYTES = 0 << 2, |
||||
SG_CDB2_TLEN_SECTORS = 1 << 2, |
||||
|
||||
SG_CDB2_TDIR_TO_DEV = 0 << 3, |
||||
SG_CDB2_TDIR_FROM_DEV = 1 << 3, |
||||
|
||||
SG_CDB2_CHECK_COND = 1 << 5, |
||||
}; |
||||
|
||||
static void dump_bytes (const char *prefix, unsigned char *p, int len) |
||||
{ |
||||
int i; |
||||
|
||||
if (prefix) |
||||
fprintf(stderr, "%s: ", prefix); |
||||
for (i = 0; i < len; ++i) |
||||
fprintf(stderr, " %02x", p[i]); |
||||
fprintf(stderr, "\n"); |
||||
} |
||||
|
||||
int sg16 (int fd, int rw, int dma, struct ata_tf *tf, |
||||
void *data, unsigned int data_bytes, unsigned int timeout_secs) |
||||
{ |
||||
unsigned char cdb[SG_ATA_16_LEN]; |
||||
unsigned char sb[32], *desc; |
||||
struct scsi_sg_io_hdr io_hdr; |
||||
|
||||
memset(&cdb, 0, sizeof(cdb)); |
||||
memset(&sb, 0, sizeof(sb)); |
||||
memset(&io_hdr, 0, sizeof(struct scsi_sg_io_hdr)); |
||||
|
||||
if (dma) { |
||||
//cdb[1] = data ? (rw ? SG_ATA_PROTO_UDMA_OUT : SG_ATA_PROTO_UDMA_IN) : SG_ATA_PROTO_NON_DATA;
|
||||
cdb[1] = data ? SG_ATA_PROTO_DMA : SG_ATA_PROTO_NON_DATA; |
||||
} else { |
||||
cdb[1] = data ? (rw ? SG_ATA_PROTO_PIO_OUT : SG_ATA_PROTO_PIO_IN) : SG_ATA_PROTO_NON_DATA; |
||||
} |
||||
cdb[ 2] = SG_CDB2_CHECK_COND; |
||||
if (data) { |
||||
cdb[2] |= SG_CDB2_TLEN_NSECT | SG_CDB2_TLEN_SECTORS; |
||||
cdb[2] |= rw ? SG_CDB2_TDIR_TO_DEV : SG_CDB2_TDIR_FROM_DEV; |
||||
} |
||||
|
||||
if (!prefer_ata12 || tf->is_lba48) { |
||||
cdb[ 0] = SG_ATA_16; |
||||
cdb[ 4] = tf->lob.feat; |
||||
cdb[ 6] = tf->lob.nsect; |
||||
cdb[ 8] = tf->lob.lbal; |
||||
cdb[10] = tf->lob.lbam; |
||||
cdb[12] = tf->lob.lbah; |
||||
cdb[13] = tf->dev; |
||||
cdb[14] = tf->command; |
||||
if (tf->is_lba48) { |
||||
cdb[ 1] |= SG_ATA_LBA48; |
||||
cdb[ 3] = tf->hob.feat; |
||||
cdb[ 5] = tf->hob.nsect; |
||||
cdb[ 7] = tf->hob.lbal; |
||||
cdb[ 9] = tf->hob.lbam; |
||||
cdb[11] = tf->hob.lbah; |
||||
} |
||||
io_hdr.cmd_len = SG_ATA_16_LEN; |
||||
} else { |
||||
cdb[ 0] = SG_ATA_12; |
||||
cdb[ 3] = tf->lob.feat; |
||||
cdb[ 4] = tf->lob.nsect; |
||||
cdb[ 5] = tf->lob.lbal; |
||||
cdb[ 6] = tf->lob.lbam; |
||||
cdb[ 7] = tf->lob.lbah; |
||||
cdb[ 8] = tf->dev; |
||||
cdb[ 9] = tf->command; |
||||
io_hdr.cmd_len = SG_ATA_12_LEN; |
||||
} |
||||
|
||||
io_hdr.interface_id = 'S'; |
||||
io_hdr.mx_sb_len = sizeof(sb); |
||||
io_hdr.dxfer_direction = data ? (rw ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV) : SG_DXFER_NONE; |
||||
io_hdr.dxfer_len = data ? data_bytes : 0; |
||||
io_hdr.dxferp = data; |
||||
io_hdr.cmdp = cdb; |
||||
io_hdr.sbp = sb; |
||||
io_hdr.pack_id = tf_to_lba(tf); |
||||
io_hdr.timeout = (timeout_secs ? timeout_secs : 5) * 1000; /* msecs */ |
||||
|
||||
if (verbose) |
||||
dump_bytes("outgoing cdb", cdb, sizeof(cdb)); |
||||
if (ioctl(fd, SG_IO, &io_hdr) == -1) { |
||||
if (verbose) |
||||
perror("ioctl(fd,SG_IO)"); |
||||
return -1; /* SG_IO not supported */ |
||||
} |
||||
if (verbose) |
||||
fprintf(stderr, "SG_IO: ATA_%u status=0x%x, host_status=0x%x, driver_status=0x%x\n", |
||||
io_hdr.cmd_len, io_hdr.status, io_hdr.host_status, io_hdr.driver_status); |
||||
|
||||
if (io_hdr.host_status || io_hdr.driver_status != SG_DRIVER_SENSE |
||||
|| (io_hdr.status && io_hdr.status != SG_CHECK_CONDITION)) |
||||
{ |
||||
if (verbose) |
||||
fprintf(stderr, "SG_IO: bad response (not CHECK_CONDITION)\n"); |
||||
errno = EBADE; |
||||
return -1; |
||||
} |
||||
|
||||
desc = sb + 8; |
||||
if (sb[0] != 0x72 || sb[7] < 14 || desc[0] != 0x09 || desc[1] < 0x0c) { |
||||
if (verbose) |
||||
dump_bytes("SG_IO: bad/missing sense data, sb[]", sb, sizeof(sb)); |
||||
errno = EBADE; |
||||
return -1; |
||||
} |
||||
|
||||
if (verbose) |
||||
dump_bytes("SG_IO: sb[]", sb, sizeof(sb)); |
||||
|
||||
if (verbose) { |
||||
int len = desc[1], maxlen = sizeof(sb) - 8 - 2; |
||||
if (len > maxlen) |
||||
len = maxlen; |
||||
dump_bytes("SG_IO: desc[]", desc, len); |
||||
} |
||||
|
||||
tf->is_lba48 = desc[ 2] & 1; |
||||
tf->error = desc[ 3]; |
||||
tf->lob.nsect = desc[ 5]; |
||||
tf->lob.lbal = desc[ 7]; |
||||
tf->lob.lbam = desc[ 9]; |
||||
tf->lob.lbah = desc[11]; |
||||
tf->dev = desc[12]; |
||||
tf->status = desc[13]; |
||||
tf->hob.feat = 0; |
||||
if (tf->is_lba48) { |
||||
tf->hob.nsect = desc[ 4]; |
||||
tf->hob.lbal = desc[ 6]; |
||||
tf->hob.lbam = desc[ 8]; |
||||
tf->hob.lbah = desc[10]; |
||||
} else { |
||||
tf->hob.nsect = 0; |
||||
tf->hob.lbal = 0; |
||||
tf->hob.lbam = 0; |
||||
tf->hob.lbah = 0; |
||||
} |
||||
|
||||
if (verbose) |
||||
fprintf(stderr, " ATA_%u stat=%02x err=%02x nsect=%02x lbal=%02x lbam=%02x lbah=%02x dev=%02x\n", |
||||
io_hdr.cmd_len, tf->status, tf->error, tf->lob.nsect, tf->lob.lbal, tf->lob.lbam, tf->lob.lbah, tf->dev); |
||||
|
||||
if (tf->status & (ATA_STAT_ERR | ATA_STAT_DRQ)) { |
||||
if (verbose) { |
||||
fprintf(stderr, "I/O error, ata_op=0x%02x ata_status=0x%02x ata_error=0x%02x\n", |
||||
tf->command, tf->status, tf->error); |
||||
} |
||||
errno = EIO; |
||||
return -1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
#endif /* SG_IO */ |
||||
|
||||
int do_drive_cmd (int fd, unsigned char *args) |
||||
{ |
||||
#ifdef SG_IO |
||||
|
||||
struct ata_tf tf; |
||||
void *data = NULL; |
||||
unsigned int data_bytes = 0; |
||||
int rc; |
||||
|
||||
if (args == NULL) |
||||
goto use_legacy_ioctl; |
||||
/*
|
||||
* Reformat and try to issue via SG_IO: |
||||
*/ |
||||
if (args[3]) { |
||||
data_bytes = args[3] * 512; |
||||
data = args + 4; |
||||
} |
||||
tf_init(&tf, args[0], 0, args[1]); |
||||
tf.lob.feat = args[2]; |
||||
if (tf.command == ATA_OP_SMART) { |
||||
tf.lob.nsect = args[3]; |
||||
tf.lob.lbal = args[1]; |
||||
tf.lob.lbam = 0x4f; |
||||
tf.lob.lbah = 0xc2; |
||||
} |
||||
|
||||
rc = sg16(fd, SG_READ, SG_PIO, &tf, data, data_bytes, 0); |
||||
if (rc == -1) { |
||||
if (errno == EINVAL || errno == ENODEV) |
||||
goto use_legacy_ioctl; |
||||
} |
||||
|
||||
if (rc == 0 || errno == EIO) { |
||||
args[0] = tf.status; |
||||
args[1] = tf.error; |
||||
args[2] = tf.lob.nsect; |
||||
} |
||||
return rc; |
||||
|
||||
use_legacy_ioctl: |
||||
#endif /* SG_IO */ |
||||
if (verbose) |
||||
fprintf(stderr, "Trying legacy HDIO_DRIVE_CMD\n"); |
||||
return ioctl(fd, HDIO_DRIVE_CMD, args); |
||||
} |
||||
|
||||
int do_taskfile_cmd (int fd, struct hdio_taskfile *r, unsigned int timeout_secs) |
||||
{ |
||||
int rc; |
||||
#ifdef SG_IO |
||||
struct ata_tf tf; |
||||
void *data = NULL; |
||||
unsigned int data_bytes = 0; |
||||
int rw = SG_READ; |
||||
/*
|
||||
* Reformat and try to issue via SG_IO: |
||||
*/ |
||||
tf_init(&tf, 0, 0, 0); |
||||
#if 1 /* debugging */ |
||||
if (verbose) { |
||||
printf("oflags.lob_all=0x%02x, flags={", r->oflags.lob_all); |
||||
if (r->oflags.lob.feat) printf(" feat"); |
||||
if (r->oflags.lob.lbal) printf(" lbal"); |
||||
if (r->oflags.lob.nsect)printf(" nsect"); |
||||
if (r->oflags.lob.lbam) printf(" lbam"); |
||||
if (r->oflags.lob.lbah) printf(" lbah"); |
||||
if (r->oflags.lob.dev) printf(" dev"); |
||||
if (r->oflags.lob.command) printf(" command"); |
||||
printf(" }\n"); |
||||
printf("oflags.hob_all=0x%02x, flags={", r->oflags.hob_all); |
||||
if (r->oflags.hob.feat) printf(" feat"); |
||||
if (r->oflags.hob.lbal) printf(" lbal"); |
||||
if (r->oflags.hob.nsect)printf(" nsect"); |
||||
if (r->oflags.hob.lbam) printf(" lbam"); |
||||
if (r->oflags.hob.lbah) printf(" lbah"); |
||||
printf(" }\n"); |
||||
} |
||||
#endif |
||||
if (r->oflags.lob.feat) tf.lob.feat = r->lob.feat; |
||||
if (r->oflags.lob.lbal) tf.lob.lbal = r->lob.lbal; |
||||
if (r->oflags.lob.nsect) tf.lob.nsect = r->lob.nsect; |
||||
if (r->oflags.lob.lbam) tf.lob.lbam = r->lob.lbam; |
||||
if (r->oflags.lob.lbah) tf.lob.lbah = r->lob.lbah; |
||||
if (r->oflags.lob.dev) tf.dev = r->lob.dev; |
||||
if (r->oflags.lob.command) tf.command = r->lob.command; |
||||
if (r->oflags.hob_all || r->iflags.hob_all) { |
||||
tf.is_lba48 = 1; |
||||
if (r->oflags.hob.feat) tf.hob.feat = r->hob.feat; |
||||
if (r->oflags.hob.lbal) tf.hob.lbal = r->hob.lbal; |
||||
if (r->oflags.hob.nsect)tf.hob.nsect = r->hob.nsect; |
||||
if (r->oflags.hob.lbam) tf.hob.lbam = r->hob.lbam; |
||||
if (r->oflags.hob.lbah) tf.hob.lbah = r->hob.lbah; |
||||
if (verbose) |
||||
fprintf(stderr, "using LBA48 taskfile\n"); |
||||
} |
||||
switch (r->cmd_req) { |
||||
case TASKFILE_CMD_REQ_OUT: |
||||
case TASKFILE_CMD_REQ_RAW_OUT: |
||||
data_bytes = r->obytes; |
||||
data = r->data; |
||||
rw = SG_WRITE; |
||||
break; |
||||
case TASKFILE_CMD_REQ_IN: |
||||
data_bytes = r->ibytes; |
||||
data = r->data; |
||||
break; |
||||
} |
||||
|
||||
rc = sg16(fd, rw, SG_PIO, &tf, data, data_bytes, timeout_secs); |
||||
if (rc == -1) { |
||||
if (errno == EINVAL || errno == ENODEV) |
||||
goto use_legacy_ioctl; |
||||
} |
||||
|
||||
if (rc == 0 || errno == EIO) { |
||||
if (r->iflags.lob.feat) r->lob.feat = tf.error; |
||||
if (r->iflags.lob.lbal) r->lob.lbal = tf.lob.lbal; |
||||
if (r->iflags.lob.nsect) r->lob.nsect = tf.lob.nsect; |
||||
if (r->iflags.lob.lbam) r->lob.lbam = tf.lob.lbam; |
||||
if (r->iflags.lob.lbah) r->lob.lbah = tf.lob.lbah; |
||||
if (r->iflags.lob.dev) r->lob.dev = tf.dev; |
||||
if (r->iflags.lob.command) r->lob.command = tf.status; |
||||
if (r->iflags.hob.feat) r->hob.feat = tf.hob.feat; |
||||
if (r->iflags.hob.lbal) r->hob.lbal = tf.hob.lbal; |
||||
if (r->iflags.hob.nsect) r->hob.nsect = tf.hob.nsect; |
||||
if (r->iflags.hob.lbam) r->hob.lbam = tf.hob.lbam; |
||||
if (r->iflags.hob.lbah) r->hob.lbah = tf.hob.lbah; |
||||
} |
||||
return rc; |
||||
|
||||
use_legacy_ioctl: |
||||
#else |
||||
timeout_secs = 0; /* keep compiler happy */ |
||||
#endif /* SG_IO */ |
||||
if (verbose) |
||||
fprintf(stderr, "trying legacy HDIO_DRIVE_TASKFILE\n"); |
||||
errno = 0; |
||||
rc = ioctl(fd, HDIO_DRIVE_TASKFILE, r); |
||||
if (verbose) { |
||||
int err = errno; |
||||
fprintf(stderr, "rc=%d, errno=%d, returned ATA registers: ", rc, err); |
||||
if (r->iflags.lob.feat) fprintf(stderr, " er=%02x", r->lob.feat); |
||||
if (r->iflags.lob.nsect) fprintf(stderr, " ns=%02x", r->lob.nsect); |
||||
if (r->iflags.lob.lbal) fprintf(stderr, " ll=%02x", r->lob.lbal); |
||||
if (r->iflags.lob.lbam) fprintf(stderr, " lm=%02x", r->lob.lbam); |
||||
if (r->iflags.lob.lbah) fprintf(stderr, " lh=%02x", r->lob.lbah); |
||||
if (r->iflags.lob.dev) fprintf(stderr, " dh=%02x", r->lob.dev); |
||||
if (r->iflags.lob.command) fprintf(stderr, " st=%02x", r->lob.command); |
||||
if (r->iflags.hob.feat) fprintf(stderr, " err=%02x", r->hob.feat); |
||||
if (r->iflags.hob.nsect) fprintf(stderr, " err=%02x", r->hob.nsect); |
||||
if (r->iflags.hob.lbal) fprintf(stderr, " err=%02x", r->hob.lbal); |
||||
if (r->iflags.hob.lbam) fprintf(stderr, " err=%02x", r->hob.lbam); |
||||
if (r->iflags.hob.lbah) fprintf(stderr, " err=%02x", r->hob.lbah); |
||||
fprintf(stderr, "\n"); |
||||
errno = err; |
||||
} |
||||
if (rc == -1 && errno == EINVAL) { |
||||
fprintf(stderr, "The running kernel lacks CONFIG_IDE_TASK_IOCTL support for this device.\n"); |
||||
errno = EINVAL; |
||||
} |
||||
return rc; |
||||
} |
||||
|
||||
void init_hdio_taskfile (struct hdio_taskfile *r, __u8 ata_op, int rw, int force_lba48, |
||||
__u64 lba, unsigned int nsect, int data_bytes) |
||||
{ |
||||
memset(r, 0, sizeof(struct hdio_taskfile) + data_bytes); |
||||
if (!data_bytes) { |
||||
r->dphase = TASKFILE_DPHASE_NONE; |
||||
r->cmd_req = TASKFILE_CMD_REQ_NODATA; |
||||
} else if (rw == RW_WRITE) { |
||||
r->dphase = TASKFILE_DPHASE_PIO_OUT; |
||||
r->cmd_req = TASKFILE_CMD_REQ_RAW_OUT; |
||||
r->obytes = data_bytes; |
||||
} else { /* rw == RW_READ */ |
||||
r->dphase = TASKFILE_DPHASE_PIO_IN; |
||||
r->cmd_req = TASKFILE_CMD_REQ_IN; |
||||
r->ibytes = data_bytes; |
||||
} |
||||
r->lob.command = ata_op; |
||||
r->oflags.lob.command = 1; |
||||
r->oflags.lob.dev = 1; |
||||
r->oflags.lob.lbal = 1; |
||||
r->oflags.lob.lbam = 1; |
||||
r->oflags.lob.lbah = 1; |
||||
r->oflags.lob.nsect = 1; |
||||
|
||||
r->iflags.lob.command = 1; |
||||
r->iflags.lob.feat = 1; |
||||
|
||||
r->lob.nsect = nsect; |
||||
r->lob.lbal = lba; |
||||
r->lob.lbam = lba >> 8; |
||||
r->lob.lbah = lba >> 16; |
||||
r->lob.dev = 0xa0 | ATA_USING_LBA; |
||||
|
||||
if (needs_lba48(ata_op, lba, nsect) || force_lba48) { |
||||
r->hob.nsect = nsect >> 8; |
||||
r->hob.lbal = lba >> 24; |
||||
r->hob.lbam = lba >> 32; |
||||
r->hob.lbah = lba >> 40; |
||||
r->oflags.hob.nsect = 1; |
||||
r->oflags.hob.lbal = 1; |
||||
r->oflags.hob.lbam = 1; |
||||
r->oflags.hob.lbah = 1; |
||||
} else { |
||||
r->lob.dev |= (lba >> 24) & 0x0f; |
||||
} |
||||
} |
@ -0,0 +1,230 @@
|
||||
/* prototypes and stuff for ATA command ioctls */ |
||||
|
||||
#include <linux/types.h> |
||||
|
||||
enum { |
||||
ATA_OP_READ_PIO = 0x20, |
||||
ATA_OP_READ_PIO_ONCE = 0x21, |
||||
ATA_OP_READ_LONG = 0x22, |
||||
ATA_OP_READ_LONG_ONCE = 0x23, |
||||
ATA_OP_READ_PIO_EXT = 0x24, |
||||
ATA_OP_READ_DMA_EXT = 0x25, |
||||
ATA_OP_READ_FPDMA = 0x60, // NCQ
|
||||
ATA_OP_WRITE_PIO = 0x30, |
||||
ATA_OP_WRITE_LONG = 0x32, |
||||
ATA_OP_WRITE_LONG_ONCE = 0x33, |
||||
ATA_OP_WRITE_PIO_EXT = 0x34, |
||||
ATA_OP_WRITE_DMA_EXT = 0x35, |
||||
ATA_OP_WRITE_FPDMA = 0x61, // NCQ
|
||||
ATA_OP_READ_VERIFY = 0x40, |
||||
ATA_OP_READ_VERIFY_ONCE = 0x41, |
||||
ATA_OP_READ_VERIFY_EXT = 0x42, |
||||
ATA_OP_WRITE_UNC_EXT = 0x45, // lba48, no data, uses feat reg
|
||||
ATA_OP_FORMAT_TRACK = 0x50, |
||||
ATA_OP_DOWNLOAD_MICROCODE = 0x92, |
||||
ATA_OP_STANDBYNOW2 = 0x94, |
||||
ATA_OP_CHECKPOWERMODE2 = 0x98, |
||||
ATA_OP_SLEEPNOW2 = 0x99, |
||||
ATA_OP_PIDENTIFY = 0xa1, |
||||
ATA_OP_READ_NATIVE_MAX = 0xf8, |
||||
ATA_OP_READ_NATIVE_MAX_EXT = 0x27, |
||||
ATA_OP_SMART = 0xb0, |
||||
ATA_OP_DCO = 0xb1, |
||||
ATA_OP_ERASE_SECTORS = 0xc0, |
||||
ATA_OP_READ_DMA = 0xc8, |
||||
ATA_OP_WRITE_DMA = 0xca, |
||||
ATA_OP_DOORLOCK = 0xde, |
||||
ATA_OP_DOORUNLOCK = 0xdf, |
||||
ATA_OP_STANDBYNOW1 = 0xe0, |
||||
ATA_OP_IDLEIMMEDIATE = 0xe1, |
||||
ATA_OP_SETIDLE = 0xe3, |
||||
ATA_OP_SET_MAX = 0xf9, |
||||
ATA_OP_SET_MAX_EXT = 0x37, |
||||
ATA_OP_SET_MULTIPLE = 0xc6, |
||||
ATA_OP_CHECKPOWERMODE1 = 0xe5, |
||||
ATA_OP_SLEEPNOW1 = 0xe6, |
||||
ATA_OP_FLUSHCACHE = 0xe7, |
||||
ATA_OP_FLUSHCACHE_EXT = 0xea, |
||||
ATA_OP_IDENTIFY = 0xec, |
||||
ATA_OP_SETFEATURES = 0xef, |
||||
ATA_OP_SECURITY_SET_PASS = 0xf1, |
||||
ATA_OP_SECURITY_UNLOCK = 0xf2, |
||||
ATA_OP_SECURITY_ERASE_PREPARE = 0xf3, |
||||
ATA_OP_SECURITY_ERASE_UNIT = 0xf4, |
||||
ATA_OP_SECURITY_FREEZE_LOCK = 0xf5, |
||||
ATA_OP_SECURITY_DISABLE = 0xf6, |
||||
}; |
||||
|
||||
/*
|
||||
* Some useful ATA register bits |
||||
*/ |
||||
enum { |
||||
ATA_USING_LBA = (1 << 6), |
||||
ATA_STAT_DRQ = (1 << 3), |
||||
ATA_STAT_ERR = (1 << 0), |
||||
}; |
||||
|
||||
/*
|
||||
* Useful parameters for init_hdio_taskfile(): |
||||
*/ |
||||
enum { RW_READ = 0, |
||||
RW_WRITE = 1, |
||||
LBA28_OK = 0, |
||||
LBA48_FORCE = 1, |
||||
}; |
||||
|
||||
/*
|
||||
* Definitions and structures for use with SG_IO + ATA_16: |
||||
*/ |
||||
struct ata_lba_regs { |
||||
__u8 feat; |
||||
__u8 nsect; |
||||
__u8 lbal; |
||||
__u8 lbam; |
||||
__u8 lbah; |
||||
}; |
||||
struct ata_tf { |
||||
__u8 dev; |
||||
__u8 command; |
||||
__u8 error; |
||||
__u8 status; |
||||
__u8 is_lba48; |
||||
struct ata_lba_regs lob; |
||||
struct ata_lba_regs hob; |
||||
}; |
||||
|
||||
/*
|
||||
* Definitions and structures for use with HDIO_DRIVE_TASKFILE: |
||||
*/ |
||||
|
||||
enum { |
||||
/*
|
||||
* These (redundantly) specify the category of the request |
||||
*/ |
||||
TASKFILE_CMD_REQ_NODATA = 0, /* ide: IDE_DRIVE_TASK_NO_DATA */ |
||||
TASKFILE_CMD_REQ_IN = 2, /* ide: IDE_DRIVE_TASK_IN */ |
||||
TASKFILE_CMD_REQ_OUT = 3, /* ide: IDE_DRIVE_TASK_OUT */ |
||||
TASKFILE_CMD_REQ_RAW_OUT= 4, /* ide: IDE_DRIVE_TASK_RAW_WRITE */ |
||||
/*
|
||||
* These specify the method of transfer (pio, dma, multi, ..) |
||||
*/ |
||||
TASKFILE_DPHASE_NONE = 0, /* ide: TASKFILE_IN */ |
||||
TASKFILE_DPHASE_PIO_IN = 1, /* ide: TASKFILE_IN */ |
||||
TASKFILE_DPHASE_PIO_OUT = 4, /* ide: TASKFILE_OUT */ |
||||
}; |
||||
|
||||
struct reg_flags { |
||||
union { |
||||
unsigned lob_all : 8; |
||||
struct { |
||||
unsigned data : 1; |
||||
unsigned feat : 1; |
||||
unsigned lbal : 1; |
||||
unsigned nsect : 1; |
||||
unsigned lbam : 1; |
||||
unsigned lbah : 1; |
||||
unsigned dev : 1; |
||||
unsigned command : 1; |
||||
} lob; |
||||
}; |
||||
union { |
||||
unsigned hob_all : 8; |
||||
struct { |
||||
unsigned data : 1; |
||||
unsigned feat : 1; |
||||
unsigned lbal : 1; |
||||
unsigned nsect : 1; |
||||
unsigned lbam : 1; |
||||
unsigned lbah : 1; |
||||
unsigned dev : 1; |
||||
unsigned command : 1; |
||||
} hob; |
||||
}; |
||||
}; |
||||
|
||||
struct taskfile_regs { |
||||
__u8 data; |
||||
__u8 feat; |
||||
__u8 nsect; |
||||
__u8 lbal; |
||||
__u8 lbam; |
||||
__u8 lbah; |
||||
__u8 dev; |
||||
__u8 command; |
||||
}; |
||||
|
||||
struct hdio_taskfile { |
||||
struct taskfile_regs lob; |
||||
struct taskfile_regs hob; |
||||
struct reg_flags oflags; |
||||
struct reg_flags iflags; |
||||
int dphase; |
||||
int cmd_req; /* IDE command_type */ |
||||
unsigned long obytes; |
||||
unsigned long ibytes; |
||||
__u16 data[0]; |
||||
}; |
||||
|
||||
struct scsi_sg_io_hdr { |
||||
int interface_id; |
||||
int dxfer_direction; |
||||
unsigned char cmd_len; |
||||
unsigned char mx_sb_len; |
||||
unsigned short iovec_count; |
||||
unsigned int dxfer_len; |
||||
void * dxferp; |
||||
unsigned char * cmdp; |
||||
void * sbp; |
||||
unsigned int timeout; |
||||
unsigned int flags; |
||||
int pack_id; |
||||
void * usr_ptr; |
||||
unsigned char status; |
||||
unsigned char masked_status; |
||||
unsigned char msg_status; |
||||
unsigned char sb_len_wr; |
||||
unsigned short host_status; |
||||
unsigned short driver_status; |
||||
int resid; |
||||
unsigned int duration; |
||||
unsigned int info; |
||||
}; |
||||
|
||||
#ifndef SG_DXFER_NONE |
||||
#define SG_DXFER_NONE -1 |
||||
#define SG_DXFER_TO_DEV -2 |
||||
#define SG_DXFER_FROM_DEV -3 |
||||
#define SG_DXFER_TO_FROM_DEV -4 |
||||
#endif |
||||
|
||||
#define SG_READ 0 |
||||
#define SG_WRITE 1 |
||||
|
||||
#define SG_PIO 0 |
||||
#define SG_DMA 1 |
||||
|
||||
#define SG_CHECK_CONDITION 0x02 |
||||
#define SG_DRIVER_SENSE 0x08 |
||||
|
||||
#define SG_ATA_16 0x85 |
||||
#define SG_ATA_16_LEN 16 |
||||
|
||||
#define SG_ATA_12 0xa1 |
||||
#define SG_ATA_12_LEN 12 |
||||
|
||||
#define SG_ATA_LBA48 1 |
||||
#define SG_ATA_PROTO_NON_DATA ( 3 << 1) |
||||
#define SG_ATA_PROTO_PIO_IN ( 4 << 1) |
||||
#define SG_ATA_PROTO_PIO_OUT ( 5 << 1) |
||||
#define SG_ATA_PROTO_DMA ( 6 << 1) |
||||
#define SG_ATA_PROTO_UDMA_IN (11 << 1) /* not yet supported in libata */ |
||||
#define SG_ATA_PROTO_UDMA_OUT (12 << 1) /* not yet supported in libata */ |
||||
|
||||
void tf_init (struct ata_tf *tf, __u8 ata_op, __u64 lba, unsigned int nsect); |
||||
__u64 tf_to_lba (struct ata_tf *tf); |
||||
int sg16 (int fd, int rw, int dma, struct ata_tf *tf, void *data, unsigned int data_bytes, unsigned int timeout_secs); |
||||
int do_drive_cmd (int fd, unsigned char *args); |
||||
int do_taskfile_cmd (int fd, struct hdio_taskfile *r, unsigned int timeout_secs); |
||||
int dev_has_sgio (int fd); |
||||
void init_hdio_taskfile (struct hdio_taskfile *r, __u8 ata_op, int rw, int force_lba48, |
||||
__u64 lba, unsigned int nsect, int data_bytes); |
Loading…
Reference in new issue