From 1c13dd3f5832c3137bfd9241da66e71dfb9ab63d Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Sat, 12 Jun 2010 13:43:51 +0200 Subject: [PATCH] missing files --- plugins/diskstandby.c | 152 +++++++++++++ plugins/sgio.c | 492 ++++++++++++++++++++++++++++++++++++++++++ plugins/sgio.h | 230 ++++++++++++++++++++ 3 files changed, 874 insertions(+) create mode 100644 plugins/diskstandby.c create mode 100644 plugins/sgio.c create mode 100644 plugins/sgio.h diff --git a/plugins/diskstandby.c b/plugins/diskstandby.c new file mode 100644 index 0000000..ebde78d --- /dev/null +++ b/plugins/diskstandby.c @@ -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 +#include +#include +#include +#include + +#include +#include +#include + +#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, +}; diff --git a/plugins/sgio.c b/plugins/sgio.c new file mode 100644 index 0000000..41f2ff6 --- /dev/null +++ b/plugins/sgio.c @@ -0,0 +1,492 @@ +/* sgio.c - by Mark Lord (C) 2007 -- freely distributable */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sgio.h" +//#include "hdparm.h" + +#include + +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; + } +} diff --git a/plugins/sgio.h b/plugins/sgio.h new file mode 100644 index 0000000..7ad2d11 --- /dev/null +++ b/plugins/sgio.h @@ -0,0 +1,230 @@ +/* prototypes and stuff for ATA command ioctls */ + +#include + +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);