368 lines
9.8 KiB
C
368 lines
9.8 KiB
C
/*
|
|
* This file is copied from libzbc.
|
|
*
|
|
* Copyright (C) 2009-2014, HGST, Inc. All rights reserved.
|
|
*
|
|
* This software is distributed under the terms of the BSD 2-clause license,
|
|
* "as is," without technical support, and WITHOUT ANY WARRANTY, without
|
|
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. You should have received a copy of the BSD 2-clause license along
|
|
* with libzbc. If not, see <http://opensource.org/licenses/BSD-2-Clause>.
|
|
*
|
|
* Author: Damien Le Moal (damien.lemoal@hgst.com)
|
|
* Christophe Louargant (christophe.louargant@hgst.com)
|
|
*/
|
|
|
|
#ifndef __LIBZBC_SG_H__
|
|
#define __LIBZBC_SG_H__
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
#ifdef __linux__
|
|
#include <linux/types.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#ifdef __linux__
|
|
#include <scsi/scsi.h>
|
|
#include <scsi/sg.h>
|
|
#else
|
|
#include "scsi/scsi.h"
|
|
#include "scsi/sg.h"
|
|
#endif
|
|
|
|
#define zbc_error(format, args...) \
|
|
fprintf(stderr, "[ERROR] " format, ##args)
|
|
|
|
/**
|
|
* SG SCSI command names.
|
|
*/
|
|
enum {
|
|
|
|
ZBC_SG_TEST_UNIT_READY = 0,
|
|
ZBC_SG_INQUIRY,
|
|
ZBC_SG_READ_CAPACITY,
|
|
ZBC_SG_READ,
|
|
ZBC_SG_WRITE,
|
|
ZBC_SG_SYNC_CACHE,
|
|
ZBC_SG_REPORT_ZONES,
|
|
ZBC_SG_OPEN_ZONE,
|
|
ZBC_SG_CLOSE_ZONE,
|
|
ZBC_SG_FINISH_ZONE,
|
|
ZBC_SG_RESET_WRITE_POINTER,
|
|
ZBC_SG_SET_ZONES,
|
|
ZBC_SG_SET_WRITE_POINTER,
|
|
ZBC_SG_ATA12,
|
|
ZBC_SG_ATA16,
|
|
|
|
ZBC_SG_CMD_NUM,
|
|
};
|
|
|
|
/**
|
|
* Test unit ready command definition.
|
|
*/
|
|
#define ZBC_SG_TEST_UNIT_READY_CDB_OPCODE 0x00
|
|
#define ZBC_SG_TEST_UNIT_READY_CDB_LENGTH 6
|
|
#define ZBC_ZONE_DESCRIPTOR_LENGTH 64
|
|
|
|
/**
|
|
* Number of bytes in the buffer before the first Zone Descriptor.
|
|
*/
|
|
#define ZBC_ZONE_DESCRIPTOR_OFFSET 64
|
|
|
|
/**
|
|
* Inquiry command definition.
|
|
*/
|
|
#define ZBC_SG_INQUIRY_CDB_OPCODE 0x12
|
|
#define ZBC_SG_INQUIRY_CDB_LENGTH 6
|
|
#define ZBC_SG_INQUIRY_REPLY_LEN 96
|
|
#define ZBC_SG_INQUIRY_REPLY_LEN_VPD_PAGE_B1 64
|
|
#define ZBC_SG_INQUIRY_REPLY_LEN_VPD_PAGE_B6 64
|
|
|
|
/**
|
|
* Read capacity command definition.
|
|
*/
|
|
#define ZBC_SG_READ_CAPACITY_CDB_OPCODE 0x9E
|
|
#define ZBC_SG_READ_CAPACITY_CDB_SA 0x10
|
|
#define ZBC_SG_READ_CAPACITY_CDB_LENGTH 16
|
|
#define ZBC_SG_READ_CAPACITY_REPLY_LEN 32
|
|
|
|
/**
|
|
* Read command definition.
|
|
*/
|
|
#define ZBC_SG_READ_CDB_OPCODE 0x88
|
|
#define ZBC_SG_READ_CDB_LENGTH 16
|
|
|
|
/**
|
|
* Write command definition.
|
|
*/
|
|
#define ZBC_SG_WRITE_CDB_OPCODE 0x8A
|
|
#define ZBC_SG_WRITE_CDB_LENGTH 16
|
|
|
|
/**
|
|
* Sync cache command definition.
|
|
*/
|
|
#define ZBC_SG_SYNC_CACHE_CDB_OPCODE 0x91
|
|
#define ZBC_SG_SYNC_CACHE_CDB_LENGTH 16
|
|
|
|
/**
|
|
* Report zones command definition.
|
|
*/
|
|
#define ZBC_SG_REPORT_ZONES_CDB_OPCODE 0x95
|
|
#define ZBC_SG_REPORT_ZONES_CDB_SA 0x00
|
|
#define ZBC_SG_REPORT_ZONES_CDB_LENGTH 16
|
|
|
|
/**
|
|
* Open zone command definition.
|
|
*/
|
|
#define ZBC_SG_OPEN_ZONE_CDB_OPCODE 0x94
|
|
#define ZBC_SG_OPEN_ZONE_CDB_SA 0x03
|
|
#define ZBC_SG_OPEN_ZONE_CDB_LENGTH 16
|
|
|
|
/**
|
|
* Close zone command definition.
|
|
*/
|
|
#define ZBC_SG_CLOSE_ZONE_CDB_OPCODE 0x94
|
|
#define ZBC_SG_CLOSE_ZONE_CDB_SA 0x01
|
|
#define ZBC_SG_CLOSE_ZONE_CDB_LENGTH 16
|
|
|
|
/**
|
|
* Finish zone command definition.
|
|
*/
|
|
#define ZBC_SG_FINISH_ZONE_CDB_OPCODE 0x94
|
|
#define ZBC_SG_FINISH_ZONE_CDB_SA 0x02
|
|
#define ZBC_SG_FINISH_ZONE_CDB_LENGTH 16
|
|
|
|
/**
|
|
* Reset write pointer command definition.
|
|
*/
|
|
#define ZBC_SG_RESET_WRITE_POINTER_CDB_OPCODE 0x94
|
|
#define ZBC_SG_RESET_WRITE_POINTER_CDB_SA 0x04
|
|
#define ZBC_SG_RESET_WRITE_POINTER_CDB_LENGTH 16
|
|
|
|
/**
|
|
* Set zones command definition.
|
|
*/
|
|
#define ZBC_SG_SET_ZONES_CDB_OPCODE 0x9F
|
|
#define ZBC_SG_SET_ZONES_CDB_SA 0x15
|
|
#define ZBC_SG_SET_ZONES_CDB_LENGTH 16
|
|
|
|
/**
|
|
* Set write pointer command definition.
|
|
*/
|
|
#define ZBC_SG_SET_WRITE_POINTER_CDB_OPCODE 0x9F
|
|
#define ZBC_SG_SET_WRITE_POINTER_CDB_SA 0x16
|
|
#define ZBC_SG_SET_WRITE_POINTER_CDB_LENGTH 16
|
|
|
|
/**
|
|
* ATA pass through 12.
|
|
*/
|
|
#define ZBC_SG_ATA12_CDB_OPCODE 0xA1
|
|
#define ZBC_SG_ATA12_CDB_LENGTH 12
|
|
|
|
/**
|
|
* ATA pass through 16.
|
|
*/
|
|
#define ZBC_SG_ATA16_CDB_OPCODE 0x85
|
|
#define ZBC_SG_ATA16_CDB_LENGTH 16
|
|
|
|
/**
|
|
* Command sense buffer maximum length.
|
|
*/
|
|
#define ZBC_SG_SENSE_MAX_LENGTH 64
|
|
|
|
/**
|
|
* Maximum command CDB length.
|
|
*/
|
|
#define ZBC_SG_CDB_MAX_LENGTH 16
|
|
|
|
/**
|
|
* Status codes.
|
|
*/
|
|
#define ZBC_SG_CHECK_CONDITION 0x02
|
|
|
|
/**
|
|
* Host status codes.
|
|
*/
|
|
#define ZBC_SG_DID_OK 0x00 /* No error */
|
|
#define ZBC_SG_DID_NO_CONNECT 0x01 /* Couldn't connect before timeout period */
|
|
#define ZBC_SG_DID_BUS_BUSY 0x02 /* BUS stayed busy through time out period */
|
|
#define ZBC_SG_DID_TIME_OUT 0x03 /* Timed out for other reason */
|
|
#define ZBC_SG_DID_BAD_TARGET 0x04 /* Bad target, device not responding? */
|
|
#define ZBC_SG_DID_ABORT 0x05 /* Told to abort for some other reason. */
|
|
#define ZBC_SG_DID_PARITY 0x06 /* Parity error. */
|
|
#define ZBC_SG_DID_ERROR 0x07 /* Internal error detected in the host adapter. */
|
|
#define ZBC_SG_DID_RESET 0x08 /* The SCSI bus (or this device) has been reset. */
|
|
#define ZBC_SG_DID_BAD_INTR 0x09 /* Got an unexpected interrupt */
|
|
#define ZBC_SG_DID_PASSTHROUGH 0x0a /* Forced command past mid-layer. */
|
|
#define ZBC_SG_DID_SOFT_ERROR 0x0b /* The low level driver wants a retry. */
|
|
|
|
/**
|
|
* Driver status codes.
|
|
*/
|
|
#define ZBC_SG_DRIVER_OK 0x00
|
|
#define ZBC_SG_DRIVER_BUSY 0x01
|
|
#define ZBC_SG_DRIVER_SOFT 0x02
|
|
#define ZBC_SG_DRIVER_MEDIA 0x03
|
|
#define ZBC_SG_DRIVER_ERROR 0x04
|
|
#define ZBC_SG_DRIVER_INVALID 0x05
|
|
#define ZBC_SG_DRIVER_TIMEOUT 0x06
|
|
#define ZBC_SG_DRIVER_HARD 0x07
|
|
#define ZBC_SG_DRIVER_SENSE 0x08
|
|
#define ZBC_SG_DRIVER_STATUS_MASK 0x0f
|
|
|
|
/**
|
|
* Driver status code flags ('or'ed with code)
|
|
*/
|
|
#define ZBC_SG_DRIVER_SUGGEST_RETRY 0x10
|
|
#define ZBC_SG_DRIVER_SUGGEST_ABORT 0x20
|
|
#define ZBC_SG_DRIVER_SUGGEST_REMAP 0x30
|
|
#define ZBC_SG_DRIVER_SUGGEST_DIE 0x40
|
|
#define ZBC_SG_DRIVER_SUGGEST_SENSE 0x80
|
|
#define ZBC_SG_DRIVER_FLAGS_MASK 0xf0
|
|
|
|
/***** Type definitions *****/
|
|
|
|
/**
|
|
* SG command descriptor. Used to process SCSI commands.
|
|
*/
|
|
typedef struct zbc_sg_cmd {
|
|
|
|
int code;
|
|
|
|
int cdb_opcode;
|
|
int cdb_sa;
|
|
size_t cdb_sz;
|
|
uint8_t cdb[ZBC_SG_CDB_MAX_LENGTH];
|
|
|
|
size_t sense_bufsz;
|
|
uint8_t sense_buf[ZBC_SG_SENSE_MAX_LENGTH];
|
|
|
|
int out_buf_needfree;
|
|
size_t out_bufsz;
|
|
uint8_t *out_buf;
|
|
|
|
sg_io_hdr_t io_hdr;
|
|
|
|
} zbc_sg_cmd_t;
|
|
|
|
/**
|
|
* Zone descriptor.
|
|
*/
|
|
struct zbc_zone {
|
|
|
|
uint64_t zbz_length;
|
|
uint64_t zbz_start;
|
|
uint64_t zbz_write_pointer;
|
|
|
|
uint8_t zbz_type;
|
|
uint8_t zbz_condition;
|
|
uint8_t zbz_flags;
|
|
|
|
uint8_t __pad[5];
|
|
|
|
};
|
|
typedef struct zbc_zone zbc_zone_t;
|
|
|
|
#define ZBC_FORCE_ATA_RW 0x40000000
|
|
#define zbc_open_flags(f) ((f) & ~ZBC_FORCE_ATA_RW)
|
|
|
|
/**
|
|
* Zone type.
|
|
*/
|
|
enum zbc_zone_type {
|
|
ZBC_ZT_CONVENTIONAL = 0x01,
|
|
ZBC_ZT_SEQUENTIAL_REQ = 0x02,
|
|
ZBC_ZT_SEQUENTIAL_PREF = 0x03,
|
|
};
|
|
#define zbc_zone_type(z) ((int)(z)->zbz_type)
|
|
|
|
#define zbc_zone_conventional(z) ((z)->zbz_type == ZBC_ZT_CONVENTIONAL)
|
|
static inline const char *zbc_zone_type_str(enum zbc_zone_type type)
|
|
{
|
|
switch( type ) {
|
|
case ZBC_ZT_CONVENTIONAL:
|
|
return( "Conventional" );
|
|
case ZBC_ZT_SEQUENTIAL_REQ:
|
|
return( "Sequential-write-required" );
|
|
case ZBC_ZT_SEQUENTIAL_PREF:
|
|
return( "Sequential-write-preferred" );
|
|
}
|
|
return( "Unknown-type" );
|
|
}
|
|
|
|
/**
|
|
* Zone condition.
|
|
*/
|
|
enum zbc_zone_condition {
|
|
ZBC_ZC_NOT_WP = 0x00,
|
|
ZBC_ZC_EMPTY = 0x01,
|
|
ZBC_ZC_IMP_OPEN = 0x02,
|
|
ZBC_ZC_EXP_OPEN = 0x03,
|
|
ZBC_ZC_CLOSED = 0x04,
|
|
ZBC_ZC_RDONLY = 0x0d,
|
|
ZBC_ZC_FULL = 0x0e,
|
|
ZBC_ZC_OFFLINE = 0x0f,
|
|
};
|
|
|
|
/**
|
|
* zbc_zone_cond_str - returns a string describing a zone condition.
|
|
* @zone: (IN) ZBC_ZC_NOT_WP, ZBC_ZC_EMPTY, ZBC_ZC_IMP_OPEN, ZBC_ZC_EXP_OPEN,
|
|
* ZBC_ZC_CLOSED, ZBC_ZC_RDONLY, ZBC_ZC_FULL or ZBC_ZC_OFFLINE
|
|
*
|
|
* Returns a string describing a zone condition.
|
|
*/
|
|
static inline const char *zbc_zone_condition_str(enum zbc_zone_condition cond)
|
|
{
|
|
switch( cond ) {
|
|
case ZBC_ZC_NOT_WP:
|
|
return "Not-write-pointer";
|
|
case ZBC_ZC_EMPTY:
|
|
return "Empty";
|
|
case ZBC_ZC_IMP_OPEN:
|
|
return "Implicit-open";
|
|
case ZBC_ZC_EXP_OPEN:
|
|
return "Explicit-open";
|
|
case ZBC_ZC_CLOSED:
|
|
return "Closed";
|
|
case ZBC_ZC_RDONLY:
|
|
return "Read-only";
|
|
case ZBC_ZC_FULL:
|
|
return "Full";
|
|
case ZBC_ZC_OFFLINE:
|
|
return "Offline";
|
|
}
|
|
return "Unknown-cond";
|
|
}
|
|
|
|
#define zbc_zone_condition(z) ((int)(z)->zbz_condition)
|
|
#define zbc_zone_start_lba(z) ((unsigned long long)((z)->zbz_start))
|
|
#define zbc_zone_length(z) ((unsigned long long)((z)->zbz_length))
|
|
#define zbc_zone_wp_lba(z) ((unsigned long long)((z)->zbz_write_pointer))
|
|
|
|
/**
|
|
* Zone flags: need reset, and non-seq write.
|
|
*/
|
|
enum zbc_zone_flags {
|
|
ZBC_ZF_NEED_RESET = 0x0001,
|
|
ZBC_ZF_NON_SEQ = 0x0002,
|
|
};
|
|
#define zbc_zone_need_reset(z) (((z)->zbz_flags & ZBC_ZF_NEED_RESET) != 0)
|
|
#define zbc_zone_non_seq(z) (((z)->zbz_flags & ZBC_ZF_NON_SEQ) != 0)
|
|
|
|
#define zbc_sg_cmd_driver_status(cmd) ((cmd)->io_hdr.driver_status & ZBC_SG_DRIVER_STATUS_MASK)
|
|
#define zbc_sg_cmd_driver_flags(cmd) ((cmd)->io_hdr.driver_status & ZBC_SG_DRIVER_FLAGS_MASK)
|
|
|
|
union converter {
|
|
uint8_t val_buf[8];
|
|
uint16_t val16;
|
|
uint32_t val32;
|
|
uint64_t val64;
|
|
};
|
|
|
|
#endif /* __LIBZBC_SG_H__ */
|