| /* |
| * 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__ */ |