Fixing a potential fall-through in switch in data unmarshal function for maintenance-in commands. The fall-through never happens for valid commands, but the build tools may complain about it.
4511 lines
111 KiB
C
4511 lines
111 KiB
C
/*
|
|
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/*
|
|
* would be nice if this could grow into a full blown library to
|
|
* 1, build and unmarshall a CDB
|
|
* 2, check how big a complete data-in structure needs to be
|
|
* 3, unmarshall data-in into a real structure
|
|
* 4, marshall a real structure into a data-out blob
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_ARPA_INET_H
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#ifdef AROS
|
|
#include "aros/aros_compat.h"
|
|
#endif
|
|
|
|
#if defined(_WIN32)
|
|
#include <winsock2.h>
|
|
#include "win32/win32_compat.h"
|
|
#else
|
|
#include <strings.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_SOCKET_H
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
#include "slist.h"
|
|
#include "scsi-lowlevel.h"
|
|
#include "utils.h"
|
|
|
|
void scsi_task_set_iov_out(struct scsi_task *task, struct scsi_iovec *iov, int niov);
|
|
|
|
struct scsi_allocated_memory {
|
|
struct scsi_allocated_memory *next;
|
|
char buf[0];
|
|
};
|
|
|
|
void
|
|
scsi_free_scsi_task(struct scsi_task *task)
|
|
{
|
|
struct scsi_allocated_memory *mem;
|
|
|
|
if (!task)
|
|
return;
|
|
|
|
while ((mem = task->mem)) {
|
|
ISCSI_LIST_REMOVE(&task->mem, mem);
|
|
free(mem);
|
|
}
|
|
|
|
free(task->datain.data);
|
|
task->datain.data = NULL;
|
|
free(task);
|
|
task = NULL;
|
|
}
|
|
|
|
struct scsi_task *
|
|
scsi_create_task(int cdb_size, unsigned char *cdb, int xfer_dir, int expxferlen)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
|
|
memcpy(&task->cdb[0], cdb, cdb_size);
|
|
task->cdb_size = cdb_size;
|
|
task->xfer_dir = xfer_dir;
|
|
task->expxferlen = expxferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
|
|
void *
|
|
scsi_malloc(struct scsi_task *task, size_t size)
|
|
{
|
|
struct scsi_allocated_memory *mem;
|
|
|
|
mem = malloc(sizeof(struct scsi_allocated_memory) + size);
|
|
if (mem == NULL) {
|
|
return NULL;
|
|
}
|
|
memset(mem, 0, sizeof(struct scsi_allocated_memory) + size);
|
|
ISCSI_LIST_ADD(&task->mem, mem);
|
|
return &mem->buf[0];
|
|
}
|
|
|
|
const char *
|
|
scsi_sense_key_str(int key)
|
|
{
|
|
static struct iscsi_value_string keys[] = {
|
|
{SCSI_SENSE_NO_SENSE,
|
|
"NO SENSE"},
|
|
{SCSI_SENSE_RECOVERED_ERROR,
|
|
"RECOVERED ERROR"},
|
|
{SCSI_SENSE_NOT_READY,
|
|
"NOT READY"},
|
|
{SCSI_SENSE_HARDWARE_ERROR,
|
|
"HARDWARE_ERROR"},
|
|
{SCSI_SENSE_ILLEGAL_REQUEST,
|
|
"ILLEGAL_REQUEST"},
|
|
{SCSI_SENSE_UNIT_ATTENTION,
|
|
"UNIT_ATTENTION"},
|
|
{SCSI_SENSE_DATA_PROTECTION,
|
|
"DATA PROTECTION"},
|
|
{SCSI_SENSE_BLANK_CHECK,
|
|
"BLANK CHECK"},
|
|
{SCSI_SENSE_VENDOR_SPECIFIC,
|
|
"VENDOR SPECIFIC"},
|
|
{SCSI_SENSE_COPY_ABORTED,
|
|
"COPY ABORTED"},
|
|
{SCSI_SENSE_COMMAND_ABORTED,
|
|
"COMMAND ABORTED"},
|
|
{SCSI_SENSE_OBSOLETE_ERROR_CODE,
|
|
"OBSOLETE_ERROR_CODE"},
|
|
{SCSI_SENSE_OVERFLOW_COMMAND,
|
|
"OVERFLOW_COMMAND"},
|
|
{SCSI_SENSE_MISCOMPARE,
|
|
"MISCOMPARE"},
|
|
{0, NULL}
|
|
};
|
|
|
|
return iscsi_value_string_find(keys, key, "UNKNOWN");
|
|
}
|
|
|
|
const char *
|
|
scsi_sense_ascq_str(int ascq)
|
|
{
|
|
static struct iscsi_value_string ascqs[] = {
|
|
{SCSI_SENSE_ASCQ_SANITIZE_IN_PROGRESS,
|
|
"SANITIZE_IN_PROGRESS"},
|
|
{SCSI_SENSE_ASCQ_WRITE_AFTER_SANITIZE_REQUIRED,
|
|
"WRITE_AFTER_SANITIZE_REQUIRED"},
|
|
{SCSI_SENSE_ASCQ_INVALID_OPERATION_CODE,
|
|
"INVALID_OPERATION_CODE"},
|
|
{SCSI_SENSE_ASCQ_LBA_OUT_OF_RANGE,
|
|
"LBA_OUT_OF_RANGE"},
|
|
{SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB,
|
|
"INVALID_FIELD_IN_CDB"},
|
|
{SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED,
|
|
"LOGICAL_UNIT_NOT_SUPPORTED"},
|
|
{SCSI_SENSE_ASCQ_PARAMETER_LIST_LENGTH_ERROR,
|
|
"PARAMETER_LIST_LENGTH_ERROR"},
|
|
{SCSI_SENSE_ASCQ_INVALID_FIELD_IN_PARAMETER_LIST,
|
|
"INVALID_FIELD_IN_PARAMETER_LIST"},
|
|
{SCSI_SENSE_ASCQ_WRITE_PROTECTED,
|
|
"WRITE_PROTECTED"},
|
|
{SCSI_SENSE_ASCQ_WRITE_PROTECTED,
|
|
"WRITE_PROTECTED"},
|
|
{SCSI_SENSE_ASCQ_HARDWARE_WRITE_PROTECTED,
|
|
"HARDWARE_WRITE_PROTECTED"},
|
|
{SCSI_SENSE_ASCQ_SOFTWARE_WRITE_PROTECTED,
|
|
"SOFTWARE_WRITE_PROTECTED"},
|
|
{SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT,
|
|
"MEDIUM_NOT_PRESENT"},
|
|
{SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_CLOSED,
|
|
"MEDIUM_NOT_PRESENT-TRAY_CLOSED"},
|
|
{SCSI_SENSE_ASCQ_MEDIUM_NOT_PRESENT_TRAY_OPEN,
|
|
"MEDIUM_NOT_PRESENT-TRAY_OPEN"},
|
|
{SCSI_SENSE_ASCQ_BUS_RESET,
|
|
"BUS_RESET"},
|
|
{SCSI_SENSE_ASCQ_POWER_ON_OCCURED,
|
|
"POWER_ON_OCCURED"},
|
|
{SCSI_SENSE_ASCQ_SCSI_BUS_RESET_OCCURED,
|
|
"SCSI_BUS_RESET_OCCURED"},
|
|
{SCSI_SENSE_ASCQ_BUS_DEVICE_RESET_FUNCTION_OCCURED,
|
|
"BUS_DEVICE_RESET_FUNCTION_OCCURED"},
|
|
{SCSI_SENSE_ASCQ_DEVICE_INTERNAL_RESET,
|
|
"DEVICE_INTERNAL_RESET"},
|
|
{SCSI_SENSE_ASCQ_TRANSCEIVER_MODE_CHANGED_TO_SINGLE_ENDED,
|
|
"TRANSCEIVER_MODE_CHANGED_TO_SINGLE_ENDED"},
|
|
{SCSI_SENSE_ASCQ_TRANSCEIVER_MODE_CHANGED_TO_LVD,
|
|
"TRANSCEIVER_MODE_CHANGED_TO_LVD"},
|
|
{SCSI_SENSE_ASCQ_MODE_PARAMETERS_CHANGED,
|
|
"MODE PARAMETERS CHANGED"},
|
|
{SCSI_SENSE_ASCQ_CAPACITY_DATA_HAS_CHANGED,
|
|
"CAPACITY_DATA_HAS_CHANGED"},
|
|
{SCSI_SENSE_ASCQ_THIN_PROVISION_SOFT_THRES_REACHED,
|
|
"THIN PROVISIONING SOFT THRESHOLD REACHED"},
|
|
{SCSI_SENSE_ASCQ_INQUIRY_DATA_HAS_CHANGED,
|
|
"INQUIRY DATA HAS CHANGED"},
|
|
{SCSI_SENSE_ASCQ_INTERNAL_TARGET_FAILURE,
|
|
"INTERNAL_TARGET_FAILURE"},
|
|
{SCSI_SENSE_ASCQ_MISCOMPARE_DURING_VERIFY,
|
|
"MISCOMPARE_DURING_VERIFY"},
|
|
{SCSI_SENSE_ASCQ_MISCOMPARE_VERIFY_OF_UNMAPPED_LBA,
|
|
"MISCOMPARE_VERIFY_OF_UNMAPPED_LBA"},
|
|
{ SCSI_SENSE_ASCQ_MEDIUM_LOAD_OR_EJECT_FAILED,
|
|
"MEDIUM_LOAD_OR_EJECT_FAILED" },
|
|
{SCSI_SENSE_ASCQ_MEDIUM_REMOVAL_PREVENTED,
|
|
"SCSI_SENSE_ASCQ_MEDIUM_REMOVAL_PREVENTED"},
|
|
{0, NULL}
|
|
};
|
|
|
|
return iscsi_value_string_find(ascqs, ascq, "UNKNOWN");
|
|
}
|
|
|
|
const char *
|
|
scsi_pr_type_str(enum scsi_persistent_out_type pr_type)
|
|
{
|
|
static struct iscsi_value_string pr_type_strings[] = {
|
|
{SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE,
|
|
"Write Exclusive"},
|
|
{SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS,
|
|
"Exclusive Access"},
|
|
{SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY,
|
|
"Write Exclusive, Registrants Only"},
|
|
{SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY,
|
|
"Exclusive Access Registrants Only"},
|
|
{SCSI_PERSISTENT_RESERVE_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS,
|
|
"Write Exclusive, All Registrants"},
|
|
{SCSI_PERSISTENT_RESERVE_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS,
|
|
"Exclusive Access, All Registrants"},
|
|
{0, NULL}
|
|
};
|
|
|
|
return iscsi_value_string_find(pr_type_strings, pr_type, "UNKNOWN");
|
|
}
|
|
|
|
const char *
|
|
scsi_opcode_str(int opcode)
|
|
{
|
|
static struct iscsi_value_string opcode_strings[] = {
|
|
{SCSI_OPCODE_TESTUNITREADY,
|
|
"TESTUNITREADY"},
|
|
{SCSI_OPCODE_READ6,
|
|
"READ6"},
|
|
{SCSI_OPCODE_INQUIRY,
|
|
"INQUIRY"},
|
|
{SCSI_OPCODE_MODESELECT6,
|
|
"MODESELECT6"},
|
|
{SCSI_OPCODE_RESERVE6,
|
|
"RESERVE6"},
|
|
{SCSI_OPCODE_RELEASE6,
|
|
"RELEASE6"},
|
|
{SCSI_OPCODE_MODESENSE6,
|
|
"MODESENSE6"},
|
|
{SCSI_OPCODE_STARTSTOPUNIT,
|
|
"STARTSTOPUNIT"},
|
|
{SCSI_OPCODE_PREVENTALLOW,
|
|
"PREVENTALLOW"},
|
|
{SCSI_OPCODE_READCAPACITY10,
|
|
"READCAPACITY10"},
|
|
{SCSI_OPCODE_READ10,
|
|
"READ10"},
|
|
{SCSI_OPCODE_WRITE10,
|
|
"WRITE10"},
|
|
{SCSI_OPCODE_WRITE_VERIFY10,
|
|
"WRITE VERIFY10"},
|
|
{SCSI_OPCODE_VERIFY10,
|
|
"VERIFY10"},
|
|
{SCSI_OPCODE_PREFETCH10,
|
|
"PREFETCH10"},
|
|
{SCSI_OPCODE_SYNCHRONIZECACHE10,
|
|
"SYNCHRONIZECACHE10"},
|
|
{SCSI_OPCODE_READ_DEFECT_DATA10,
|
|
"READ DEFECT DATA10"},
|
|
{SCSI_OPCODE_WRITE_SAME10,
|
|
"WRITE SAME10"},
|
|
{SCSI_OPCODE_UNMAP,
|
|
"UNMAP"},
|
|
{SCSI_OPCODE_READTOC,
|
|
"READTOC"},
|
|
{SCSI_OPCODE_SANITIZE,
|
|
"SANITIZE"},
|
|
{SCSI_OPCODE_MODESELECT10,
|
|
"MODESELECT10"},
|
|
{SCSI_OPCODE_MODESENSE10,
|
|
"MODESENSE10"},
|
|
{SCSI_OPCODE_PERSISTENT_RESERVE_IN,
|
|
"PERSISTENT RESERVE IN"},
|
|
{SCSI_OPCODE_PERSISTENT_RESERVE_OUT,
|
|
"PERSISTENT RESERVE OUT"},
|
|
{SCSI_OPCODE_EXTENDED_COPY,
|
|
"EXTENDED COPY"},
|
|
{SCSI_OPCODE_RECEIVE_COPY_RESULTS,
|
|
"RECEIVE COPY RESULTS"},
|
|
{SCSI_OPCODE_READ16,
|
|
"READ16"},
|
|
{SCSI_OPCODE_COMPARE_AND_WRITE,
|
|
"COMPARE AND WRITE"},
|
|
{SCSI_OPCODE_WRITE16,
|
|
"WRITE16"},
|
|
{SCSI_OPCODE_ORWRITE,
|
|
"ORWRITE"},
|
|
{SCSI_OPCODE_WRITE_VERIFY16,
|
|
"WRITE VERIFY16"},
|
|
{SCSI_OPCODE_VERIFY16,
|
|
"VERIFY16"},
|
|
{SCSI_OPCODE_PREFETCH16,
|
|
"PREFETCH16"},
|
|
{SCSI_OPCODE_SYNCHRONIZECACHE16,
|
|
"SYNCHRONIZECACHE16"},
|
|
{SCSI_OPCODE_WRITE_SAME16,
|
|
"WRITE SAME16"},
|
|
{SCSI_OPCODE_WRITE_ATOMIC16,
|
|
"WRITE ATOMIC16"},
|
|
{SCSI_OPCODE_SERVICE_ACTION_IN,
|
|
"SERVICE ACTION IN"},
|
|
{SCSI_OPCODE_REPORTLUNS,
|
|
"REPORTLUNS"},
|
|
{SCSI_OPCODE_MAINTENANCE_IN,
|
|
"MAINTENANCE IN"},
|
|
{SCSI_OPCODE_READ12,
|
|
"READ12"},
|
|
{SCSI_OPCODE_WRITE12,
|
|
"WRITE12"},
|
|
{SCSI_OPCODE_WRITE_VERIFY12,
|
|
"WRITE VERIFY12"},
|
|
{SCSI_OPCODE_VERIFY12,
|
|
"VERIFY12"},
|
|
{SCSI_OPCODE_READ_DEFECT_DATA12,
|
|
"READ DEFECT DATA12"},
|
|
{0, NULL}
|
|
};
|
|
|
|
return iscsi_value_string_find(opcode_strings, opcode, "UNKNOWN");
|
|
}
|
|
|
|
uint64_t
|
|
scsi_get_uint64(const unsigned char *c)
|
|
{
|
|
uint64_t val;
|
|
|
|
val = scsi_get_uint32(c);
|
|
val <<= 32;
|
|
c += 4;
|
|
val |= scsi_get_uint32(c);
|
|
|
|
return val;
|
|
}
|
|
|
|
uint32_t
|
|
scsi_get_uint32(const unsigned char *c)
|
|
{
|
|
uint32_t val;
|
|
val = c[0];
|
|
val = (val << 8) | c[1];
|
|
val = (val << 8) | c[2];
|
|
val = (val << 8) | c[3];
|
|
return val;
|
|
}
|
|
|
|
uint16_t
|
|
scsi_get_uint16(const unsigned char *c)
|
|
{
|
|
uint16_t val;
|
|
val = c[0];
|
|
val = (val << 8) | c[1];
|
|
return val;
|
|
}
|
|
|
|
static inline uint64_t
|
|
task_get_uint64(struct scsi_task *task, int offset)
|
|
{
|
|
if (offset <= task->datain.size - 8) {
|
|
const unsigned char *c = &task->datain.data[offset];
|
|
|
|
return scsi_get_uint64(c);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static inline uint32_t
|
|
task_get_uint32(struct scsi_task *task, int offset)
|
|
{
|
|
if (offset <= task->datain.size - 4) {
|
|
const unsigned char *c = &task->datain.data[offset];
|
|
|
|
return scsi_get_uint32(c);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static inline uint16_t
|
|
task_get_uint16(struct scsi_task *task, int offset)
|
|
{
|
|
if (offset <= task->datain.size - 2) {
|
|
const unsigned char *c = &task->datain.data[offset];
|
|
|
|
return scsi_get_uint16(c);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static inline uint8_t
|
|
task_get_uint8(struct scsi_task *task, int offset)
|
|
{
|
|
if (offset <= task->datain.size - 1) {
|
|
return task->datain.data[offset];
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
scsi_set_uint64(unsigned char *c, uint64_t v)
|
|
{
|
|
uint32_t val;
|
|
|
|
val = (v >> 32) & 0xffffffff;
|
|
scsi_set_uint32(c, val);
|
|
|
|
c += 4;
|
|
val = v & 0xffffffff;
|
|
scsi_set_uint32(c, val);
|
|
}
|
|
|
|
void
|
|
scsi_set_uint32(unsigned char *c, uint32_t val)
|
|
{
|
|
c[0] = val >> 24;
|
|
c[1] = val >> 16;
|
|
c[2] = val >> 8;
|
|
c[3] = val;
|
|
}
|
|
|
|
void
|
|
scsi_set_uint16(unsigned char *c, uint16_t val)
|
|
{
|
|
c[0] = val >> 8;
|
|
c[1] = val;
|
|
}
|
|
|
|
/*
|
|
* TESTUNITREADY
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_testunitready(void)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_TESTUNITREADY;
|
|
|
|
task->cdb_size = 6;
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
task->expxferlen = 0;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* SANITIZE
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_sanitize(int immed, int ause, int sa, int param_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_SANITIZE;
|
|
|
|
task->cdb[1] = sa & 0x1f;
|
|
if (immed) {
|
|
task->cdb[1] |= 0x80;
|
|
}
|
|
if (ause) {
|
|
task->cdb[1] |= 0x20;
|
|
}
|
|
|
|
scsi_set_uint16(&task->cdb[7], param_len);
|
|
|
|
task->cdb_size = 10;
|
|
if (param_len != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = (param_len + 3) & 0xfffc;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* REPORTLUNS
|
|
*/
|
|
struct scsi_task *
|
|
scsi_reportluns_cdb(int report_type, int alloc_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_REPORTLUNS;
|
|
task->cdb[2] = report_type;
|
|
scsi_set_uint32(&task->cdb[6], alloc_len);
|
|
|
|
task->cdb_size = 12;
|
|
if (alloc_len != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = alloc_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* parse the data in blob and calculate the size of a full report luns
|
|
* datain structure
|
|
*/
|
|
static int
|
|
scsi_reportluns_datain_getfullsize(struct scsi_task *task)
|
|
{
|
|
uint32_t list_size;
|
|
|
|
list_size = task_get_uint32(task, 0) + 8;
|
|
|
|
return list_size;
|
|
}
|
|
|
|
/*
|
|
* unmarshall the data in blob for reportluns into a structure
|
|
*/
|
|
static struct scsi_reportluns_list *
|
|
scsi_reportluns_datain_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_reportluns_list *list;
|
|
int list_size;
|
|
int i, num_luns;
|
|
|
|
if (task->datain.size < 4) {
|
|
return NULL;
|
|
}
|
|
|
|
list_size = task_get_uint32(task, 0) + 8;
|
|
if (list_size > task->datain.size)
|
|
list_size = task->datain.size;
|
|
|
|
num_luns = list_size / 8 - 1;
|
|
list = scsi_malloc(task, offsetof(struct scsi_reportluns_list, luns)
|
|
+ sizeof(uint16_t) * num_luns);
|
|
if (list == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
list->num = num_luns;
|
|
for (i = 0; i < num_luns; i++) {
|
|
list->luns[i] = task_get_uint16(task, i * 8 + 8);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
/*
|
|
* READCAPACITY10
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_readcapacity10(int lba, int pmi)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_READCAPACITY10;
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba);
|
|
|
|
if (pmi) {
|
|
task->cdb[8] |= 0x01;
|
|
}
|
|
|
|
task->cdb_size = 10;
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
task->expxferlen = 8;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* READDEFECTDATA10
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_readdefectdata10(int req_plist, int req_glist, int defect_list_format,
|
|
uint16_t alloc_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_READ_DEFECT_DATA10;
|
|
|
|
if (req_plist) {
|
|
task->cdb[2] |= 0x10;
|
|
}
|
|
if (req_glist) {
|
|
task->cdb[2] |= 0x08;
|
|
}
|
|
task->cdb[2] |= (defect_list_format & 0x07);
|
|
|
|
scsi_set_uint16(&task->cdb[7], alloc_len);
|
|
|
|
task->cdb_size = 10;
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
task->expxferlen = alloc_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* READDEFECTDATA12
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_readdefectdata12(int req_plist, int req_glist, int defect_list_format,
|
|
uint32_t address_descriptor_index, uint32_t alloc_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_READ_DEFECT_DATA12;
|
|
|
|
if (req_plist) {
|
|
task->cdb[2] |= 0x10;
|
|
}
|
|
if (req_glist) {
|
|
task->cdb[2] |= 0x08;
|
|
}
|
|
task->cdb[2] |= (defect_list_format & 0x07);
|
|
|
|
scsi_set_uint32(&task->cdb[2], address_descriptor_index);
|
|
scsi_set_uint32(&task->cdb[6], alloc_len);
|
|
|
|
task->cdb_size = 12;
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
task->expxferlen = alloc_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* READTOC
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_readtoc(int msf, int format, int track_session, uint16_t alloc_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
if (format != SCSI_READ_TOC && format != SCSI_READ_SESSION_INFO
|
|
&& format != SCSI_READ_FULL_TOC){
|
|
fprintf(stderr, "Read TOC format %d not fully supported yet\n", format);
|
|
return NULL;
|
|
}
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_READTOC;
|
|
|
|
if (msf) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
task->cdb[2] = format & 0xf;
|
|
|
|
/* Prevent invalid setting of Track/Session Number */
|
|
if (format == SCSI_READ_TOC || format == SCSI_READ_FULL_TOC) {
|
|
task->cdb[6] = 0xff & track_session;
|
|
}
|
|
|
|
scsi_set_uint16(&task->cdb[7], alloc_len);
|
|
|
|
task->cdb_size = 10;
|
|
if (alloc_len != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = alloc_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* parse the data in blob and calculate the size of a full read TOC
|
|
* datain structure
|
|
*/
|
|
static int
|
|
scsi_readtoc_datain_getfullsize(struct scsi_task *task)
|
|
{
|
|
uint16_t toc_data_len;
|
|
|
|
toc_data_len = task_get_uint16(task, 0) + 2;
|
|
|
|
return toc_data_len;
|
|
}
|
|
|
|
static inline enum scsi_readtoc_fmt
|
|
scsi_readtoc_format(const struct scsi_task *task)
|
|
{
|
|
return task->cdb[2] & 0xf;
|
|
}
|
|
|
|
static void
|
|
scsi_readtoc_desc_unmarshall(struct scsi_task *task, struct scsi_readtoc_list *list, int i)
|
|
{
|
|
switch(scsi_readtoc_format(task)) {
|
|
case SCSI_READ_TOC:
|
|
list->desc[i].desc.toc.adr
|
|
= task_get_uint8(task, 4 + 8 * i + 1) & 0xf0;
|
|
list->desc[i].desc.toc.control
|
|
= task_get_uint8(task, 4 + 8 * i + 1) & 0x0f;
|
|
list->desc[i].desc.toc.track
|
|
= task_get_uint8(task, 4 + 8 * i + 2);
|
|
list->desc[i].desc.toc.lba
|
|
= task_get_uint32(task, 4 + 8 * i + 4);
|
|
break;
|
|
case SCSI_READ_SESSION_INFO:
|
|
list->desc[i].desc.ses.adr
|
|
= task_get_uint8(task, 4 + 8 * i + 1) & 0xf0;
|
|
list->desc[i].desc.ses.control
|
|
= task_get_uint8(task, 4 + 8 * i + 1) & 0x0f;
|
|
list->desc[i].desc.ses.first_in_last
|
|
= task_get_uint8(task, 4 + 8 * i + 2);
|
|
list->desc[i].desc.ses.lba
|
|
= task_get_uint32(task, 4 + 8 * i + 4);
|
|
break;
|
|
case SCSI_READ_FULL_TOC:
|
|
list->desc[i].desc.full.session
|
|
= task_get_uint8(task, 4 + 11 * i + 0) & 0xf0;
|
|
list->desc[i].desc.full.adr
|
|
= task_get_uint8(task, 4 + 11 * i + 1) & 0xf0;
|
|
list->desc[i].desc.full.control
|
|
= task_get_uint8(task, 4 + 11 * i + 1) & 0x0f;
|
|
list->desc[i].desc.full.tno
|
|
= task_get_uint8(task, 4 + 11 * i + 2);
|
|
list->desc[i].desc.full.point
|
|
= task_get_uint8(task, 4 + 11 * i + 3);
|
|
list->desc[i].desc.full.min
|
|
= task_get_uint8(task, 4 + 11 * i + 4);
|
|
list->desc[i].desc.full.sec
|
|
= task_get_uint8(task, 4 + 11 * i + 5);
|
|
list->desc[i].desc.full.frame
|
|
= task_get_uint8(task, 4 + 11 * i + 6);
|
|
list->desc[i].desc.full.zero
|
|
= task_get_uint8(task, 4 + 11 * i + 7);
|
|
list->desc[i].desc.full.pmin
|
|
= task_get_uint8(task, 4 + 11 * i + 8);
|
|
list->desc[i].desc.full.psec
|
|
= task_get_uint8(task, 4 + 11 * i + 9);
|
|
list->desc[i].desc.full.pframe
|
|
= task_get_uint8(task, 4 + 11 * i + 10);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* unmarshall the data in blob for read TOC into a structure
|
|
*/
|
|
static struct scsi_readtoc_list *
|
|
scsi_readtoc_datain_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_readtoc_list *list;
|
|
int data_len;
|
|
int i, num_desc;
|
|
|
|
if (task->datain.size < 4) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Do we have all data? */
|
|
data_len = scsi_readtoc_datain_getfullsize(task) - 2;
|
|
if(task->datain.size < data_len) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Remove header size (4) to get bytes in descriptor list */
|
|
num_desc = (data_len - 4) / 8;
|
|
|
|
list = scsi_malloc(task, offsetof(struct scsi_readtoc_list, desc)
|
|
+ sizeof(struct scsi_readtoc_desc) * num_desc);
|
|
if (list == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
list->num = num_desc;
|
|
list->first = task_get_uint8(task, 2);
|
|
list->last = task_get_uint8(task, 3);
|
|
|
|
for (i = 0; i < num_desc; i++) {
|
|
scsi_readtoc_desc_unmarshall(task, list, i);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
/*
|
|
* RESERVE6
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_reserve6(void)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_RESERVE6;
|
|
|
|
task->cdb_size = 6;
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
|
|
return task;
|
|
}
|
|
/*
|
|
* RELEASE10
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_release6(void)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_RELEASE6;
|
|
|
|
task->cdb_size = 6;
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
|
|
return task;
|
|
}
|
|
|
|
static inline uint8_t
|
|
scsi_serviceactionin_sa(const struct scsi_task *task)
|
|
{
|
|
return task->cdb[1];
|
|
}
|
|
|
|
/*
|
|
* service_action_in unmarshall
|
|
*/
|
|
static void *
|
|
scsi_serviceactionin_datain_unmarshall(struct scsi_task *task)
|
|
{
|
|
switch (scsi_serviceactionin_sa(task)) {
|
|
case SCSI_READCAPACITY16: {
|
|
struct scsi_readcapacity16 *rc16 = scsi_malloc(task,
|
|
sizeof(*rc16));
|
|
if (rc16 == NULL) {
|
|
return NULL;
|
|
}
|
|
rc16->returned_lba = task_get_uint32(task, 0);
|
|
rc16->returned_lba = (rc16->returned_lba << 32) | task_get_uint32(task, 4);
|
|
rc16->block_length = task_get_uint32(task, 8);
|
|
rc16->p_type = (task_get_uint8(task, 12) >> 1) & 0x07;
|
|
rc16->prot_en = task_get_uint8(task, 12) & 0x01;
|
|
rc16->p_i_exp = (task_get_uint8(task, 13) >> 4) & 0x0f;
|
|
rc16->lbppbe = task_get_uint8(task, 13) & 0x0f;
|
|
rc16->lbpme = !!(task_get_uint8(task, 14) & 0x80);
|
|
rc16->lbprz = !!(task_get_uint8(task, 14) & 0x40);
|
|
rc16->lalba = task_get_uint16(task, 14) & 0x3fff;
|
|
return rc16;
|
|
}
|
|
case SCSI_GET_LBA_STATUS: {
|
|
struct scsi_get_lba_status *gls = scsi_malloc(task,
|
|
sizeof(*gls));
|
|
int32_t len = task_get_uint32(task, 0);
|
|
int i;
|
|
|
|
if (gls == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (len > task->datain.size - 4) {
|
|
len = task->datain.size - 4;
|
|
}
|
|
len = len / 16;
|
|
|
|
gls->num_descriptors = len;
|
|
gls->descriptors = scsi_malloc(task,
|
|
sizeof(*gls->descriptors) * len);
|
|
if (gls->descriptors == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < (int)gls->num_descriptors; i++) {
|
|
gls->descriptors[i].lba = task_get_uint32(task, 8 + i * sizeof(struct scsi_lba_status_descriptor) + 0);
|
|
gls->descriptors[i].lba <<= 32;
|
|
gls->descriptors[i].lba |= task_get_uint32(task, 8 + i * sizeof(struct scsi_lba_status_descriptor) + 4);
|
|
|
|
gls->descriptors[i].num_blocks = task_get_uint32(task, 8 + i * sizeof(struct scsi_lba_status_descriptor) + 8);
|
|
|
|
gls->descriptors[i].provisioning = task_get_uint8(task, 8 + i * sizeof(struct scsi_lba_status_descriptor) + 12) & 0x0f;
|
|
}
|
|
|
|
return gls;
|
|
}
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* persistent_reserve_in unmarshall
|
|
*/
|
|
static inline uint8_t
|
|
scsi_persistentreservein_sa(const struct scsi_task *task)
|
|
{
|
|
return task->cdb[1] & 0x1f;
|
|
}
|
|
|
|
static int
|
|
scsi_persistentreservein_datain_getfullsize(struct scsi_task *task)
|
|
{
|
|
switch (scsi_persistentreservein_sa(task)) {
|
|
case SCSI_PERSISTENT_RESERVE_READ_KEYS:
|
|
return task_get_uint32(task, 4) + 8;
|
|
case SCSI_PERSISTENT_RESERVE_READ_RESERVATION:
|
|
return 8;
|
|
case SCSI_PERSISTENT_RESERVE_REPORT_CAPABILITIES:
|
|
return 8;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static void *
|
|
scsi_receivecopyresults_datain_unmarshall(struct scsi_task *task)
|
|
{
|
|
int sa = task->cdb[1] & 0x1f;
|
|
int len, i;
|
|
struct scsi_copy_results_copy_status *cs;
|
|
struct scsi_copy_results_op_params *op;
|
|
|
|
switch (sa) {
|
|
case SCSI_COPY_RESULTS_COPY_STATUS:
|
|
len = task_get_uint32(task, 0);
|
|
if (len < 8)
|
|
return NULL;
|
|
cs = scsi_malloc(task, sizeof(*cs));
|
|
if (cs == NULL) {
|
|
return NULL;
|
|
}
|
|
cs->available_data = len;
|
|
cs->copy_manager_status = task_get_uint8(task, 4) & 0x7F;
|
|
cs->hdd = (task_get_uint8(task, 4) & 0x80) >> 7;
|
|
cs->segments_processed = task_get_uint16(task, 5);
|
|
cs->transfer_count_units = task_get_uint8(task, 7);
|
|
cs->transfer_count = task_get_uint32(task, 8);
|
|
return cs;
|
|
|
|
case SCSI_COPY_RESULTS_OP_PARAMS:
|
|
len = task_get_uint32(task, 0);
|
|
if (len < 40)
|
|
return NULL;
|
|
op = scsi_malloc(task, sizeof(*op) + task_get_uint8(task, 43));
|
|
if (op == NULL) {
|
|
return NULL;
|
|
}
|
|
op->available_data = len;
|
|
op->max_target_desc_count = task_get_uint16(task, 8);
|
|
op->max_segment_desc_count = task_get_uint16(task, 10);
|
|
op->max_desc_list_length = task_get_uint32(task, 12);
|
|
op->max_segment_length = task_get_uint32(task, 16);
|
|
op->max_inline_data_length = task_get_uint32(task, 20);
|
|
op->held_data_limit = task_get_uint32(task, 24);
|
|
op->max_stream_device_transfer_size = task_get_uint32(task, 28);
|
|
op->total_concurrent_copies = task_get_uint16(task, 34);
|
|
op->max_concurrent_copies = task_get_uint8(task, 36);
|
|
op->data_segment_granularity = task_get_uint8(task, 37);
|
|
op->inline_data_granularity = task_get_uint8(task, 38);
|
|
op->held_data_granularity = task_get_uint8(task, 39);
|
|
op->impl_desc_list_length = task_get_uint8(task, 43);
|
|
for (i = 0; i < (int)op->impl_desc_list_length; i++) {
|
|
op->imp_desc_type_codes[i] = task_get_uint8(task, 44+i);
|
|
}
|
|
return op;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
#ifndef MIN /* instead of including all of iscsi-private.h */
|
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
|
#endif
|
|
static void *
|
|
scsi_persistentreservein_datain_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_persistent_reserve_in_read_keys *rk;
|
|
struct scsi_persistent_reserve_in_read_reservation *rr;
|
|
struct scsi_persistent_reserve_in_report_capabilities *rc;
|
|
int i;
|
|
|
|
switch (scsi_persistentreservein_sa(task)) {
|
|
case SCSI_PERSISTENT_RESERVE_READ_KEYS: {
|
|
uint32_t cdb_keys_len;
|
|
uint32_t data_keys_len;
|
|
uint32_t keys_len;
|
|
|
|
if (task->datain.size < 8) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* SPC5r17: 6.16.2 READ KEYS service action
|
|
* The ADDITIONAL LENGTH field indicates the number of bytes in
|
|
* the Reservation key list. The contents of the ADDITIONAL
|
|
* LENGTH field are not altered based on the allocation length.
|
|
*/
|
|
cdb_keys_len = task_get_uint32(task, 4);
|
|
data_keys_len = task->datain.size - 8;
|
|
/*
|
|
* Only process as many keys as permitted by the given
|
|
* ADDITIONAL LENGTH and data-in size limits.
|
|
*/
|
|
keys_len = MIN(cdb_keys_len, data_keys_len);
|
|
|
|
rk = scsi_malloc(task,
|
|
offsetof(struct scsi_persistent_reserve_in_read_keys,
|
|
keys) + keys_len);
|
|
if (rk == NULL) {
|
|
return NULL;
|
|
}
|
|
rk->prgeneration = task_get_uint32(task, 0);
|
|
rk->additional_length = cdb_keys_len;
|
|
|
|
rk->num_keys = keys_len / 8;
|
|
for (i = 0; i < (int)rk->num_keys; i++) {
|
|
rk->keys[i] = task_get_uint64(task, 8 + i * 8);
|
|
}
|
|
return rk;
|
|
}
|
|
case SCSI_PERSISTENT_RESERVE_READ_RESERVATION: {
|
|
size_t alloc_sz;
|
|
|
|
i = task_get_uint32(task, 4);
|
|
alloc_sz = sizeof(struct scsi_persistent_reserve_in_read_reservation);
|
|
|
|
rr = scsi_malloc(task, alloc_sz);
|
|
if (rr == NULL) {
|
|
return NULL;
|
|
}
|
|
memset(rr, 0, alloc_sz);
|
|
rr->prgeneration = task_get_uint32(task, 0);
|
|
|
|
if (i > 0) {
|
|
rr->reserved = 1;
|
|
rr->reservation_key =
|
|
task_get_uint64(task, 8);
|
|
rr->pr_scope = task_get_uint8(task, 21) >> 4;
|
|
rr->pr_type = task_get_uint8(task, 21) & 0xf;
|
|
}
|
|
|
|
return rr;
|
|
}
|
|
case SCSI_PERSISTENT_RESERVE_REPORT_CAPABILITIES:
|
|
rc = scsi_malloc(task, sizeof(struct scsi_persistent_reserve_in_report_capabilities));
|
|
if (rc == NULL) {
|
|
return NULL;
|
|
}
|
|
rc->length = task_get_uint16(task, 0);
|
|
rc->crh = !!(task_get_uint8(task, 2) & 0x10);
|
|
rc->sip_c = !!(task_get_uint8(task, 2) & 0x08);
|
|
rc->atp_c = !!(task_get_uint8(task, 2) & 0x04);
|
|
rc->ptpl_c = !!(task_get_uint8(task, 2) & 0x01);
|
|
rc->tmv = !!(task_get_uint8(task, 3) & 0x80);
|
|
rc->allow_commands = (task_get_uint8(task, 3) & 0x70) >> 4;
|
|
rc->persistent_reservation_type_mask = task_get_uint16(task, 4);
|
|
|
|
return rc;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static inline uint8_t
|
|
scsi_maintenancein_sa(const struct scsi_task *task)
|
|
{
|
|
return task->cdb[1];
|
|
}
|
|
|
|
static inline uint8_t
|
|
scsi_report_supported_opcodes_options(const struct scsi_task *task)
|
|
{
|
|
return task->cdb[2] & 0x07;
|
|
}
|
|
|
|
/*
|
|
* parse the data in blob and calculate the size of a full maintenancein
|
|
* datain structure
|
|
*/
|
|
static int
|
|
scsi_maintenancein_datain_getfullsize(struct scsi_task *task)
|
|
{
|
|
|
|
switch (scsi_maintenancein_sa(task)) {
|
|
case SCSI_REPORT_SUPPORTED_OP_CODES:
|
|
switch (scsi_report_supported_opcodes_options(task)) {
|
|
case SCSI_REPORT_SUPPORTING_OPS_ALL:
|
|
return task_get_uint32(task, 0) + 4;
|
|
case SCSI_REPORT_SUPPORTING_OPCODE:
|
|
case SCSI_REPORT_SUPPORTING_SERVICEACTION:
|
|
return 4 +
|
|
((task_get_uint8(task, 1) & 0x80) ? 12 : 0) +
|
|
task_get_uint16(task, 2);
|
|
}
|
|
return -1;
|
|
case SCSI_REPORT_TARGET_PORT_GROUPS:
|
|
return task_get_uint32(task, 0) + 4;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static struct scsi_report_target_port_groups *
|
|
scsi_report_target_port_groups_unmarshal(struct scsi_task *task)
|
|
{
|
|
int const group_descriptor_size = 8;
|
|
int const port_descriptor_size = 4;
|
|
|
|
struct scsi_report_target_port_groups *rtpg = NULL;
|
|
uint16_t *port = NULL;
|
|
int group_count, port_count, i, j, k;
|
|
|
|
if (task->datain.size < 4) {
|
|
return NULL;
|
|
}
|
|
|
|
port_count= 0;
|
|
group_count = 0;
|
|
for (j = 0; j < 2; ++j) {
|
|
/* 1st pass counts groups and ports, then allocates data structs to fit those;
|
|
* 2nd pass populates the allocated data structs.*/
|
|
for (i = 4; i< task->datain.size; ) {
|
|
uint8_t current_port_count;
|
|
|
|
if (task->datain.size - i < group_descriptor_size) {
|
|
break;
|
|
}
|
|
current_port_count = task_get_uint8(task, i + 7);
|
|
|
|
if (j == 1) {
|
|
rtpg->groups[group_count].port_count = current_port_count;
|
|
rtpg->groups[group_count].byte0 = task_get_uint8(task, i);
|
|
rtpg->groups[group_count].flags = task_get_uint8(task, i + 1);
|
|
rtpg->groups[group_count].port_group = task_get_uint16(task, i + 2);
|
|
rtpg->groups[group_count].status_code = task_get_uint8(task, i + 5);
|
|
rtpg->groups[group_count].ports = port;
|
|
}
|
|
|
|
i += group_descriptor_size;
|
|
for (k = 0; k < current_port_count &&
|
|
i + (k + 1) * port_descriptor_size <= task->datain.size; ++k) {
|
|
if (j == 1) {
|
|
rtpg->groups[group_count].ports[k] =
|
|
task_get_uint16(task, i + k * port_descriptor_size + 2);
|
|
}
|
|
}
|
|
if (j == 1) {
|
|
rtpg->groups[group_count].retrieved_port_count = k;
|
|
port += k;
|
|
}
|
|
++group_count;
|
|
port_count += k;
|
|
i += k * port_descriptor_size;
|
|
}
|
|
|
|
if (j == 0) {
|
|
rtpg = scsi_malloc(
|
|
task,
|
|
sizeof(struct scsi_report_target_port_groups) +
|
|
sizeof(struct scsi_target_port_group) * group_count +
|
|
sizeof(uint16_t) * port_count);
|
|
if (rtpg == NULL) {
|
|
return NULL;
|
|
}
|
|
port = (uint16_t *)((uint8_t *)rtpg +
|
|
sizeof(struct scsi_report_target_port_groups) +
|
|
sizeof(struct scsi_target_port_group) * group_count);
|
|
rtpg->num_groups = group_count;
|
|
group_count = 0;
|
|
port_count = 0;
|
|
}
|
|
}
|
|
|
|
return rtpg;
|
|
}
|
|
|
|
/*
|
|
* maintenance_in unmarshall
|
|
*/
|
|
static void *
|
|
scsi_maintenancein_datain_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_report_supported_op_codes *rsoc;
|
|
struct scsi_report_supported_op_codes_one_command *rsoc_one;
|
|
int len, i;
|
|
|
|
switch (scsi_maintenancein_sa(task)) {
|
|
case SCSI_REPORT_SUPPORTED_OP_CODES:
|
|
switch (scsi_report_supported_opcodes_options(task)) {
|
|
case SCSI_REPORT_SUPPORTING_OPS_ALL:
|
|
if (task->datain.size < 4) {
|
|
return NULL;
|
|
}
|
|
|
|
len = task_get_uint32(task, 0);
|
|
/* len / 8 is not always correct since if CTDP==1 then
|
|
* the descriptor is 20 bytes in size intead of 8.
|
|
* It doesnt matter here though since it just means
|
|
* we would allocate more descriptors at the end of
|
|
* the structure than we strictly need. This avoids
|
|
* having to traverse the datain buffer twice.
|
|
*/
|
|
rsoc = scsi_malloc(task,
|
|
offsetof(struct scsi_report_supported_op_codes,
|
|
descriptors) +
|
|
len / 8 * sizeof(struct scsi_command_descriptor));
|
|
if (rsoc == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
rsoc->num_descriptors = 0;
|
|
i = 4;
|
|
while (len >= 8) {
|
|
struct scsi_command_descriptor *desc;
|
|
|
|
desc = &rsoc->descriptors[rsoc->num_descriptors++];
|
|
desc->opcode =
|
|
task_get_uint8(task, i);
|
|
desc->sa =
|
|
task_get_uint16(task, i + 2);
|
|
desc->ctdp =
|
|
!!(task_get_uint8(task, i + 5) & 0x02);
|
|
desc->servactv =
|
|
!!(task_get_uint8(task, i + 5) & 0x01);
|
|
desc->cdb_len =
|
|
task_get_uint16(task, i + 6);
|
|
|
|
len -= 8;
|
|
i += 8;
|
|
|
|
/* No tiemout description */
|
|
if (!desc->ctdp) {
|
|
continue;
|
|
}
|
|
|
|
desc->to.descriptor_length =
|
|
task_get_uint16(task, i);
|
|
desc->to.command_specific =
|
|
task_get_uint8(task, i + 3);
|
|
desc->to.nominal_processing_timeout =
|
|
task_get_uint32(task, i + 4);
|
|
desc->to.recommended_timeout =
|
|
task_get_uint32(task, i + 8);
|
|
|
|
len -= desc->to.descriptor_length + 2;
|
|
i += desc->to.descriptor_length + 2;
|
|
}
|
|
return rsoc;
|
|
case SCSI_REPORT_SUPPORTING_OPCODE:
|
|
case SCSI_REPORT_SUPPORTING_SERVICEACTION:
|
|
rsoc_one = scsi_malloc(task, sizeof(struct scsi_report_supported_op_codes_one_command));
|
|
if (rsoc_one == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
rsoc_one->ctdp =
|
|
!!(task_get_uint8(task, 1) & 0x80);
|
|
rsoc_one->support =
|
|
task_get_uint8(task, 1) & 0x07;
|
|
rsoc_one->cdb_length =
|
|
task_get_uint16(task, 2);
|
|
if (rsoc_one->cdb_length <=
|
|
sizeof(rsoc_one->cdb_usage_data)) {
|
|
memcpy(rsoc_one->cdb_usage_data,
|
|
&task->datain.data[4],
|
|
rsoc_one->cdb_length);
|
|
}
|
|
|
|
if (rsoc_one->ctdp) {
|
|
i = 4 + rsoc_one->cdb_length;
|
|
|
|
rsoc_one->to.descriptor_length =
|
|
task_get_uint16(task, i);
|
|
rsoc_one->to.command_specific =
|
|
task_get_uint8(task, i + 3);
|
|
rsoc_one->to.nominal_processing_timeout =
|
|
task_get_uint32(task, i + 4);
|
|
rsoc_one->to.recommended_timeout =
|
|
task_get_uint32(task, i + 8);
|
|
}
|
|
return rsoc_one;
|
|
}
|
|
|
|
break;
|
|
case SCSI_REPORT_TARGET_PORT_GROUPS:
|
|
return scsi_report_target_port_groups_unmarshal(task);
|
|
};
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* MAINTENANCE In / Report Target Port Groups
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_report_target_port_groups(uint32_t alloc_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_MAINTENANCE_IN;
|
|
task->cdb[1] = SCSI_REPORT_TARGET_PORT_GROUPS;
|
|
|
|
scsi_set_uint32(&task->cdb[6], alloc_len);
|
|
|
|
task->cdb_size = 12;
|
|
if (alloc_len != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = alloc_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* MAINTENANCE In / Read Supported Op Codes
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_report_supported_opcodes(int rctd, int options, enum scsi_opcode opcode, int sa, uint32_t alloc_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_MAINTENANCE_IN;
|
|
task->cdb[1] = SCSI_REPORT_SUPPORTED_OP_CODES;
|
|
task->cdb[2] = options & 0x07;
|
|
|
|
if (rctd) {
|
|
task->cdb[2] |= 0x80;
|
|
}
|
|
|
|
task->cdb[3] = opcode;
|
|
|
|
scsi_set_uint16(&task->cdb[4], sa);
|
|
|
|
scsi_set_uint32(&task->cdb[6], alloc_len);
|
|
|
|
task->cdb_size = 12;
|
|
if (alloc_len != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = alloc_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* parse the data in blob and calculate the size of a full
|
|
* readcapacity10 datain structure
|
|
*/
|
|
static int
|
|
scsi_readcapacity10_datain_getfullsize(struct scsi_task *task)
|
|
{
|
|
return 8;
|
|
}
|
|
|
|
/*
|
|
* unmarshall the data in blob for readcapacity10 into a structure
|
|
*/
|
|
static struct scsi_readcapacity10 *
|
|
scsi_readcapacity10_datain_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_readcapacity10 *rc10;
|
|
|
|
if (task->datain.size < 8) {
|
|
return NULL;
|
|
}
|
|
rc10 = scsi_malloc(task, sizeof(struct scsi_readcapacity10));
|
|
if (rc10 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
rc10->lba = task_get_uint32(task, 0);
|
|
rc10->block_size = task_get_uint32(task, 4);
|
|
|
|
return rc10;
|
|
}
|
|
|
|
/*
|
|
* INQUIRY
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_inquiry(int evpd, int page_code, int alloc_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_INQUIRY;
|
|
|
|
if (evpd) {
|
|
task->cdb[1] |= 0x01;
|
|
}
|
|
|
|
task->cdb[2] = page_code;
|
|
|
|
scsi_set_uint16(&task->cdb[3], alloc_len);
|
|
|
|
task->cdb_size = 6;
|
|
if (alloc_len != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = alloc_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
static inline int
|
|
scsi_inquiry_evpd_set(const struct scsi_task *task)
|
|
{
|
|
return task->cdb[1] & 0x1;
|
|
}
|
|
|
|
static inline uint8_t
|
|
scsi_inquiry_page_code(const struct scsi_task *task)
|
|
{
|
|
return task->cdb[2];
|
|
}
|
|
|
|
/*
|
|
* parse the data in blob and calculate the size of a full
|
|
* inquiry datain structure
|
|
*/
|
|
static int
|
|
scsi_inquiry_datain_getfullsize(struct scsi_task *task)
|
|
{
|
|
if (scsi_inquiry_evpd_set(task) == 0) {
|
|
return task_get_uint8(task, 4) + 5;
|
|
}
|
|
|
|
switch (scsi_inquiry_page_code(task)) {
|
|
case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES:
|
|
case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS:
|
|
case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER:
|
|
return task_get_uint8(task, 3) + 4;
|
|
case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
|
|
case SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS:
|
|
case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
|
|
return task_get_uint16(task, 2) + 4;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static struct scsi_inquiry_standard *
|
|
scsi_inquiry_unmarshall_standard(struct scsi_task *task)
|
|
{
|
|
int i;
|
|
|
|
struct scsi_inquiry_standard *inq = scsi_malloc(task, sizeof(*inq));
|
|
if (inq == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07;
|
|
inq->device_type = task_get_uint8(task, 0) & 0x1f;
|
|
inq->rmb = !!(task_get_uint8(task, 1) & 0x80);
|
|
inq->version = task_get_uint8(task, 2);
|
|
inq->normaca = !!(task_get_uint8(task, 3) & 0x20);
|
|
inq->hisup = !!(task_get_uint8(task, 3) & 0x10);
|
|
inq->response_data_format = task_get_uint8(task, 3) & 0x0f;
|
|
|
|
inq->additional_length = task_get_uint8(task, 4);
|
|
|
|
inq->sccs = !!(task_get_uint8(task, 5) & 0x80);
|
|
inq->acc = !!(task_get_uint8(task, 5) & 0x40);
|
|
inq->tpgs = (task_get_uint8(task, 5) >> 4) & 0x03;
|
|
inq->threepc = !!(task_get_uint8(task, 5) & 0x08);
|
|
inq->protect = !!(task_get_uint8(task, 5) & 0x01);
|
|
|
|
inq->encserv = !!(task_get_uint8(task, 6) & 0x40);
|
|
inq->multip = !!(task_get_uint8(task, 6) & 0x10);
|
|
inq->addr16 = !!(task_get_uint8(task, 6) & 0x01);
|
|
inq->wbus16 = !!(task_get_uint8(task, 7) & 0x20);
|
|
inq->sync = !!(task_get_uint8(task, 7) & 0x10);
|
|
inq->cmdque = !!(task_get_uint8(task, 7) & 0x02);
|
|
|
|
memcpy(&inq->vendor_identification[0],
|
|
&task->datain.data[8], 8);
|
|
memcpy(&inq->product_identification[0],
|
|
&task->datain.data[16], 16);
|
|
memcpy(&inq->product_revision_level[0],
|
|
&task->datain.data[32], 4);
|
|
|
|
inq->clocking = (task_get_uint8(task, 56) >> 2) & 0x03;
|
|
inq->qas = !!(task_get_uint8(task, 56) & 0x02);
|
|
inq->ius = !!(task_get_uint8(task, 56) & 0x01);
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
inq->version_descriptor[i] = task_get_uint16(task, 58 + i * 2);
|
|
}
|
|
|
|
return inq;
|
|
}
|
|
|
|
static struct scsi_inquiry_supported_pages *
|
|
scsi_inquiry_unmarshall_supported_pages(struct scsi_task *task)
|
|
{
|
|
struct scsi_inquiry_supported_pages *inq = scsi_malloc(task,
|
|
sizeof(*inq));
|
|
if (inq == NULL) {
|
|
return NULL;
|
|
}
|
|
inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07;
|
|
inq->device_type = task_get_uint8(task, 0) & 0x1f;
|
|
inq->pagecode = task_get_uint8(task, 1);
|
|
|
|
inq->num_pages = task_get_uint8(task, 3);
|
|
inq->pages = scsi_malloc(task, inq->num_pages);
|
|
if (inq->pages == NULL) {
|
|
return NULL;
|
|
}
|
|
memcpy(inq->pages, &task->datain.data[4], inq->num_pages);
|
|
return inq;
|
|
}
|
|
|
|
static struct scsi_inquiry_unit_serial_number *
|
|
scsi_inquiry_unmarshall_unit_serial_number(struct scsi_task* task)
|
|
{
|
|
struct scsi_inquiry_unit_serial_number *inq = scsi_malloc(task,
|
|
sizeof(*inq));
|
|
if (inq == NULL) {
|
|
return NULL;
|
|
}
|
|
inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07;
|
|
inq->device_type = task_get_uint8(task, 0) & 0x1f;
|
|
inq->pagecode = task_get_uint8(task, 1);
|
|
|
|
inq->usn = scsi_malloc(task, task_get_uint8(task, 3) + 1);
|
|
if (inq->usn == NULL) {
|
|
return NULL;
|
|
}
|
|
memcpy(inq->usn, &task->datain.data[4], task_get_uint8(task, 3));
|
|
inq->usn[task_get_uint8(task, 3)] = 0;
|
|
return inq;
|
|
}
|
|
|
|
static struct scsi_inquiry_device_identification *
|
|
scsi_inquiry_unmarshall_device_identification(struct scsi_task *task)
|
|
{
|
|
struct scsi_inquiry_device_identification *inq = scsi_malloc(task,
|
|
sizeof(*inq));
|
|
int remaining = task_get_uint16(task, 2);
|
|
unsigned char *dptr;
|
|
|
|
if (inq == NULL) {
|
|
return NULL;
|
|
}
|
|
inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07;
|
|
inq->device_type = task_get_uint8(task, 0) & 0x1f;
|
|
inq->pagecode = task_get_uint8(task, 1);
|
|
|
|
dptr = &task->datain.data[4];
|
|
while (remaining > 0) {
|
|
struct scsi_inquiry_device_designator *dev =
|
|
scsi_malloc(task, sizeof(*dev));
|
|
if (dev == NULL) {
|
|
goto err;
|
|
}
|
|
|
|
dev->next = inq->designators;
|
|
inq->designators = dev;
|
|
|
|
dev->protocol_identifier = (dptr[0]>>4) & 0x0f;
|
|
dev->code_set = dptr[0] & 0x0f;
|
|
dev->piv = !!(dptr[1]&0x80);
|
|
dev->association = (dptr[1]>>4)&0x03;
|
|
dev->designator_type = dptr[1]&0x0f;
|
|
|
|
dev->designator_length = dptr[3];
|
|
dev->designator = scsi_malloc(task, dev->designator_length + 1);
|
|
if (dev->designator == NULL) {
|
|
goto err;
|
|
}
|
|
dev->designator[dev->designator_length] = 0;
|
|
memcpy(dev->designator, &dptr[4],
|
|
dev->designator_length);
|
|
|
|
remaining -= 4;
|
|
remaining -= dev->designator_length;
|
|
|
|
dptr += dev->designator_length + 4;
|
|
}
|
|
return inq;
|
|
|
|
err:
|
|
while (inq->designators) {
|
|
struct scsi_inquiry_device_designator *dev = inq->designators;
|
|
inq->designators = dev->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct third_party_copy_supported_commands *
|
|
third_party_copy_unmarshall_supported_commands(struct scsi_task *task,
|
|
unsigned char *dptr)
|
|
{
|
|
struct third_party_copy_supported_commands *supported_commands =
|
|
scsi_malloc(task, sizeof(*supported_commands));
|
|
int remaining;
|
|
unsigned char *lptr;
|
|
|
|
if (supported_commands == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
supported_commands->descriptor_type = scsi_get_uint16(&dptr[0]);
|
|
|
|
remaining = dptr[4];
|
|
lptr = &dptr[5];
|
|
while (remaining > 0) {
|
|
struct third_party_copy_command_support *command =
|
|
scsi_malloc(task, sizeof(*command));
|
|
int i;
|
|
|
|
if (command == NULL) {
|
|
goto err;
|
|
}
|
|
|
|
command->next = supported_commands->commands_supported;
|
|
supported_commands->commands_supported = command;
|
|
|
|
command->operation_code = lptr[0];
|
|
command->service_action_length = lptr[1];
|
|
command->service_action = scsi_malloc(task,
|
|
sizeof(*command->service_action) * (command->service_action_length + 1));
|
|
if (command->service_action == NULL) {
|
|
goto err;
|
|
}
|
|
command->service_action[command->service_action_length] = 0;
|
|
for (i = 0; i < command->service_action_length; i++) {
|
|
command->service_action[i] = lptr[2 + i];
|
|
}
|
|
|
|
remaining -= command->service_action_length + 2;
|
|
lptr += command->service_action_length + 2;
|
|
}
|
|
return supported_commands;
|
|
|
|
err:
|
|
return NULL;
|
|
}
|
|
|
|
static struct scsi_inquiry_third_party_copy *
|
|
scsi_inquiry_unmarshall_third_party_copy(struct scsi_task *task)
|
|
{
|
|
struct scsi_inquiry_third_party_copy *inq = scsi_malloc(task,
|
|
sizeof(*inq));
|
|
int remaining;
|
|
unsigned char *dptr;
|
|
|
|
if (inq == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07;
|
|
inq->device_type = task_get_uint8(task, 0) & 0x1f;
|
|
inq->pagecode = task_get_uint8(task, 1);
|
|
|
|
remaining = task_get_uint16(task, 2);
|
|
dptr = &task->datain.data[4];
|
|
while (remaining > 0) {
|
|
int copy_desc_type = scsi_get_uint16(&dptr[0]);
|
|
int copy_desc_len = scsi_get_uint16(&dptr[2]);
|
|
|
|
switch (copy_desc_type) {
|
|
case THIRD_PARTY_COPY_TYPE_SUPPORTED_COMMANDS:
|
|
inq->supported_commands =
|
|
third_party_copy_unmarshall_supported_commands(task, dptr);
|
|
if (inq->supported_commands == NULL) {
|
|
goto err;
|
|
}
|
|
break;
|
|
}
|
|
|
|
remaining -= copy_desc_len + 4;
|
|
dptr += copy_desc_len + 4;
|
|
}
|
|
return inq;
|
|
|
|
err:
|
|
return NULL;
|
|
}
|
|
|
|
static struct scsi_inquiry_block_limits *
|
|
scsi_inquiry_unmarshall_block_limits(struct scsi_task *task)
|
|
{
|
|
struct scsi_inquiry_block_limits *inq = scsi_malloc(task,
|
|
sizeof(*inq));
|
|
if (inq == NULL) {
|
|
return NULL;
|
|
}
|
|
inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07;
|
|
inq->device_type = task_get_uint8(task, 0) & 0x1f;
|
|
inq->pagecode = task_get_uint8(task, 1);
|
|
|
|
inq->wsnz = task_get_uint8(task, 4) & 0x01;
|
|
inq->max_cmp = task_get_uint8(task, 5);
|
|
inq->opt_gran = task_get_uint16(task, 6);
|
|
inq->max_xfer_len = task_get_uint32(task, 8);
|
|
inq->opt_xfer_len = task_get_uint32(task, 12);
|
|
inq->max_prefetch = task_get_uint32(task, 16);
|
|
inq->max_unmap = task_get_uint32(task, 20);
|
|
inq->max_unmap_bdc = task_get_uint32(task, 24);
|
|
inq->opt_unmap_gran = task_get_uint32(task, 28);
|
|
inq->ugavalid = !!(task_get_uint8(task, 32)&0x80);
|
|
inq->unmap_gran_align = task_get_uint32(task, 32) & 0x7fffffff;
|
|
inq->max_ws_len = task_get_uint32(task, 36);
|
|
inq->max_ws_len = (inq->max_ws_len << 32)
|
|
| task_get_uint32(task, 40);
|
|
|
|
inq->max_atomic_xfer_len = task_get_uint32(task, 44);
|
|
inq->atomic_align = task_get_uint32(task, 48);
|
|
inq->atomic_gran = task_get_uint32(task, 52);
|
|
inq->max_atomic_tl_with_atomic_boundary =
|
|
task_get_uint32(task, 56);
|
|
inq->max_atomic_boundary_size =
|
|
task_get_uint32(task, 60);
|
|
|
|
return inq;
|
|
}
|
|
|
|
static struct scsi_inquiry_block_device_characteristics *
|
|
scsi_inquiry_unmarshall_block_device_characteristics(struct scsi_task *task)
|
|
{
|
|
struct scsi_inquiry_block_device_characteristics *inq =
|
|
scsi_malloc(task, sizeof(*inq));
|
|
if (inq == NULL) {
|
|
return NULL;
|
|
}
|
|
inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07;
|
|
inq->device_type = task_get_uint8(task, 0) & 0x1f;
|
|
inq->pagecode = task_get_uint8(task, 1);
|
|
|
|
inq->medium_rotation_rate = task_get_uint16(task, 4);
|
|
inq->product_type = task_get_uint8(task, 6);
|
|
inq->wabereq = (task_get_uint8(task, 7) >> 6) & 0x03;
|
|
inq->wacereq = (task_get_uint8(task, 7) >> 4) & 0x03;
|
|
inq->nominal_form_factor = task_get_uint8(task, 7) & 0x0f;
|
|
inq->fuab = !!(task_get_uint8(task, 8) & 0x02);
|
|
inq->vbuls = !!(task_get_uint8(task, 8) & 0x01);
|
|
return inq;
|
|
}
|
|
|
|
struct scsi_inquiry_logical_block_provisioning *
|
|
scsi_inquiry_unmarshall_logical_block_provisioning(struct scsi_task *task)
|
|
{
|
|
struct scsi_inquiry_logical_block_provisioning *inq =
|
|
scsi_malloc(task, sizeof(*inq));
|
|
if (inq == NULL) {
|
|
return NULL;
|
|
}
|
|
inq->qualifier = (task_get_uint8(task, 0) >> 5) & 0x07;
|
|
inq->device_type = task_get_uint8(task, 0) & 0x1f;
|
|
inq->pagecode = task_get_uint8(task, 1);
|
|
|
|
inq->threshold_exponent = task_get_uint8(task, 4);
|
|
inq->lbpu = !!(task_get_uint8(task, 5) & 0x80);
|
|
inq->lbpws = !!(task_get_uint8(task, 5) & 0x40);
|
|
inq->lbpws10 = !!(task_get_uint8(task, 5) & 0x20);
|
|
inq->lbprz = !!(task_get_uint8(task, 5) & 0x04);
|
|
inq->anc_sup = !!(task_get_uint8(task, 5) & 0x02);
|
|
inq->dp = !!(task_get_uint8(task, 5) & 0x01);
|
|
inq->provisioning_type = task_get_uint8(task, 6) & 0x07;
|
|
|
|
return inq;
|
|
}
|
|
|
|
/*
|
|
* unmarshall the data in blob for inquiry into a structure
|
|
*/
|
|
static void *
|
|
scsi_inquiry_datain_unmarshall(struct scsi_task *task)
|
|
{
|
|
if (scsi_inquiry_evpd_set(task) == 0) {
|
|
return scsi_inquiry_unmarshall_standard(task);
|
|
}
|
|
|
|
switch (scsi_inquiry_page_code(task))
|
|
{
|
|
case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES:
|
|
return scsi_inquiry_unmarshall_supported_pages(task);
|
|
case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER:
|
|
return scsi_inquiry_unmarshall_unit_serial_number(task);
|
|
case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
|
|
return scsi_inquiry_unmarshall_device_identification(task);
|
|
case SCSI_INQUIRY_PAGECODE_THIRD_PARTY_COPY:
|
|
return scsi_inquiry_unmarshall_third_party_copy(task);
|
|
case SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS:
|
|
return scsi_inquiry_unmarshall_block_limits(task);
|
|
case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS:
|
|
return scsi_inquiry_unmarshall_block_device_characteristics(task);
|
|
case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
|
|
return scsi_inquiry_unmarshall_logical_block_provisioning(task);
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* READ6
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_read6(uint32_t lba, uint32_t xferlen, int blocksize)
|
|
{
|
|
struct scsi_task *task;
|
|
int num_blocks;
|
|
|
|
num_blocks = xferlen/blocksize;
|
|
if (num_blocks > 256) {
|
|
return NULL;
|
|
}
|
|
|
|
if (lba > 0x1fffff) {
|
|
return NULL;
|
|
}
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_READ6;
|
|
task->cdb_size = 6;
|
|
|
|
task->cdb[1] = (lba>>16)&0x1f;
|
|
task->cdb[2] = (lba>> 8)&0xff;
|
|
task->cdb[3] = (lba )&0xff;
|
|
|
|
if (num_blocks < 256) {
|
|
task->cdb[4] = num_blocks & 0xff;
|
|
}
|
|
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* READ10
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_read10(uint32_t lba, uint32_t xferlen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_READ10;
|
|
|
|
task->cdb[1] |= ((rdprotect & 0x07) << 5);
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (fua) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
if (fua_nv) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba);
|
|
scsi_set_uint16(&task->cdb[7], xferlen/blocksize);
|
|
|
|
task->cdb[6] |= (group_number & 0x1f);
|
|
|
|
task->cdb_size = 10;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* READ12
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_read12(uint32_t lba, uint32_t xferlen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_READ12;
|
|
|
|
task->cdb[1] |= ((rdprotect & 0x07) << 5);
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (fua) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
if (fua_nv) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba);
|
|
scsi_set_uint32(&task->cdb[6], xferlen/blocksize);
|
|
|
|
task->cdb[10] |= (group_number & 0x1f);
|
|
|
|
task->cdb_size = 12;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* READ16
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_read16(uint64_t lba, uint32_t xferlen, int blocksize, int rdprotect, int dpo, int fua, int fua_nv, int group_number)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_READ16;
|
|
|
|
task->cdb[1] |= ((rdprotect & 0x07) << 5);
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (fua) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
if (fua_nv) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba >> 32);
|
|
scsi_set_uint32(&task->cdb[6], lba & 0xffffffff);
|
|
scsi_set_uint32(&task->cdb[10], xferlen/blocksize);
|
|
|
|
task->cdb[14] |= (group_number & 0x1f);
|
|
|
|
task->cdb_size = 16;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* WRITE10
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_write10(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_WRITE10;
|
|
|
|
task->cdb[1] |= ((wrprotect & 0x07) << 5);
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (fua) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
if (fua_nv) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba);
|
|
scsi_set_uint16(&task->cdb[7], xferlen/blocksize);
|
|
|
|
task->cdb[6] |= (group_number & 0x1f);
|
|
|
|
task->cdb_size = 10;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* WRITE12
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_write12(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_WRITE12;
|
|
|
|
task->cdb[1] |= ((wrprotect & 0x07) << 5);
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (fua) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
if (fua_nv) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba);
|
|
scsi_set_uint32(&task->cdb[6], xferlen/blocksize);
|
|
|
|
task->cdb[10] |= (group_number & 0x1f);
|
|
|
|
task->cdb_size = 12;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* WRITE16
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_write16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_WRITE16;
|
|
|
|
task->cdb[1] |= ((wrprotect & 0x07) << 5);
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (fua) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
if (fua_nv) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba >> 32);
|
|
scsi_set_uint32(&task->cdb[6], lba & 0xffffffff);
|
|
scsi_set_uint32(&task->cdb[10], xferlen / blocksize);
|
|
|
|
task->cdb[14] |= (group_number & 0x1f);
|
|
|
|
task->cdb_size = 16;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* WRITEATOMIC16
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_writeatomic16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int group_number)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_WRITE_ATOMIC16;
|
|
|
|
task->cdb[1] |= ((wrprotect & 0x07) << 5);
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (fua) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba >> 32);
|
|
scsi_set_uint32(&task->cdb[6], lba & 0xffffffff);
|
|
scsi_set_uint16(&task->cdb[12], xferlen / blocksize);
|
|
|
|
task->cdb[14] |= (group_number & 0x1f);
|
|
|
|
task->cdb_size = 16;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* ORWRITE
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_orwrite(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_ORWRITE;
|
|
|
|
task->cdb[1] |= ((wrprotect & 0x07) << 5);
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (fua) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
if (fua_nv) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba >> 32);
|
|
scsi_set_uint32(&task->cdb[6], lba & 0xffffffff);
|
|
scsi_set_uint32(&task->cdb[10], xferlen/blocksize);
|
|
|
|
task->cdb[14] |= (group_number & 0x1f);
|
|
|
|
task->cdb_size = 16;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* COMPAREANDWRITE
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_compareandwrite(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int fua, int fua_nv, int group_number)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_COMPARE_AND_WRITE;
|
|
|
|
task->cdb[1] |= ((wrprotect & 0x07) << 5);
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (fua) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
if (fua_nv) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba >> 32);
|
|
scsi_set_uint32(&task->cdb[6], lba & 0xffffffff);
|
|
task->cdb[13] = xferlen / blocksize / 2;
|
|
|
|
task->cdb[14] |= (group_number & 0x1f);
|
|
task->cdb_size = 16;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* VERIFY10
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_verify10(uint32_t lba, uint32_t xferlen, int vprotect, int dpo, int bytchk, int blocksize)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_VERIFY10;
|
|
|
|
if (vprotect) {
|
|
task->cdb[1] |= ((vprotect << 5) & 0xe0);
|
|
}
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (bytchk) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba);
|
|
scsi_set_uint16(&task->cdb[7], xferlen/blocksize);
|
|
|
|
task->cdb_size = 10;
|
|
if (xferlen != 0 && bytchk) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
task->expxferlen = xferlen;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
task->expxferlen = 0;
|
|
}
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* VERIFY12
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_verify12(uint32_t lba, uint32_t xferlen, int vprotect, int dpo, int bytchk, int blocksize)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_VERIFY12;
|
|
|
|
if (vprotect) {
|
|
task->cdb[1] |= ((vprotect << 5) & 0xe0);
|
|
}
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (bytchk) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba);
|
|
scsi_set_uint32(&task->cdb[6], xferlen/blocksize);
|
|
|
|
task->cdb_size = 12;
|
|
if (xferlen != 0 && bytchk) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
task->expxferlen = xferlen;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
task->expxferlen = 0;
|
|
}
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* VERIFY16
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_verify16(uint64_t lba, uint32_t xferlen, int vprotect, int dpo, int bytchk, int blocksize)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_VERIFY16;
|
|
|
|
if (vprotect) {
|
|
task->cdb[1] |= ((vprotect << 5) & 0xe0);
|
|
}
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (bytchk) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba >> 32);
|
|
scsi_set_uint32(&task->cdb[6], lba & 0xffffffff);
|
|
scsi_set_uint32(&task->cdb[10], xferlen/blocksize);
|
|
|
|
task->cdb_size = 16;
|
|
if (xferlen != 0 && bytchk) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
task->expxferlen = xferlen;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
task->expxferlen = 0;
|
|
}
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* UNMAP
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_unmap(int anchor, int group, uint16_t xferlen)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_UNMAP;
|
|
|
|
if (anchor) {
|
|
task->cdb[1] |= 0x01;
|
|
}
|
|
task->cdb[6] |= group & 0x1f;
|
|
|
|
scsi_set_uint16(&task->cdb[7], xferlen);
|
|
|
|
task->cdb_size = 10;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* PERSISTENT_RESEERVE_IN
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_persistent_reserve_in(enum scsi_persistent_in_sa sa, uint16_t xferlen)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_PERSISTENT_RESERVE_IN;
|
|
|
|
task->cdb[1] |= sa & 0x1f;
|
|
|
|
scsi_set_uint16(&task->cdb[7], xferlen);
|
|
|
|
task->cdb_size = 10;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* PERSISTENT_RESERVE_OUT
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_persistent_reserve_out(enum scsi_persistent_out_sa sa, enum scsi_persistent_out_scope scope, enum scsi_persistent_out_type type, void *param)
|
|
{
|
|
struct scsi_task *task;
|
|
struct scsi_persistent_reserve_out_basic *basic;
|
|
struct scsi_iovec *iov;
|
|
unsigned char *buf;
|
|
int xferlen;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL)
|
|
goto err;
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
|
|
iov = scsi_malloc(task, sizeof(struct scsi_iovec));
|
|
if (iov == NULL)
|
|
goto err;
|
|
|
|
switch(sa) {
|
|
case SCSI_PERSISTENT_RESERVE_REGISTER:
|
|
case SCSI_PERSISTENT_RESERVE_RESERVE:
|
|
case SCSI_PERSISTENT_RESERVE_RELEASE:
|
|
case SCSI_PERSISTENT_RESERVE_CLEAR:
|
|
case SCSI_PERSISTENT_RESERVE_PREEMPT:
|
|
case SCSI_PERSISTENT_RESERVE_PREEMPT_AND_ABORT:
|
|
case SCSI_PERSISTENT_RESERVE_REGISTER_AND_IGNORE_EXISTING_KEY:
|
|
basic = param;
|
|
|
|
xferlen = 24;
|
|
buf = scsi_malloc(task, xferlen);
|
|
if (buf == NULL)
|
|
goto err;
|
|
|
|
memset(buf, 0, xferlen);
|
|
scsi_set_uint64(&buf[0], basic->reservation_key);
|
|
scsi_set_uint64(&buf[8], basic->service_action_reservation_key);
|
|
if (basic->spec_i_pt) {
|
|
buf[20] |= 0x08;
|
|
}
|
|
if (basic->all_tg_pt) {
|
|
buf[20] |= 0x04;
|
|
}
|
|
if (basic->aptpl) {
|
|
buf[20] |= 0x01;
|
|
}
|
|
break;
|
|
case SCSI_PERSISTENT_RESERVE_REGISTER_AND_MOVE:
|
|
goto err;
|
|
default:
|
|
goto err;
|
|
}
|
|
|
|
task->cdb[0] = SCSI_OPCODE_PERSISTENT_RESERVE_OUT;
|
|
task->cdb[1] |= sa & 0x1f;
|
|
task->cdb[2] = ((scope << 4) & 0xf0) | (type & 0x0f);
|
|
|
|
scsi_set_uint32(&task->cdb[5], xferlen);
|
|
|
|
task->cdb_size = 10;
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
task->expxferlen = xferlen;
|
|
|
|
iov->iov_base = buf;
|
|
iov->iov_len = xferlen;
|
|
scsi_task_set_iov_out(task, iov, 1);
|
|
|
|
return task;
|
|
|
|
err:
|
|
scsi_free_scsi_task(task);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* WRITE_SAME10
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_writesame10(int wrprotect, int anchor, int unmap, uint32_t lba, int group, uint16_t num_blocks, uint32_t datalen)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_WRITE_SAME10;
|
|
|
|
if (wrprotect) {
|
|
task->cdb[1] |= ((wrprotect & 0x7) << 5);
|
|
}
|
|
if (anchor) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (unmap) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
scsi_set_uint32(&task->cdb[2], lba);
|
|
if (group) {
|
|
task->cdb[6] |= (group & 0x1f);
|
|
}
|
|
scsi_set_uint16(&task->cdb[7], num_blocks);
|
|
|
|
task->cdb_size = 10;
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
task->expxferlen = datalen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* WRITE_SAME16
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_writesame16(int wrprotect, int anchor, int unmap, uint64_t lba, int group, uint32_t num_blocks, uint32_t datalen)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_WRITE_SAME16;
|
|
|
|
if (wrprotect) {
|
|
task->cdb[1] |= ((wrprotect & 0x7) << 5);
|
|
}
|
|
if (anchor) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (unmap) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
if (datalen == 0) {
|
|
task->cdb[1] |= 0x01;
|
|
}
|
|
scsi_set_uint32(&task->cdb[2], lba >> 32);
|
|
scsi_set_uint32(&task->cdb[6], lba & 0xffffffff);
|
|
scsi_set_uint32(&task->cdb[10], num_blocks);
|
|
if (group) {
|
|
task->cdb[14] |= (group & 0x1f);
|
|
}
|
|
|
|
task->cdb_size = 16;
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
task->expxferlen = datalen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* MODESENSE6
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc,
|
|
enum scsi_modesense_page_code page_code,
|
|
int sub_page_code, unsigned char alloc_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_MODESENSE6;
|
|
|
|
if (dbd) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
task->cdb[2] = pc<<6 | page_code;
|
|
task->cdb[3] = sub_page_code;
|
|
task->cdb[4] = alloc_len;
|
|
|
|
task->cdb_size = 6;
|
|
if (alloc_len != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = alloc_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* MODESENSE10
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_modesense10(int llbaa, int dbd, enum scsi_modesense_page_control pc,
|
|
enum scsi_modesense_page_code page_code,
|
|
int sub_page_code, unsigned char alloc_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_MODESENSE10;
|
|
|
|
if (llbaa) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (dbd) {
|
|
task->cdb[1] |= 0x08;
|
|
}
|
|
task->cdb[2] = pc<<6 | page_code;
|
|
task->cdb[3] = sub_page_code;
|
|
|
|
scsi_set_uint16(&task->cdb[7], alloc_len);
|
|
|
|
task->cdb_size = 10;
|
|
if (alloc_len != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = alloc_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* MODESELECT6
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_modeselect6(int pf, int sp, int param_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_MODESELECT6;
|
|
|
|
if (pf) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (sp) {
|
|
task->cdb[1] |= 0x01;
|
|
}
|
|
task->cdb[4] = param_len;
|
|
|
|
task->cdb_size = 6;
|
|
if (param_len != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = param_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* MODESELECT10
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_modeselect10(int pf, int sp, int param_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_MODESELECT10;
|
|
|
|
if (pf) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (sp) {
|
|
task->cdb[1] |= 0x01;
|
|
}
|
|
|
|
scsi_set_uint16(&task->cdb[7], param_len);
|
|
|
|
task->cdb_size = 10;
|
|
if (param_len != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = param_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
struct scsi_mode_page *
|
|
scsi_modesense_get_page(struct scsi_mode_sense *ms,
|
|
enum scsi_modesense_page_code page_code,
|
|
int subpage_code)
|
|
{
|
|
struct scsi_mode_page *mp;
|
|
|
|
for (mp = ms->pages; mp; mp = mp->next) {
|
|
if (mp->page_code == page_code
|
|
&& mp->subpage_code == subpage_code) {
|
|
return mp;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* parse the data in blob and calculate the size of a full
|
|
* modesense6 datain structure
|
|
*/
|
|
static int
|
|
scsi_modesense_datain_getfullsize(struct scsi_task *task, int is_modesense6)
|
|
{
|
|
int len;
|
|
|
|
if (is_modesense6) {
|
|
len = task_get_uint8(task, 0) + 1;
|
|
} else {
|
|
len = task_get_uint16(task, 0) + 2;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static void
|
|
scsi_parse_mode_caching(struct scsi_task *task, int pos, struct scsi_mode_page *mp)
|
|
{
|
|
mp->caching.ic = !!(task_get_uint8(task, pos) & 0x80);
|
|
mp->caching.abpf = !!(task_get_uint8(task, pos) & 0x40);
|
|
mp->caching.cap = !!(task_get_uint8(task, pos) & 0x20);
|
|
mp->caching.disc = !!(task_get_uint8(task, pos) & 0x10);
|
|
mp->caching.size = !!(task_get_uint8(task, pos) & 0x08);
|
|
mp->caching.wce = !!(task_get_uint8(task, pos) & 0x04);
|
|
mp->caching.mf = !!(task_get_uint8(task, pos) & 0x02);
|
|
mp->caching.rcd = !!(task_get_uint8(task, pos) & 0x01);
|
|
|
|
mp->caching.demand_read_retention_priority =
|
|
(task_get_uint8(task, pos + 1) >> 4) & 0x0f;
|
|
mp->caching.write_retention_priority =
|
|
task_get_uint8(task, pos + 1) & 0x0f;
|
|
|
|
mp->caching.disable_prefetch_transfer_length =
|
|
task_get_uint16(task, pos + 2);
|
|
mp->caching.minimum_prefetch = task_get_uint16(task, pos + 4);
|
|
mp->caching.maximum_prefetch = task_get_uint16(task, pos + 6);
|
|
mp->caching.maximum_prefetch_ceiling = task_get_uint16(task, pos + 8);
|
|
|
|
mp->caching.fsw = !!(task_get_uint8(task, pos + 10) & 0x80);
|
|
mp->caching.lbcss = !!(task_get_uint8(task, pos + 10) & 0x40);
|
|
mp->caching.dra = !!(task_get_uint8(task, pos + 10) & 0x20);
|
|
mp->caching.nv_dis = !!(task_get_uint8(task, pos + 10) & 0x01);
|
|
|
|
mp->caching.number_of_cache_segments = task_get_uint8(task, pos + 11);
|
|
mp->caching.cache_segment_size = task_get_uint16(task, pos + 12);
|
|
}
|
|
|
|
static void
|
|
scsi_parse_mode_control(struct scsi_task *task, int pos, struct scsi_mode_page *mp)
|
|
{
|
|
mp->control.tst = (task_get_uint8(task, pos) >> 5) & 0x07;
|
|
mp->control.tmf_only = !!(task_get_uint8(task, pos) & 0x10);
|
|
mp->control.dpicz = !!(task_get_uint8(task, pos) & 0x08);
|
|
mp->control.d_sense = !!(task_get_uint8(task, pos) & 0x04);
|
|
mp->control.gltsd = !!(task_get_uint8(task, pos) & 0x02);
|
|
mp->control.rlec = !!(task_get_uint8(task, pos) & 0x01);
|
|
|
|
mp->control.queue_algorithm_modifier =
|
|
(task_get_uint8(task, pos + 1) >> 4) & 0x0f;
|
|
mp->control.nuar = task_get_uint8(task, pos + 1) & 0x08;
|
|
mp->control.qerr = (task_get_uint8(task, pos + 1) >> 1) & 0x03;
|
|
|
|
mp->control.vs = !!(task_get_uint8(task, pos + 2) & 0x80);
|
|
mp->control.rac = !!(task_get_uint8(task, pos + 2) & 0x40);
|
|
mp->control.ua_intlck_ctrl =
|
|
(task_get_uint8(task, pos + 2) >> 4) & 0x0f;
|
|
mp->control.swp = !!(task_get_uint8(task, pos + 2) & 0x08);
|
|
|
|
mp->control.ato = !!(task_get_uint8(task, pos + 3) & 0x80);
|
|
mp->control.tas = !!(task_get_uint8(task, pos + 3) & 0x40);
|
|
mp->control.atmpe = !!(task_get_uint8(task, pos + 3) & 0x20);
|
|
mp->control.rwwp = !!(task_get_uint8(task, pos + 3) & 0x10);
|
|
mp->control.autoload_mode = !!(task_get_uint8(task, pos + 3) & 0x07);
|
|
|
|
mp->control.busy_timeout_period =
|
|
task_get_uint16(task, pos + 6);
|
|
mp->control.extended_selftest_completion_time =
|
|
task_get_uint16(task, pos + 8);
|
|
}
|
|
|
|
static void
|
|
scsi_parse_mode_power_condition(struct scsi_task *task, int pos, struct scsi_mode_page *mp)
|
|
{
|
|
mp->power_condition.pm_bg_precedence =
|
|
(task_get_uint8(task, pos) >> 6) & 0x03;
|
|
mp->power_condition.standby_y =
|
|
!!(task_get_uint8(task, pos) & 0x01);
|
|
|
|
mp->power_condition.idle_c =
|
|
!!(task_get_uint8(task, pos + 1) & 0x08);
|
|
mp->power_condition.idle_b =
|
|
!!(task_get_uint8(task, pos + 1) & 0x04);
|
|
mp->power_condition.idle_a =
|
|
!!(task_get_uint8(task, pos + 1) & 0x02);
|
|
mp->power_condition.standby_z =
|
|
!!(task_get_uint8(task, pos + 1) & 0x01);
|
|
|
|
mp->power_condition.idle_a_condition_timer =
|
|
task_get_uint32(task, pos + 2);
|
|
mp->power_condition.standby_z_condition_timer =
|
|
task_get_uint32(task, pos + 6);
|
|
mp->power_condition.idle_b_condition_timer =
|
|
task_get_uint32(task, pos + 10);
|
|
mp->power_condition.idle_c_condition_timer =
|
|
task_get_uint32(task, pos + 14);
|
|
mp->power_condition.standby_y_condition_timer =
|
|
task_get_uint32(task, pos + 18);
|
|
|
|
mp->power_condition.ccf_idle =
|
|
(task_get_uint8(task, pos + 37) >> 6) & 0x03;
|
|
mp->power_condition.ccf_standby =
|
|
(task_get_uint8(task, pos + 37) >> 4) & 0x03;
|
|
mp->power_condition.ccf_stopped =
|
|
(task_get_uint8(task, pos + 37) >> 2) & 0x03;
|
|
}
|
|
|
|
static void
|
|
scsi_parse_mode_disconnect_reconnect(struct scsi_task *task, int pos, struct scsi_mode_page *mp)
|
|
{
|
|
mp->disconnect_reconnect.buffer_full_ratio =
|
|
task_get_uint8(task, pos);
|
|
mp->disconnect_reconnect.buffer_empty_ratio =
|
|
task_get_uint8(task, pos + 1);
|
|
mp->disconnect_reconnect.bus_inactivity_limit =
|
|
task_get_uint16(task, pos + 2);
|
|
mp->disconnect_reconnect.disconnect_time_limit =
|
|
task_get_uint16(task, pos + 4);
|
|
mp->disconnect_reconnect.connect_time_limit =
|
|
task_get_uint16(task, pos + 6);
|
|
mp->disconnect_reconnect.maximum_burst_size =
|
|
task_get_uint16(task, pos + 8);
|
|
mp->disconnect_reconnect.emdp =
|
|
!!(task_get_uint8(task, pos + 10) & 0x80);
|
|
mp->disconnect_reconnect.fair_arbitration =
|
|
(task_get_uint8(task, pos + 10) >> 4) & 0x0f;
|
|
mp->disconnect_reconnect.dimm =
|
|
!!(task_get_uint8(task, pos + 10) & 0x08);
|
|
mp->disconnect_reconnect.dtdc =
|
|
task_get_uint8(task, pos + 10) & 0x07;
|
|
mp->disconnect_reconnect.first_burst_size =
|
|
task_get_uint16(task, pos + 12);
|
|
}
|
|
|
|
static void
|
|
scsi_parse_mode_informational_exceptions_control(struct scsi_task *task, int pos, struct scsi_mode_page *mp)
|
|
{
|
|
mp->iec.perf = !!(task_get_uint8(task, pos) & 0x80);
|
|
mp->iec.ebf = !!(task_get_uint8(task, pos) & 0x20);
|
|
mp->iec.ewasc = !!(task_get_uint8(task, pos) & 0x10);
|
|
mp->iec.dexcpt = !!(task_get_uint8(task, pos) & 0x08);
|
|
mp->iec.test = !!(task_get_uint8(task, pos) & 0x04);
|
|
mp->iec.ebackerr = !!(task_get_uint8(task, pos) & 0x02);
|
|
mp->iec.logerr = !!(task_get_uint8(task, pos) & 0x01);
|
|
mp->iec.mrie = task_get_uint8(task, pos + 1) & 0x0f;
|
|
mp->iec.interval_timer = task_get_uint32(task, pos + 2);
|
|
mp->iec.report_count = task_get_uint32(task, pos + 6);
|
|
}
|
|
|
|
|
|
/*
|
|
* parse and unmarshall the mode sense data in buffer
|
|
*/
|
|
static struct scsi_mode_sense *
|
|
scsi_modesense_datain_unmarshall(struct scsi_task *task, int is_modesense6)
|
|
{
|
|
struct scsi_mode_sense *ms;
|
|
int hdr_len;
|
|
int pos;
|
|
|
|
if (is_modesense6) {
|
|
hdr_len = 4;
|
|
} else {
|
|
hdr_len = 8;
|
|
}
|
|
|
|
if (task->datain.size < hdr_len) {
|
|
return NULL;
|
|
}
|
|
|
|
ms = scsi_malloc(task, sizeof(struct scsi_mode_sense));
|
|
if (ms == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (is_modesense6) {
|
|
ms->mode_data_length = task_get_uint8(task, 0);
|
|
ms->medium_type = task_get_uint8(task, 1);
|
|
ms->device_specific_parameter = task_get_uint8(task, 2);
|
|
ms->block_descriptor_length = task_get_uint8(task, 3);
|
|
ms->pages = NULL;
|
|
} else {
|
|
ms->mode_data_length = task_get_uint16(task, 0);
|
|
ms->medium_type = task_get_uint8(task, 2);
|
|
ms->device_specific_parameter = task_get_uint8(task, 3);
|
|
ms->longlba = task_get_uint8(task, 4) & 0x01;
|
|
ms->block_descriptor_length = task_get_uint16(task, 6);
|
|
ms->pages = NULL;
|
|
}
|
|
|
|
if (ms->mode_data_length + 1 > task->datain.size) {
|
|
return NULL;
|
|
}
|
|
|
|
pos = hdr_len + ms->block_descriptor_length;
|
|
while (pos < task->datain.size) {
|
|
struct scsi_mode_page *mp;
|
|
|
|
mp = scsi_malloc(task, sizeof(struct scsi_mode_page));
|
|
if (mp == NULL) {
|
|
return ms;
|
|
}
|
|
mp->ps = task_get_uint8(task, pos) & 0x80;
|
|
mp->spf = task_get_uint8(task, pos) & 0x40;
|
|
mp->page_code = task_get_uint8(task, pos) & 0x3f;
|
|
pos++;
|
|
|
|
if (mp->spf) {
|
|
mp->subpage_code = task_get_uint8(task, pos);
|
|
mp->len = task_get_uint16(task, pos + 1);
|
|
pos += 3;
|
|
} else {
|
|
mp->subpage_code = 0;
|
|
mp->len = task_get_uint8(task, pos);
|
|
pos++;
|
|
}
|
|
|
|
switch (mp->page_code) {
|
|
case SCSI_MODEPAGE_CACHING:
|
|
scsi_parse_mode_caching(task, pos, mp);
|
|
break;
|
|
case SCSI_MODEPAGE_CONTROL:
|
|
scsi_parse_mode_control(task, pos, mp);
|
|
break;
|
|
case SCSI_MODEPAGE_DISCONNECT_RECONNECT:
|
|
scsi_parse_mode_disconnect_reconnect(task, pos, mp);
|
|
break;
|
|
case SCSI_MODEPAGE_INFORMATIONAL_EXCEPTIONS_CONTROL:
|
|
scsi_parse_mode_informational_exceptions_control(task, pos, mp);
|
|
break;
|
|
case SCSI_MODEPAGE_POWER_CONDITION:
|
|
scsi_parse_mode_power_condition(task, pos, mp);
|
|
break;
|
|
default:
|
|
/* TODO: process other pages, or add raw data to struct
|
|
* scsi_mode_page. */
|
|
break;
|
|
}
|
|
|
|
mp->next = ms->pages;
|
|
ms->pages = mp;
|
|
|
|
pos += mp->len;
|
|
}
|
|
|
|
return ms;
|
|
}
|
|
|
|
static struct scsi_data *
|
|
scsi_modesense_marshall_caching(struct scsi_task *task,
|
|
struct scsi_mode_page *mp,
|
|
int hdr_size)
|
|
{
|
|
struct scsi_data *data;
|
|
|
|
data = scsi_malloc(task, sizeof(struct scsi_data));
|
|
if (data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
data->size = 20 + hdr_size;
|
|
data->data = scsi_malloc(task, data->size);
|
|
if (data->data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (mp->caching.ic) data->data[hdr_size + 2] |= 0x80;
|
|
if (mp->caching.abpf) data->data[hdr_size + 2] |= 0x40;
|
|
if (mp->caching.cap) data->data[hdr_size + 2] |= 0x20;
|
|
if (mp->caching.disc) data->data[hdr_size + 2] |= 0x10;
|
|
if (mp->caching.size) data->data[hdr_size + 2] |= 0x08;
|
|
if (mp->caching.wce) data->data[hdr_size + 2] |= 0x04;
|
|
if (mp->caching.mf) data->data[hdr_size + 2] |= 0x02;
|
|
if (mp->caching.rcd) data->data[hdr_size + 2] |= 0x01;
|
|
|
|
data->data[hdr_size + 3] |= (mp->caching.demand_read_retention_priority << 4) & 0xf0;
|
|
data->data[hdr_size + 3] |= mp->caching.write_retention_priority & 0x0f;
|
|
|
|
scsi_set_uint16(&data->data[hdr_size + 4], mp->caching.disable_prefetch_transfer_length);
|
|
scsi_set_uint16(&data->data[hdr_size + 6], mp->caching.minimum_prefetch);
|
|
scsi_set_uint16(&data->data[hdr_size + 8], mp->caching.maximum_prefetch);
|
|
scsi_set_uint16(&data->data[hdr_size + 10], mp->caching.maximum_prefetch_ceiling);
|
|
|
|
if (mp->caching.fsw) data->data[hdr_size + 12] |= 0x80;
|
|
if (mp->caching.lbcss) data->data[hdr_size + 12] |= 0x40;
|
|
if (mp->caching.dra) data->data[hdr_size + 12] |= 0x20;
|
|
if (mp->caching.nv_dis) data->data[hdr_size + 12] |= 0x01;
|
|
|
|
data->data[hdr_size + 13] = mp->caching.number_of_cache_segments;
|
|
|
|
scsi_set_uint16(&data->data[hdr_size + 14], mp->caching.cache_segment_size);
|
|
|
|
return data;
|
|
}
|
|
|
|
static struct scsi_data *
|
|
scsi_modesense_marshall_control(struct scsi_task *task,
|
|
struct scsi_mode_page *mp,
|
|
int hdr_size)
|
|
{
|
|
struct scsi_data *data;
|
|
|
|
data = scsi_malloc(task, sizeof(struct scsi_data));
|
|
if (data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
data->size = 12 + hdr_size;
|
|
data->data = scsi_malloc(task, data->size);
|
|
if (data->data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
data->data[hdr_size + 2] |= (mp->control.tst << 5) & 0xe0;
|
|
if (mp->control.tmf_only) data->data[hdr_size + 2] |= 0x10;
|
|
if (mp->control.dpicz) data->data[hdr_size + 2] |= 0x08;
|
|
if (mp->control.d_sense) data->data[hdr_size + 2] |= 0x04;
|
|
if (mp->control.gltsd) data->data[hdr_size + 2] |= 0x02;
|
|
if (mp->control.rlec) data->data[hdr_size + 2] |= 0x01;
|
|
|
|
data->data[hdr_size + 3] |= (mp->control.queue_algorithm_modifier << 4) & 0xf0;
|
|
if (mp->control.nuar) data->data[hdr_size + 3] |= 0x08;
|
|
data->data[hdr_size + 3] |= (mp->control.qerr << 1) & 0x06;
|
|
|
|
if (mp->control.vs) data->data[hdr_size + 4] |= 0x80;
|
|
if (mp->control.rac) data->data[hdr_size + 4] |= 0x40;
|
|
data->data[hdr_size + 4] |= (mp->control.ua_intlck_ctrl << 4) & 0x30;
|
|
if (mp->control.swp) data->data[hdr_size + 4] |= 0x08;
|
|
|
|
if (mp->control.ato) data->data[hdr_size + 5] |= 0x80;
|
|
if (mp->control.tas) data->data[hdr_size + 5] |= 0x40;
|
|
if (mp->control.atmpe) data->data[hdr_size + 5] |= 0x20;
|
|
if (mp->control.rwwp) data->data[hdr_size + 5] |= 0x10;
|
|
data->data[hdr_size + 5] |= mp->control.autoload_mode & 0x07;
|
|
|
|
scsi_set_uint16(&data->data[hdr_size + 8], mp->control.busy_timeout_period);
|
|
scsi_set_uint16(&data->data[hdr_size + 10], mp->control.extended_selftest_completion_time);
|
|
|
|
return data;
|
|
}
|
|
|
|
static struct scsi_data *
|
|
scsi_modesense_marshall_power_condition(struct scsi_task *task,
|
|
struct scsi_mode_page *mp,
|
|
int hdr_size)
|
|
{
|
|
struct scsi_data *data;
|
|
|
|
data = scsi_malloc(task, sizeof(struct scsi_data));
|
|
if (data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
data->size = 40 + hdr_size;
|
|
data->data = scsi_malloc(task, data->size);
|
|
if (data->data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
data->data[hdr_size + 2] |=
|
|
(mp->power_condition.pm_bg_precedence << 6) & 0xc0;
|
|
if (mp->power_condition.standby_y) data->data[hdr_size + 2] |= 0x01;
|
|
|
|
if (mp->power_condition.idle_c) data->data[hdr_size + 3] |= 0x08;
|
|
if (mp->power_condition.idle_b) data->data[hdr_size + 3] |= 0x04;
|
|
if (mp->power_condition.idle_a) data->data[hdr_size + 3] |= 0x02;
|
|
if (mp->power_condition.standby_z) data->data[hdr_size + 3] |= 0x01;
|
|
|
|
scsi_set_uint32(&data->data[hdr_size + 4],
|
|
mp->power_condition.idle_a_condition_timer);
|
|
scsi_set_uint32(&data->data[hdr_size + 8],
|
|
mp->power_condition.standby_z_condition_timer);
|
|
scsi_set_uint32(&data->data[hdr_size + 12],
|
|
mp->power_condition.idle_b_condition_timer);
|
|
scsi_set_uint32(&data->data[hdr_size + 16],
|
|
mp->power_condition.idle_c_condition_timer);
|
|
scsi_set_uint32(&data->data[hdr_size + 20],
|
|
mp->power_condition.standby_y_condition_timer);
|
|
|
|
data->data[hdr_size + 39] |=
|
|
(mp->power_condition.ccf_idle << 6) & 0xc0;
|
|
data->data[hdr_size + 39] |=
|
|
(mp->power_condition.ccf_standby << 4) & 0x30;
|
|
data->data[hdr_size + 39] |=
|
|
(mp->power_condition.ccf_stopped << 2) & 0x0c;
|
|
|
|
return data;
|
|
}
|
|
|
|
static struct scsi_data *
|
|
scsi_modesense_marshall_disconnect_reconnect(struct scsi_task *task,
|
|
struct scsi_mode_page *mp,
|
|
int hdr_size)
|
|
{
|
|
struct scsi_data *data;
|
|
|
|
data = scsi_malloc(task, sizeof(struct scsi_data));
|
|
if (data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
data->size = 16 + hdr_size;
|
|
data->data = scsi_malloc(task, data->size);
|
|
if (data->data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
data->data[hdr_size + 2] = mp->disconnect_reconnect.buffer_full_ratio;
|
|
data->data[hdr_size + 3] = mp->disconnect_reconnect.buffer_empty_ratio;
|
|
scsi_set_uint16(&data->data[hdr_size + 4], mp->disconnect_reconnect.bus_inactivity_limit);
|
|
scsi_set_uint16(&data->data[hdr_size + 6], mp->disconnect_reconnect.disconnect_time_limit);
|
|
scsi_set_uint16(&data->data[hdr_size + 8], mp->disconnect_reconnect.connect_time_limit);
|
|
scsi_set_uint16(&data->data[hdr_size + 10], mp->disconnect_reconnect.maximum_burst_size);
|
|
|
|
if (mp->disconnect_reconnect.emdp) data->data[hdr_size + 12] |= 0x80;
|
|
data->data[hdr_size + 12] |= (mp->disconnect_reconnect.fair_arbitration << 4) & 0x70;
|
|
if (mp->disconnect_reconnect.dimm) data->data[hdr_size + 12] |= 0x08;
|
|
data->data[hdr_size + 12] |= mp->disconnect_reconnect.dtdc & 0x07;
|
|
|
|
scsi_set_uint16(&data->data[hdr_size + 14], mp->disconnect_reconnect.first_burst_size);
|
|
|
|
return data;
|
|
}
|
|
|
|
static struct scsi_data *
|
|
scsi_modesense_marshall_informational_exceptions_control(struct scsi_task *task,
|
|
struct scsi_mode_page *mp,
|
|
int hdr_size)
|
|
{
|
|
struct scsi_data *data;
|
|
|
|
data = scsi_malloc(task, sizeof(struct scsi_data));
|
|
if (data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
data->size = 12 + hdr_size;
|
|
data->data = scsi_malloc(task, data->size);
|
|
if (data->data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (mp->iec.perf) data->data[hdr_size + 2] |= 0x80;
|
|
if (mp->iec.ebf) data->data[hdr_size + 2] |= 0x20;
|
|
if (mp->iec.ewasc) data->data[hdr_size + 2] |= 0x10;
|
|
if (mp->iec.dexcpt) data->data[hdr_size + 2] |= 0x08;
|
|
if (mp->iec.test) data->data[hdr_size + 2] |= 0x04;
|
|
if (mp->iec.ebackerr) data->data[hdr_size + 2] |= 0x02;
|
|
if (mp->iec.logerr) data->data[hdr_size + 2] |= 0x01;
|
|
|
|
data->data[hdr_size + 3] |= mp->iec.mrie & 0x0f;
|
|
|
|
scsi_set_uint32(&data->data[hdr_size + 4], mp->iec.interval_timer);
|
|
scsi_set_uint32(&data->data[hdr_size + 8], mp->iec.report_count);
|
|
|
|
return data;
|
|
}
|
|
|
|
/*
|
|
* marshall the mode sense data out buffer
|
|
*/
|
|
struct scsi_data *
|
|
scsi_modesense_dataout_marshall(struct scsi_task *task,
|
|
struct scsi_mode_page *mp,
|
|
int is_modeselect6)
|
|
{
|
|
struct scsi_data *data;
|
|
int hdr_size = is_modeselect6 ? 4 : 8;
|
|
|
|
switch (mp->page_code) {
|
|
case SCSI_MODEPAGE_CACHING:
|
|
data = scsi_modesense_marshall_caching(task, mp, hdr_size);
|
|
break;
|
|
case SCSI_MODEPAGE_CONTROL:
|
|
data = scsi_modesense_marshall_control(task, mp, hdr_size);
|
|
break;
|
|
case SCSI_MODEPAGE_DISCONNECT_RECONNECT:
|
|
data = scsi_modesense_marshall_disconnect_reconnect(task, mp, hdr_size);
|
|
break;
|
|
case SCSI_MODEPAGE_INFORMATIONAL_EXCEPTIONS_CONTROL:
|
|
data = scsi_modesense_marshall_informational_exceptions_control(task, mp, hdr_size);
|
|
break;
|
|
case SCSI_MODEPAGE_POWER_CONDITION:
|
|
data = scsi_modesense_marshall_power_condition(task, mp, hdr_size);
|
|
break;
|
|
default:
|
|
/* TODO error reporting ? */
|
|
return NULL;
|
|
}
|
|
|
|
if (data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
data->data[hdr_size + 0] = mp->page_code & 0x3f;
|
|
if (mp->ps) {
|
|
data->data[hdr_size + 0] |= 0x80;
|
|
}
|
|
if (mp->spf) {
|
|
data->data[hdr_size + 0] |= 0x40;
|
|
data->data[hdr_size + 1] = mp->subpage_code;
|
|
scsi_set_uint16(&data->data[hdr_size + 2], data->size -hdr_size - 4);
|
|
} else {
|
|
data->data[hdr_size + 1] = (data->size - hdr_size - 2) & 0xff;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
/*
|
|
* STARTSTOPUNIT
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_startstopunit(int immed, int pcm, int pc, int no_flush, int loej, int start)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_STARTSTOPUNIT;
|
|
|
|
if (immed) {
|
|
task->cdb[1] |= 0x01;
|
|
}
|
|
task->cdb[3] |= pcm & 0x0f;
|
|
task->cdb[4] |= (pc << 4) & 0xf0;
|
|
if (no_flush) {
|
|
task->cdb[4] |= 0x04;
|
|
}
|
|
if (loej) {
|
|
task->cdb[4] |= 0x02;
|
|
}
|
|
if (start) {
|
|
task->cdb[4] |= 0x01;
|
|
}
|
|
|
|
|
|
task->cdb_size = 6;
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
task->expxferlen = 0;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* PREVENTALLOWMEDIUMREMOVAL
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_preventallow(int prevent)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_PREVENTALLOW;
|
|
|
|
task->cdb[4] = prevent & 0x03;
|
|
|
|
task->cdb_size = 6;
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
task->expxferlen = 0;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* SYNCHRONIZECACHE10
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_synchronizecache10(int lba, int num_blocks, int syncnv, int immed)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_SYNCHRONIZECACHE10;
|
|
|
|
if (syncnv) {
|
|
task->cdb[1] |= 0x04;
|
|
}
|
|
if (immed) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
scsi_set_uint32(&task->cdb[2], lba);
|
|
scsi_set_uint16(&task->cdb[7], num_blocks);
|
|
|
|
task->cdb_size = 10;
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
task->expxferlen = 0;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* SYNCHRONIZECACHE16
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_synchronizecache16(uint64_t lba, uint32_t num_blocks, int syncnv, int immed)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_SYNCHRONIZECACHE16;
|
|
|
|
if (syncnv) {
|
|
task->cdb[1] |= 0x04;
|
|
}
|
|
if (immed) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
scsi_set_uint32(&task->cdb[2], lba >> 32);
|
|
scsi_set_uint32(&task->cdb[6], lba & 0xffffffff);
|
|
scsi_set_uint32(&task->cdb[10], num_blocks);
|
|
|
|
task->cdb_size = 16;
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
task->expxferlen = 0;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* PREFETCH10
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_prefetch10(uint32_t lba, int num_blocks, int immed, int group)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_PREFETCH10;
|
|
|
|
if (immed) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
scsi_set_uint32(&task->cdb[2], lba);
|
|
task->cdb[6] |= group & 0x1f;
|
|
scsi_set_uint16(&task->cdb[7], num_blocks);
|
|
|
|
task->cdb_size = 10;
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
task->expxferlen = 0;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* PREFETCH16
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_prefetch16(uint64_t lba, int num_blocks, int immed, int group)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_PREFETCH16;
|
|
|
|
if (immed) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
scsi_set_uint32(&task->cdb[2], lba >> 32);
|
|
scsi_set_uint32(&task->cdb[6], lba & 0xffffffff);
|
|
scsi_set_uint32(&task->cdb[10], num_blocks);
|
|
|
|
task->cdb[14] |= group & 0x1f;
|
|
|
|
task->cdb_size = 16;
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
task->expxferlen = 0;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* SERVICEACTIONIN16
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_serviceactionin16(enum scsi_service_action_in sa, uint32_t xferlen)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_SERVICE_ACTION_IN;
|
|
|
|
task->cdb[1] = sa;
|
|
|
|
scsi_set_uint32(&task->cdb[10], xferlen);
|
|
|
|
task->cdb_size = 16;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* READCAPACITY16
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_readcapacity16(void)
|
|
{
|
|
return scsi_cdb_serviceactionin16(SCSI_READCAPACITY16, 32);
|
|
}
|
|
|
|
/*
|
|
* GET_LBA_STATUS
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_get_lba_status(uint64_t starting_lba, uint32_t alloc_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_SERVICE_ACTION_IN;
|
|
|
|
task->cdb[1] = SCSI_GET_LBA_STATUS;
|
|
|
|
scsi_set_uint32(&task->cdb[2], starting_lba >> 32);
|
|
scsi_set_uint32(&task->cdb[6], starting_lba & 0xffffffff);
|
|
scsi_set_uint32(&task->cdb[10], alloc_len);
|
|
|
|
task->cdb_size = 16;
|
|
if (alloc_len != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = alloc_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* WRITEVERIFY10
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_writeverify10(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY10;
|
|
|
|
task->cdb[1] |= ((wrprotect & 0x07) << 5);
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (bytchk) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba);
|
|
scsi_set_uint16(&task->cdb[7], xferlen/blocksize);
|
|
|
|
task->cdb[6] |= (group_number & 0x1f);
|
|
|
|
task->cdb_size = 10;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* WRITEVERIFY12
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_writeverify12(uint32_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY12;
|
|
|
|
task->cdb[1] |= ((wrprotect & 0x07) << 5);
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (bytchk) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba);
|
|
scsi_set_uint32(&task->cdb[6], xferlen/blocksize);
|
|
|
|
task->cdb[10] |= (group_number & 0x1f);
|
|
|
|
task->cdb_size = 12;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* WRITEVERIFY16
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_writeverify16(uint64_t lba, uint32_t xferlen, int blocksize, int wrprotect, int dpo, int bytchk, int group_number)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_WRITE_VERIFY16;
|
|
|
|
task->cdb[1] |= ((wrprotect & 0x07) << 5);
|
|
if (dpo) {
|
|
task->cdb[1] |= 0x10;
|
|
}
|
|
if (bytchk) {
|
|
task->cdb[1] |= 0x02;
|
|
}
|
|
|
|
scsi_set_uint32(&task->cdb[2], lba >> 32);
|
|
scsi_set_uint32(&task->cdb[6], lba & 0xffffffff);
|
|
scsi_set_uint32(&task->cdb[10], xferlen/blocksize);
|
|
|
|
task->cdb[14] |= (group_number & 0x1f);
|
|
|
|
task->cdb_size = 16;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* EXTENDED COPY
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_extended_copy(int param_len)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL)
|
|
return NULL;
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_EXTENDED_COPY;
|
|
task->cdb[10] = (param_len >> 24) & 0xFF;
|
|
task->cdb[11] = (param_len >> 16) & 0xFF;
|
|
task->cdb[12] = (param_len >> 8) & 0xFF;
|
|
task->cdb[13] = param_len & 0xFF;
|
|
/* Inititalize other fields in CDB */
|
|
task->cdb_size = 16;
|
|
if (param_len) {
|
|
task->xfer_dir = SCSI_XFER_WRITE;
|
|
}
|
|
task->expxferlen = param_len;
|
|
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* RECEIVE COPY RESULTS
|
|
*/
|
|
struct scsi_task *
|
|
scsi_cdb_receive_copy_results(enum scsi_copy_results_sa sa, int list_id, int xferlen)
|
|
{
|
|
struct scsi_task *task;
|
|
|
|
task = malloc(sizeof(struct scsi_task));
|
|
if (task == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(task, 0, sizeof(struct scsi_task));
|
|
task->cdb[0] = SCSI_OPCODE_RECEIVE_COPY_RESULTS;
|
|
task->cdb[1] |= sa & 0x1f;
|
|
task->cdb[2] = list_id & 0xFF;
|
|
|
|
scsi_set_uint32(&task->cdb[10], xferlen);
|
|
|
|
task->cdb_size = 16;
|
|
if (xferlen != 0) {
|
|
task->xfer_dir = SCSI_XFER_READ;
|
|
} else {
|
|
task->xfer_dir = SCSI_XFER_NONE;
|
|
}
|
|
task->expxferlen = xferlen;
|
|
|
|
return task;
|
|
}
|
|
|
|
int
|
|
scsi_datain_getfullsize(struct scsi_task *task)
|
|
{
|
|
switch (task->cdb[0]) {
|
|
case SCSI_OPCODE_TESTUNITREADY:
|
|
return 0;
|
|
case SCSI_OPCODE_INQUIRY:
|
|
return scsi_inquiry_datain_getfullsize(task);
|
|
case SCSI_OPCODE_MODESENSE6:
|
|
return scsi_modesense_datain_getfullsize(task, 1);
|
|
case SCSI_OPCODE_READCAPACITY10:
|
|
return scsi_readcapacity10_datain_getfullsize(task);
|
|
case SCSI_OPCODE_SYNCHRONIZECACHE10:
|
|
return 0;
|
|
case SCSI_OPCODE_READTOC:
|
|
return scsi_readtoc_datain_getfullsize(task);
|
|
case SCSI_OPCODE_REPORTLUNS:
|
|
return scsi_reportluns_datain_getfullsize(task);
|
|
case SCSI_OPCODE_PERSISTENT_RESERVE_IN:
|
|
return scsi_persistentreservein_datain_getfullsize(task);
|
|
case SCSI_OPCODE_MAINTENANCE_IN:
|
|
return scsi_maintenancein_datain_getfullsize(task);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void *
|
|
scsi_datain_unmarshall(struct scsi_task *task)
|
|
{
|
|
if (!task || !task->datain.size)
|
|
return NULL;
|
|
|
|
switch (task->cdb[0]) {
|
|
case SCSI_OPCODE_INQUIRY:
|
|
return scsi_inquiry_datain_unmarshall(task);
|
|
case SCSI_OPCODE_MODESENSE6:
|
|
return scsi_modesense_datain_unmarshall(task, 1);
|
|
case SCSI_OPCODE_MODESENSE10:
|
|
return scsi_modesense_datain_unmarshall(task, 0);
|
|
case SCSI_OPCODE_READCAPACITY10:
|
|
return scsi_readcapacity10_datain_unmarshall(task);
|
|
case SCSI_OPCODE_READTOC:
|
|
return scsi_readtoc_datain_unmarshall(task);
|
|
case SCSI_OPCODE_REPORTLUNS:
|
|
return scsi_reportluns_datain_unmarshall(task);
|
|
case SCSI_OPCODE_SERVICE_ACTION_IN:
|
|
return scsi_serviceactionin_datain_unmarshall(task);
|
|
case SCSI_OPCODE_PERSISTENT_RESERVE_IN:
|
|
return scsi_persistentreservein_datain_unmarshall(task);
|
|
case SCSI_OPCODE_MAINTENANCE_IN:
|
|
return scsi_maintenancein_datain_unmarshall(task);
|
|
case SCSI_OPCODE_RECEIVE_COPY_RESULTS:
|
|
return scsi_receivecopyresults_datain_unmarshall(task);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static struct scsi_read6_cdb *
|
|
scsi_read6_cdb_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_read6_cdb *read6;
|
|
|
|
read6 = scsi_malloc(task, sizeof(struct scsi_read6_cdb));
|
|
if (read6 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
read6->opcode = SCSI_OPCODE_READ6;
|
|
read6->lba = scsi_get_uint32(&task->cdb[0]) & 0x001fffff;
|
|
read6->transfer_length = task->cdb[4];
|
|
read6->control = task->cdb[5];
|
|
|
|
return read6;
|
|
}
|
|
|
|
static struct scsi_read10_cdb *
|
|
scsi_read10_cdb_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_read10_cdb *read10;
|
|
|
|
read10 = scsi_malloc(task, sizeof(struct scsi_read10_cdb));
|
|
if (read10 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
read10->opcode = SCSI_OPCODE_READ10;
|
|
read10->rdprotect = (task->cdb[1] >> 5) & 0x7;
|
|
read10->dpo = !!(task->cdb[1] & 0x10);
|
|
read10->fua = !!(task->cdb[1] & 0x08);
|
|
read10->fua_nv = !!(task->cdb[1] & 0x02);
|
|
read10->lba = scsi_get_uint32(&task->cdb[2]);
|
|
read10->group = task->cdb[6] & 0x1f;
|
|
read10->transfer_length = scsi_get_uint16(&task->cdb[7]);
|
|
read10->control = task->cdb[9];
|
|
|
|
return read10;
|
|
}
|
|
|
|
static struct scsi_read12_cdb *
|
|
scsi_read12_cdb_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_read12_cdb *read12;
|
|
|
|
read12 = scsi_malloc(task, sizeof(struct scsi_read12_cdb));
|
|
if (read12 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
read12->opcode = SCSI_OPCODE_READ12;
|
|
read12->rdprotect = (task->cdb[1] >> 5) & 0x7;
|
|
read12->dpo = !!(task->cdb[1] & 0x10);
|
|
read12->fua = !!(task->cdb[1] & 0x08);
|
|
read12->rarc = !!(task->cdb[1] & 0x04);
|
|
read12->fua_nv = !!(task->cdb[1] & 0x02);
|
|
read12->lba = scsi_get_uint32(&task->cdb[2]);
|
|
read12->transfer_length = scsi_get_uint32(&task->cdb[6]);
|
|
read12->group = task->cdb[10] & 0x1f;
|
|
read12->control = task->cdb[11];
|
|
|
|
return read12;
|
|
}
|
|
|
|
static struct scsi_read16_cdb *
|
|
scsi_read16_cdb_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_read16_cdb *read16;
|
|
|
|
read16 = scsi_malloc(task, sizeof(struct scsi_read16_cdb));
|
|
if (read16 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
read16->opcode = SCSI_OPCODE_READ16;
|
|
read16->rdprotect = (task->cdb[1] >> 5) & 0x7;
|
|
read16->dpo = !!(task->cdb[1] & 0x10);
|
|
read16->fua = !!(task->cdb[1] & 0x08);
|
|
read16->rarc = !!(task->cdb[1] & 0x04);
|
|
read16->fua_nv = !!(task->cdb[1] & 0x02);
|
|
read16->lba = scsi_get_uint64(&task->cdb[2]);
|
|
read16->transfer_length = scsi_get_uint32(&task->cdb[10]);
|
|
read16->group = task->cdb[14] & 0x1f;
|
|
read16->control = task->cdb[15];
|
|
|
|
return read16;
|
|
}
|
|
|
|
static struct scsi_verify10_cdb *
|
|
scsi_verify10_cdb_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_verify10_cdb *verify10;
|
|
|
|
verify10 = scsi_malloc(task, sizeof(struct scsi_verify10_cdb));
|
|
if (verify10 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
verify10->opcode = SCSI_OPCODE_VERIFY10;
|
|
verify10->vrprotect = (task->cdb[1] >> 5) & 0x7;
|
|
verify10->dpo = !!(task->cdb[1] & 0x10);
|
|
verify10->bytchk = !!(task->cdb[1] & 0x02);
|
|
verify10->lba = scsi_get_uint32(&task->cdb[2]);
|
|
verify10->group = task->cdb[6] & 0x1f;
|
|
verify10->verification_length = scsi_get_uint16(&task->cdb[7]);
|
|
verify10->control = task->cdb[9];
|
|
|
|
return verify10;
|
|
}
|
|
|
|
static struct scsi_verify12_cdb *
|
|
scsi_verify12_cdb_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_verify12_cdb *verify12;
|
|
|
|
verify12 = scsi_malloc(task, sizeof(struct scsi_verify12_cdb));
|
|
if (verify12 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
verify12->opcode = SCSI_OPCODE_VERIFY12;
|
|
verify12->vrprotect = (task->cdb[1] >> 5) & 0x7;
|
|
verify12->dpo = !!(task->cdb[1] & 0x10);
|
|
verify12->bytchk = !!(task->cdb[1] & 0x02);
|
|
verify12->lba = scsi_get_uint32(&task->cdb[2]);
|
|
verify12->verification_length = scsi_get_uint32(&task->cdb[6]);
|
|
verify12->group = task->cdb[10] & 0x1f;
|
|
verify12->control = task->cdb[11];
|
|
|
|
return verify12;
|
|
}
|
|
|
|
static struct scsi_verify16_cdb *
|
|
scsi_verify16_cdb_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_verify16_cdb *verify16;
|
|
|
|
verify16 = scsi_malloc(task, sizeof(struct scsi_verify16_cdb));
|
|
if (verify16 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
verify16->opcode = SCSI_OPCODE_VERIFY16;
|
|
verify16->vrprotect = (task->cdb[1] >> 5) & 0x7;
|
|
verify16->dpo = !!(task->cdb[1] & 0x10);
|
|
verify16->bytchk = !!(task->cdb[1] & 0x02);
|
|
verify16->lba = scsi_get_uint64(&task->cdb[2]);
|
|
verify16->verification_length = scsi_get_uint32(&task->cdb[10]);
|
|
verify16->group = task->cdb[14] & 0x1f;
|
|
verify16->control = task->cdb[15];
|
|
|
|
return verify16;
|
|
}
|
|
|
|
static struct scsi_write10_cdb *
|
|
scsi_write10_cdb_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_write10_cdb *write10;
|
|
|
|
write10 = scsi_malloc(task, sizeof(struct scsi_write10_cdb));
|
|
if (write10 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
write10->opcode = SCSI_OPCODE_WRITE10;
|
|
write10->wrprotect = (task->cdb[1] >> 5) & 0x7;
|
|
write10->dpo = !!(task->cdb[1] & 0x10);
|
|
write10->fua = !!(task->cdb[1] & 0x08);
|
|
write10->fua_nv = !!(task->cdb[1] & 0x02);
|
|
write10->lba = scsi_get_uint32(&task->cdb[2]);
|
|
write10->group = task->cdb[6] & 0x1f;
|
|
write10->transfer_length = scsi_get_uint16(&task->cdb[7]);
|
|
write10->control = task->cdb[9];
|
|
|
|
return write10;
|
|
}
|
|
|
|
static struct scsi_write12_cdb *
|
|
scsi_write12_cdb_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_write12_cdb *write12;
|
|
|
|
write12 = scsi_malloc(task, sizeof(struct scsi_write12_cdb));
|
|
if (write12 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
write12->opcode = SCSI_OPCODE_WRITE12;
|
|
write12->wrprotect = (task->cdb[1] >> 5) & 0x7;
|
|
write12->dpo = !!(task->cdb[1] & 0x10);
|
|
write12->fua = !!(task->cdb[1] & 0x08);
|
|
write12->fua_nv = !!(task->cdb[1] & 0x02);
|
|
write12->lba = scsi_get_uint32(&task->cdb[2]);
|
|
write12->transfer_length = scsi_get_uint32(&task->cdb[6]);
|
|
write12->group = task->cdb[10] & 0x1f;
|
|
write12->control = task->cdb[11];
|
|
|
|
return write12;
|
|
}
|
|
|
|
static struct scsi_write16_cdb *
|
|
scsi_write16_cdb_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_write16_cdb *write16;
|
|
|
|
write16 = scsi_malloc(task, sizeof(struct scsi_write16_cdb));
|
|
if (write16 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
write16->opcode = SCSI_OPCODE_WRITE16;
|
|
write16->wrprotect = (task->cdb[1] >> 5) & 0x7;
|
|
write16->dpo = !!(task->cdb[1] & 0x10);
|
|
write16->fua = !!(task->cdb[1] & 0x08);
|
|
write16->fua_nv = !!(task->cdb[1] & 0x02);
|
|
write16->lba = scsi_get_uint64(&task->cdb[2]);
|
|
write16->transfer_length = scsi_get_uint32(&task->cdb[10]);
|
|
write16->group = task->cdb[14] & 0x1f;
|
|
write16->control = task->cdb[15];
|
|
|
|
return write16;
|
|
}
|
|
|
|
static struct scsi_writeatomic16_cdb *
|
|
scsi_writeatomic16_cdb_unmarshall(struct scsi_task *task)
|
|
{
|
|
struct scsi_writeatomic16_cdb *writeatomic16;
|
|
|
|
writeatomic16 = scsi_malloc(task, sizeof(struct scsi_writeatomic16_cdb));
|
|
if (writeatomic16 == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
writeatomic16->opcode = SCSI_OPCODE_WRITE_ATOMIC16;
|
|
writeatomic16->wrprotect = (task->cdb[1] >> 5) & 0x7;
|
|
writeatomic16->dpo = !!(task->cdb[1] & 0x10);
|
|
writeatomic16->fua = !!(task->cdb[1] & 0x08);
|
|
writeatomic16->lba = scsi_get_uint64(&task->cdb[2]);
|
|
writeatomic16->transfer_length = scsi_get_uint16(&task->cdb[12]);
|
|
writeatomic16->group = task->cdb[14] & 0x1f;
|
|
writeatomic16->control = task->cdb[15];
|
|
|
|
return writeatomic16;
|
|
}
|
|
|
|
void *
|
|
scsi_cdb_unmarshall(struct scsi_task *task, enum scsi_opcode opcode)
|
|
{
|
|
if (task->cdb[0] != opcode) {
|
|
return NULL;
|
|
}
|
|
|
|
switch (task->cdb[0]) {
|
|
case SCSI_OPCODE_READ6:
|
|
return scsi_read6_cdb_unmarshall(task);
|
|
case SCSI_OPCODE_READ10:
|
|
return scsi_read10_cdb_unmarshall(task);
|
|
case SCSI_OPCODE_READ12:
|
|
return scsi_read12_cdb_unmarshall(task);
|
|
case SCSI_OPCODE_READ16:
|
|
return scsi_read16_cdb_unmarshall(task);
|
|
case SCSI_OPCODE_VERIFY10:
|
|
return scsi_verify10_cdb_unmarshall(task);
|
|
case SCSI_OPCODE_VERIFY12:
|
|
return scsi_verify12_cdb_unmarshall(task);
|
|
case SCSI_OPCODE_VERIFY16:
|
|
return scsi_verify16_cdb_unmarshall(task);
|
|
case SCSI_OPCODE_WRITE10:
|
|
return scsi_write10_cdb_unmarshall(task);
|
|
case SCSI_OPCODE_WRITE12:
|
|
return scsi_write12_cdb_unmarshall(task);
|
|
case SCSI_OPCODE_WRITE16:
|
|
return scsi_write16_cdb_unmarshall(task);
|
|
case SCSI_OPCODE_WRITE_ATOMIC16:
|
|
return scsi_writeatomic16_cdb_unmarshall(task);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const char *
|
|
scsi_devtype_to_str(enum scsi_inquiry_peripheral_device_type type)
|
|
{
|
|
switch (type) {
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS:
|
|
return "DIRECT_ACCESS";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS:
|
|
return "SEQUENTIAL_ACCESS";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PRINTER:
|
|
return "PRINTER";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PROCESSOR:
|
|
return "PROCESSOR";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WRITE_ONCE:
|
|
return "WRITE_ONCE";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC:
|
|
return "MMC";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SCANNER:
|
|
return "SCANNER";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_MEMORY:
|
|
return "OPTICAL_MEMORY";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MEDIA_CHANGER:
|
|
return "MEDIA_CHANGER";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_COMMUNICATIONS:
|
|
return "COMMUNICATIONS";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_STORAGE_ARRAY_CONTROLLER:
|
|
return "STORAGE_ARRAY_CONTROLLER";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_ENCLOSURE_SERVICES:
|
|
return "ENCLOSURE_SERVICES";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SIMPLIFIED_DIRECT_ACCESS:
|
|
return "SIMPLIFIED_DIRECT_ACCESS";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_CARD_READER:
|
|
return "OPTICAL_CARD_READER";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_BRIDGE_CONTROLLER:
|
|
return "BRIDGE_CONTROLLER";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OSD:
|
|
return "OSD";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_AUTOMATION:
|
|
return "AUTOMATION";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQURITY_MANAGER:
|
|
return "SEQURITY_MANAGER";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WELL_KNOWN_LUN:
|
|
return "WELL_KNOWN_LUN";
|
|
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_UNKNOWN:
|
|
return "UNKNOWN";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
const char *
|
|
scsi_devqualifier_to_str(enum scsi_inquiry_peripheral_qualifier qualifier)
|
|
{
|
|
switch (qualifier) {
|
|
case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED:
|
|
return "CONNECTED";
|
|
case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_DISCONNECTED:
|
|
return "DISCONNECTED";
|
|
case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_NOT_SUPPORTED:
|
|
return "NOT_SUPPORTED";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
const char *
|
|
scsi_version_to_str(enum scsi_version version)
|
|
{
|
|
switch (version) {
|
|
case SCSI_VERSION_SPC:
|
|
return "ANSI INCITS 301-1997 (SPC)";
|
|
case SCSI_VERSION_SPC2:
|
|
return "ANSI INCITS 351-2001 (SPC-2)";
|
|
case SCSI_VERSION_SPC3:
|
|
return "ANSI INCITS 408-2005 (SPC-3)";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
const char *
|
|
scsi_version_descriptor_to_str(enum scsi_version_descriptor version_descriptor)
|
|
{
|
|
switch (version_descriptor) {
|
|
case SCSI_VERSION_DESCRIPTOR_ISCSI:
|
|
return "iSCSI";
|
|
case SCSI_VERSION_DESCRIPTOR_SBC:
|
|
return "SBC";
|
|
case SCSI_VERSION_DESCRIPTOR_SBC_ANSI_INCITS_306_1998:
|
|
return "SBC ANSI INCITS 306-1998";
|
|
case SCSI_VERSION_DESCRIPTOR_SBC_T10_0996_D_R08C:
|
|
return "SBC T10/0996-D revision 08c";
|
|
case SCSI_VERSION_DESCRIPTOR_SBC_2:
|
|
return "SBC-2";
|
|
case SCSI_VERSION_DESCRIPTOR_SBC_2_ISO_IEC_14776_322:
|
|
return "SBC-2 ISO/IEC 14776-322";
|
|
case SCSI_VERSION_DESCRIPTOR_SBC_2_ANSI_INCITS_405_2005:
|
|
return "SBC-2 ANSI INCITS 405-2005";
|
|
case SCSI_VERSION_DESCRIPTOR_SBC_2_T10_1417_D_R16:
|
|
return "SBC-2 T10/1417-D revision 16";
|
|
case SCSI_VERSION_DESCRIPTOR_SBC_2_T10_1417_D_R5A:
|
|
return "SBC-2 T10/1417-D revision 5A";
|
|
case SCSI_VERSION_DESCRIPTOR_SBC_2_T10_1417_D_R15:
|
|
return "SBC-2 T10/1417-D revision 15";
|
|
case SCSI_VERSION_DESCRIPTOR_SBC_3:
|
|
return "SBC-3";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC:
|
|
return "SPC";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_ANSI_INCITS_301_1997:
|
|
return "SPC ANSI INCITS 301-1997";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_T10_0995_D_R11A:
|
|
return "SPC T10/0995-D revision 11a";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_2:
|
|
return "SPC-2";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_2_ISO_IEC_14776_452:
|
|
return "SPC-2 ISO.IEC 14776-452";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_2_ANSI_INCITS_351_2001:
|
|
return "SPC-2 ANSI INCITS 351-2001";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R20:
|
|
return "SPC-2 T10/1236-D revision 20";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R12:
|
|
return "SPC-2 T10/1236-D revision 12";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R18:
|
|
return "SPC-2 T10/1236-D revision 18";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_2_T10_1236_D_R19:
|
|
return "SPC-2 T10/1236-D revision 19";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_3:
|
|
return "SPC-3";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_3_ISO_IEC_14776_453:
|
|
return "SPC-3 ISO/IEC 14776-453";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_3_ANSI_INCITS_408_2005:
|
|
return "SPC-3 ANSI INCITS 408-2005";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R7:
|
|
return "SPC-3 T10/1416-D revision 7";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R21:
|
|
return "SPC-3 T10/1416-D revision 21";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R22:
|
|
return "SPC-3 T10/1416-D revision 22";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_3_T10_1416_D_R23:
|
|
return "SPC-3 T10/1416-D revision 23";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_4:
|
|
return "SPC-4";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_4_T10_1731_D_R16:
|
|
return "SPC-4 T10/1731-D revision 16";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_4_T10_1731_D_R18:
|
|
return "SPC-4 T10/1731-D revision 18";
|
|
case SCSI_VERSION_DESCRIPTOR_SPC_4_T10_1731_D_R23:
|
|
return "SPC-4 T10/1731-D revision 23";
|
|
case SCSI_VERSION_DESCRIPTOR_SSC:
|
|
return "SSC";
|
|
case SCSI_VERSION_DESCRIPTOR_UAS_T10_2095D_R04:
|
|
return "UAS T10/2095-D revision 04";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
const char *
|
|
scsi_inquiry_pagecode_to_str(int pagecode)
|
|
{
|
|
switch (pagecode) {
|
|
case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES:
|
|
return "SUPPORTED_VPD_PAGES";
|
|
case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER:
|
|
return "UNIT_SERIAL_NUMBER";
|
|
case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
|
|
return "DEVICE_IDENTIFICATION";
|
|
case SCSI_INQUIRY_PAGECODE_BLOCK_LIMITS:
|
|
return "BLOCK_LIMITS";
|
|
case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS:
|
|
return "BLOCK_DEVICE_CHARACTERISTICS";
|
|
case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
|
|
return "LOGICAL_BLOCK_PROVISIONING";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
|
|
const char *
|
|
scsi_protocol_identifier_to_str(int identifier)
|
|
{
|
|
switch (identifier) {
|
|
case SCSI_PROTOCOL_IDENTIFIER_FIBRE_CHANNEL:
|
|
return "FIBRE_CHANNEL";
|
|
case SCSI_PROTOCOL_IDENTIFIER_PARALLEL_SCSI:
|
|
return "PARALLEL_SCSI";
|
|
case SCSI_PROTOCOL_IDENTIFIER_SSA:
|
|
return "SSA";
|
|
case SCSI_PROTOCOL_IDENTIFIER_IEEE_1394:
|
|
return "IEEE_1394";
|
|
case SCSI_PROTOCOL_IDENTIFIER_RDMA:
|
|
return "RDMA";
|
|
case SCSI_PROTOCOL_IDENTIFIER_ISCSI:
|
|
return "ISCSI";
|
|
case SCSI_PROTOCOL_IDENTIFIER_SAS:
|
|
return "SAS";
|
|
case SCSI_PROTOCOL_IDENTIFIER_ADT:
|
|
return "ADT";
|
|
case SCSI_PROTOCOL_IDENTIFIER_ATA:
|
|
return "ATA";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
const char *
|
|
scsi_codeset_to_str(int codeset)
|
|
{
|
|
switch (codeset) {
|
|
case SCSI_CODESET_BINARY:
|
|
return "BINARY";
|
|
case SCSI_CODESET_ASCII:
|
|
return "ASCII";
|
|
case SCSI_CODESET_UTF8:
|
|
return "UTF8";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
const char *
|
|
scsi_association_to_str(int association)
|
|
{
|
|
switch (association) {
|
|
case SCSI_ASSOCIATION_LOGICAL_UNIT:
|
|
return "LOGICAL_UNIT";
|
|
case SCSI_ASSOCIATION_TARGET_PORT:
|
|
return "TARGET_PORT";
|
|
case SCSI_ASSOCIATION_TARGET_DEVICE:
|
|
return "TARGET_DEVICE";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
const char *
|
|
scsi_designator_type_to_str(int type)
|
|
{
|
|
switch (type) {
|
|
case SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC:
|
|
return "VENDOR_SPECIFIC";
|
|
case SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID:
|
|
return "T10_VENDORT_ID";
|
|
case SCSI_DESIGNATOR_TYPE_EUI_64:
|
|
return "EUI_64";
|
|
case SCSI_DESIGNATOR_TYPE_NAA:
|
|
return "NAA";
|
|
case SCSI_DESIGNATOR_TYPE_RELATIVE_TARGET_PORT:
|
|
return "RELATIVE_TARGET_PORT";
|
|
case SCSI_DESIGNATOR_TYPE_TARGET_PORT_GROUP:
|
|
return "TARGET_PORT_GROUP";
|
|
case SCSI_DESIGNATOR_TYPE_LOGICAL_UNIT_GROUP:
|
|
return "LOGICAL_UNIT_GROUP";
|
|
case SCSI_DESIGNATOR_TYPE_MD5_LOGICAL_UNIT_IDENTIFIER:
|
|
return "MD5_LOGICAL_UNIT_IDENTIFIER";
|
|
case SCSI_DESIGNATOR_TYPE_SCSI_NAME_STRING:
|
|
return "SCSI_NAME_STRING";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
const char *
|
|
scsi_alua_state_to_str(uint8_t state)
|
|
{
|
|
switch (state) {
|
|
case SCSI_ALUA_ACTIVE_OPTIMIZED:
|
|
return "ACTIVE-OPTIMIZED";
|
|
case SCSI_ALUA_ACTIVE_NONOPTIMIZED:
|
|
return "ACTIVE-NONOPTIMIZED";
|
|
case SCSI_ALUA_STANDBY:
|
|
return "STANDBY";
|
|
case SCSI_ALUA_UNAVAILABLE:
|
|
return "UNAVAILABLE";
|
|
case SCSI_ALUA_LOGICAL_BLOCK_DEPENDENT:
|
|
return "BLOCK-DEPENDENT";
|
|
case SCSI_ALUA_OFFLINE:
|
|
return "OFFLINE";
|
|
case SCSI_ALUA_TRANSITIONING:
|
|
return "TRANSITIONING";
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
void
|
|
scsi_set_task_private_ptr(struct scsi_task *task, void *ptr)
|
|
{
|
|
task->ptr = ptr;
|
|
}
|
|
|
|
void *
|
|
scsi_get_task_private_ptr(struct scsi_task *task)
|
|
{
|
|
return task->ptr;
|
|
}
|
|
|
|
void
|
|
scsi_task_set_iov_out(struct scsi_task *task, struct scsi_iovec *iov, int niov)
|
|
{
|
|
task->iovector_out.iov = iov;
|
|
task->iovector_out.niov = niov;
|
|
}
|
|
|
|
void
|
|
scsi_task_set_iov_in(struct scsi_task *task, struct scsi_iovec *iov, int niov)
|
|
{
|
|
task->iovector_in.iov = iov;
|
|
task->iovector_in.niov = niov;
|
|
}
|
|
|
|
void
|
|
scsi_task_reset_iov(struct scsi_iovector *iovector)
|
|
{
|
|
iovector->offset = 0;
|
|
iovector->consumed = 0;
|
|
}
|
|
|
|
#define IOVECTOR_INITAL_ALLOC (16)
|
|
|
|
static int
|
|
scsi_iovector_add(struct scsi_task *task, struct scsi_iovector *iovector, int len, unsigned char *buf)
|
|
{
|
|
if (len < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (iovector->iov == NULL) {
|
|
iovector->iov = scsi_malloc(task, IOVECTOR_INITAL_ALLOC*sizeof(struct iovec));
|
|
if (iovector->iov == NULL) {
|
|
return -1;
|
|
}
|
|
iovector->nalloc = IOVECTOR_INITAL_ALLOC;
|
|
}
|
|
|
|
/* iovec allocation is too small */
|
|
if (iovector->nalloc < iovector->niov + 1) {
|
|
struct scsi_iovec *old_iov = iovector->iov;
|
|
iovector->iov = scsi_malloc(task, 2 * iovector->nalloc * sizeof(struct iovec));
|
|
if (iovector->iov == NULL) {
|
|
return -1;
|
|
}
|
|
memcpy(iovector->iov, old_iov, iovector->niov * sizeof(struct iovec));
|
|
iovector->nalloc <<= 1;
|
|
}
|
|
|
|
iovector->iov[iovector->niov].iov_len = len;
|
|
iovector->iov[iovector->niov].iov_base = buf;
|
|
iovector->niov++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
scsi_task_add_data_in_buffer(struct scsi_task *task, int len, unsigned char *buf)
|
|
{
|
|
return scsi_iovector_add(task, &task->iovector_in, len, buf);
|
|
}
|
|
|
|
int
|
|
scsi_task_add_data_out_buffer(struct scsi_task *task, int len, unsigned char *buf)
|
|
{
|
|
return scsi_iovector_add(task, &task->iovector_out, len, buf);
|
|
}
|
|
|
|
int
|
|
scsi_task_get_status(struct scsi_task *task, struct scsi_sense *sense)
|
|
{
|
|
if (sense) {
|
|
memcpy(sense, &task->sense, sizeof(struct scsi_sense));
|
|
}
|
|
return task->status;
|
|
}
|