[Stgt-devel] [Patch 1/1] Add SCSI MEDIUM CHANGER support
Mark Harvey
markh794
Fri Jul 6 10:22:06 CEST 2007
Re-work of initial SMC commit based on latest git head MODE PAGE commit.
This version includes the ability to load/unload media from the
DATA TRANSFER DEVICE at the configured TID/LUN address.
Update spc_test_unit() TEST UNIT READY op processing to 'understand'
online, Poweron/Reset and removable flags. i.e. Response from TUR
depends on the status of each of these flags.
- If poweron_reset, return POWER-ON or RESET occurred, then clear flag.
- If online - Return READY
- If off-line and non-removable media - UNIT BECOMING READY
- If off-line and removable media - MEDIUM NOT PRESENT
Online is when the backing file is open and ready for reading/writing.
To be done at at a later time:
Other SCSI op codes need to be aware of these flags.
As is, with the MMC device, this code will work as a working CD Jukebox (medium changer)
>From f76cecc5b8d14671fe523f50a854fd2102f51616 Mon Sep 17 00:00:00 2001
From: Mark Harvey <markh794 at gmail.com>
Date: Fri, 6 Jul 2007 18:03:58 +1000
Subject: Commit of SMC module
Add initial support of SCSI MEDIUM CHANGER module.
Includes all SCSI op codes defined as MANDATORY.
doc/README.lu_config updated with SMC spcific options.
scripts/tgt-core-test updated to create an SMC device.
Signed-off-by: Mark Harvey <markh794 at gmail.com>
---
doc/README.lu_configuration | 47 +++
scripts/tgt-core-test | 104 +++++-
usr/Makefile | 5 +-
usr/media.h | 28 ++
usr/mmc.c | 1 +
usr/osd.c | 2 +
usr/sbc.c | 1 +
usr/smc.c | 846 +++++++++++++++++++++++++++++++++++++++++++
usr/smc.h | 121 ++++++
usr/spc.c | 65 ++--
usr/spc.h | 5 +-
usr/target.c | 62 ++++
usr/tgtadm.c | 2 +
usr/tgtd.h | 7 +-
14 files changed, 1255 insertions(+), 41 deletions(-)
diff --git a/doc/README.lu_configuration b/doc/README.lu_configuration
index d63668a..1e6327e 100644
--- a/doc/README.lu_configuration
+++ b/doc/README.lu_configuration
@@ -84,6 +84,53 @@ tgtadm --lld iscsi --mode logicalunit --op update --tid 1 --lun 2 \
--params mode_page=0x1c:0:10:8:0:0:0:0:0:0:0:0:0
+SMC unique options
+------------------
+
+--params have several unique key=value pairs ontop of all other modules.
+ - element_type=<1|2|3|4>
+ - start_address=Number between 1 & 65535
+ - quantity=Number between 1 & 65535
+ - sides=1|2
+ - address=Number between 1 & 65535
+ - barcode="Char string up to 10 chars"
+ - tid=<number>
+ - lun=<number>
+ - media_home=/path/to/virtual/media
+ The 'barcode' of media is appended to this path specified in media_home and
+ it is this file which is used for the backing store when reading/writing to
+ DATA TRANSFER DEVICE at address 'tid=x lun=x'
+
+Several of these parameters 'work together'
+
+e.g. To add 'quantity' slots as 'element_type' starting at 'start_address'
+ - element_type=<1|2|3|4>
+ - start_address=Number between 1 & 65535
+ - quantity=Number between 1 & 65535
+
+Note: start_address + quantity should not overlap with any other slots..
+
+
+While 'configuring slot 'address' of 'element_type':
+ - Set barcode of meda (occupy slot)
+ - If element type is DATA TRANSFER DEVICE, then define TID & LUN of device.
+
+ - element_type=<1|2|3|4>
+ - address=Number between 1 & 65535
+ - barcode="String up to 10 chars"
+ - sides=<1|2>
+ - tid=<tid of device which belongs at this address>
+ - lun=<lun of device which belongs at this address>
+
+
+It is the responsibility of the user not to configure overlapping slots
+of differing types.
+
+Slot types:
+ 1 -> Medium Transport (picker arm)
+ 2 -> Storage Element
+ 3 -> Import/Export Element
+ 4 -> Data Transfer device (CD drive, tape drive, MO drive etc)
Please refer to scripts/tgt-core-test for a working example.
diff --git a/scripts/tgt-core-test b/scripts/tgt-core-test
index a31be1b..8c58972 100755
--- a/scripts/tgt-core-test
+++ b/scripts/tgt-core-test
@@ -17,15 +17,6 @@ fi
if [ ! -f $HOME/hd_block ]; then
dd if=/dev/zero of=$HOME/hd_block bs=1M count=8
fi
-if [ ! -f $HOME/cd_block0 ]; then
- dd if=/dev/zero of=$HOME/cd_block0 bs=1M count=8
-fi
-if [ ! -f $HOME/cd_block1 ]; then
- dd if=/dev/zero of=$HOME/cd_block1 bs=1M count=8
-fi
-if [ ! -f $HOME/cd_block2 ]; then
- dd if=/dev/zero of=$HOME/cd_block2 bs=1M count=8
-fi
set -x
@@ -85,10 +76,13 @@ tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
--params mode_page=0x1c:0:10:8:0:0:0:0:0:0:0:0:0
-
for LUN in 2 3 4; do
+ if [ ! -f $HOME/cdrom$LUN ]; then
+ dd if=/dev/zero of=$HOME/cdrom$LUN bs=1M count=8
+ fi
+
# Create LUN - CD/ROM
- tgtadm --lld iscsi --mode logicalunit --op new --tid $TID --lun $LUN -b $HOME/cd_block0 --device-type=cd
+ tgtadm --lld iscsi --mode logicalunit --op new --tid $TID --lun $LUN -b $HOME/cdrom$LUN --device-type=cd
tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
--params vendor_id=VirtualCD,product_id=CD101,product_rev=0010,scsi_sn=XYZZY1$LUN,removable=1
# Vendor Uniq - Mode page 0..
@@ -102,6 +96,94 @@ for LUN in 2 3 4; do
--params mode_page=0x1c:0:10:8:0:0:0:0:0:0:0:0:0
done
+###############################################################################
+# Set up SMC Medium Changer
+###############################################################################
+LUN=5
+if [ ! -f $HOME/smc ]; then
+ dd if=/dev/zero of=$HOME/smc bs=1k count=1
+fi
+
+tgtadm --lld iscsi --mode logicalunit --op new --tid $TID --lun $LUN \
+ -b $HOME/smc --device-type=changer
+
+#### Set up mode pages ####
+# From smc3-06.pdf
+# Page 0x02: Disconnect/Reconnect SPC-3
+# Page 0x0a: Control SPC-3
+# Page 0x18: Protocol Specific LUN SPC-3
+# Page 0x19: Protocol Specific Port SPC-3
+# Page 0x1a: Power Condition SPC-3
+# Page 0x1c: Informational Exceptions Control SPC-3
+# Page 0x1d: Element Address Assignment SMC-3 7.3.4
+# Page 0x1e: Transport Geometry Parameters SMC-3 7.3.5
+# Page 0x1f: Device Capabilities SMC-3 7.3.2
+# Page 0x1f/Subpage 0x41: Extended Device Capabilities SMC-3 7.3.3
+
+# Dummy 'page 0'
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params mode_page=0:0:0
+# Disconnect/Reconnect
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params mode_page=2:0:14:0x80:0x80:0:0xa:0:0:0:0:0:0:0:0:0:0
+# Power Condition
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params mode_page=0x1a:0:18:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
+# Informational Exceptions Control Mode Page
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params mode_page=0x1c:0:10:8:0:0:0:0:0:0:0:0:0
+# Element Address Assignment - Setup afterwards.
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params mode_page=0x1d:0:0x12:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
+# Transport Geometry Parameters
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params mode_page=0x1e:0:2:0:0
+# Device Capabilities
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params mode_page=0x1f:0:0x12:0x0f:7:0x0f:0x0f:0x0f:0x0f:0:0:0:0:0x0f:0x0f:0x0f:0x0f:0:0:0:0
+
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params vendor_id=STK,product_id=L700,product_rev=0010,scsi_sn=XYZZY_0,removable=1
+
+
+## Add Data Transfer devices (3 drives)
+# Define slot address for devices.
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params element_type=4,start_address=1,quantity=3
+# Now define which device at each address.
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params element_type=4,address=1,tid=1,lun=2
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params element_type=4,address=2,tid=1,lun=3
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params element_type=4,address=3,tid=1,lun=4
+
+# Medium Transport Elements (robot arm / picker)
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params element_type=1,start_address=16,quantity=1
+
+## Storage Elements - 8 starting at addr 1024
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params element_type=2,start_address=1024,quantity=8
+# Add 'media' to slots
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params element_type=2,address=1024,barcode=ABC123,sides=1
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params element_type=2,address=1026,barcode=ULT001L3,sides=1
+
+# Import/Export Elements - 5 starting at addr 32
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params element_type=3,start_address=32,quantity=5
+
+# define path to virtual media
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params media_home=/d/01/vtl
+
+# Dump the list of configured slots to syslog...
+tgtadm --lld iscsi --mode logicalunit --op update --tid $TID --lun $LUN \
+ --params dump=1
+
+
# Allow ALL initiators to connect to this target
tgtadm --lld iscsi --mode target --op bind --tid $TID -I ALL
diff --git a/usr/Makefile b/usr/Makefile
index 14be34b..c7fb2ca 100644
--- a/usr/Makefile
+++ b/usr/Makefile
@@ -43,8 +43,9 @@ INCLUDES += -I.
CFLAGS += -Wall -g -O2 -Wstrict-prototypes -fPIC -D_LARGEFILE64_SOURCE $(INCLUDES)
PROGRAMS += tgtd tgtadm
-TGTD_OBJS += tgtd.o mgmt.o target.o spc.o sbc.o mmc.o osd.o spt.o scc.o scsi.o log.o \
- driver.o util.o work.o parser.o
+TGTD_OBJS += tgtd.o mgmt.o target.o \
+ spc.o sbc.o mmc.o osd.o spt.o scc.o smc.o \
+ scsi.o log.o driver.o util.o work.o parser.o
all: $(PROGRAMS)
diff --git a/usr/media.h b/usr/media.h
new file mode 100644
index 0000000..e54787e
--- /dev/null
+++ b/usr/media.h
@@ -0,0 +1,28 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _MEDIA_H_
+#define _MEDIA_H_
+
+enum c_type { /* Cartridge Types - Ref: smc3r06 - Table 20, page 37 */
+ CART_UNSPECIFIED,
+ CART_DATA,
+ CART_CLEAN,
+ CART_DIAGNOSTICS,
+ CART_WORM,
+ CART_MICROCODE,
+};
+
+#endif /* _MEDIA_H_ */
+
diff --git a/usr/mmc.c b/usr/mmc.c
index 0390bdb..7f70943 100644
--- a/usr/mmc.c
+++ b/usr/mmc.c
@@ -38,6 +38,7 @@
#include "util.h"
#include "tgtd.h"
#include "target.h"
+#include "tgtadm_error.h"
#include "driver.h"
#include "scsi.h"
#include "spc.h"
diff --git a/usr/osd.c b/usr/osd.c
index 2bd33e6..1136b4f 100644
--- a/usr/osd.c
+++ b/usr/osd.c
@@ -29,6 +29,7 @@
#include "list.h"
#include "util.h"
#include "tgtd.h"
+#include "tgtadm_error.h"
#include "target.h"
#include "driver.h"
#include "scsi.h"
@@ -67,6 +68,7 @@ static int osd_lu_init(struct scsi_lu *lu)
static struct device_type_template osd_template = {
.type = TYPE_OSD,
.lu_init = osd_lu_init,
+ .lu_config = spc_lu_config,
.ops = {
[0x00 ... 0x0f] = {spc_illegal_op},
diff --git a/usr/sbc.c b/usr/sbc.c
index d285207..33485e6 100644
--- a/usr/sbc.c
+++ b/usr/sbc.c
@@ -34,6 +34,7 @@
#include "list.h"
#include "util.h"
#include "tgtd.h"
+#include "tgtadm_error.h"
#include "target.h"
#include "driver.h"
#include "scsi.h"
diff --git a/usr/smc.c b/usr/smc.c
new file mode 100644
index 0000000..b97e071
--- /dev/null
+++ b/usr/smc.c
@@ -0,0 +1,846 @@
+/*
+ * SCSI Medium Changer command processing
+ * Based on smc3r06.pdf document from t10.org
+ *
+ * (C) 2004-2007 FUJITA Tomonori <tomof at acm.org>
+ * (C) 2005-2007 Mike Christie <michaelc at cs.wisc.edu>
+ * (C) 2007 Mark Harvey <markh794 at gmail.com>
+ *
+ * SCSI target emulation code is based on Ardis's iSCSI implementation.
+ * http://www.ardistech.com/iscsi/
+ * Copyright (C) 2002-2003 Ardis Technolgies <roman at ardistech.com>,
+ * licensed under the terms of the GNU GPL v2.0,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <linux/fs.h>
+
+#include "list.h"
+#include "util.h"
+#include "tgtd.h"
+#include "target.h"
+#include "driver.h"
+#include "tgtadm_error.h"
+#include "scsi.h"
+#include "spc.h"
+#include "parser.h"
+#include "smc.h"
+#include "media.h"
+
+struct slot *slot_lookup(struct list_head *head, int element_type, int address);
+static int set_slot_full(struct slot *s, uint16_t src, char *path);
+static void set_slot_empty(struct slot *s);
+static int test_slot_full(struct slot *s);
+struct lu_phy_attr *lu_attr_lookup(int tid, uint64_t lun);
+int dtd_load_unload(uint8_t tid, uint64_t lun, int flg, char *filename);
+
+/* ********************************************************
+ * READ_ELEMENT_STATUS
+ *
+ * Ref: Working Draft SCSI Media Changer-3 (smc3r06.pdf), chapter 6.10
+ *
+ * The READ ELEMENT STATUS command request that the device server report the
+ * status of its internal elements to the application client.
+ * Support for READ ELEMENT STATUS command is mandatory.
+ * ******************************************************** */
+static int determine_element_sz(uint8_t dvcid, uint8_t voltag)
+{
+ if (voltag)
+ return (dvcid) ? 86 : 52;
+ else
+ return (dvcid) ? 50 : 16;
+}
+
+/**
+ * element_status_data_hdr -- Fill in Element Status Header
+ * @data uint8_t * - data pointer
+ * @dvcid Device ID - true, return device ID information
+ * @voltag true, return Volume Tag (barcode)
+ * @start Start searching from slot 'start'
+ * @count and return 'count' elements
+ *
+ * This builds the ELEMENT STATUS DATA header information built
+ * from the params passed.
+ *
+ * DATA header is always 8 bytes long
+ */
+static int element_status_data_hdr(uint8_t *data, uint8_t dvcid,
+ uint8_t voltag, int start, int count)
+{
+ int element_sz;
+ int size;
+
+ element_sz = determine_element_sz(dvcid, voltag);
+
+ /* First Element address reported */
+ *(uint16_t *)(data) = __cpu_to_be16(start);
+
+ /* Number of elements available */
+ *(uint16_t *)(data + 2) = __cpu_to_be16(count);
+
+ /* Byte count is the length required to return all valid data.
+ * Allocated length is how much data the initiator will accept */
+ size = ((8 + (count * element_sz)) & 0xffffff);
+ *(uint32_t *)(data + 4) = __cpu_to_be32(size);
+
+return size;
+}
+
+static int add_element_descriptor(uint8_t *data, struct slot *s,
+ uint8_t element_type, uint8_t dvcid, uint8_t voltag)
+{
+ struct lu_phy_attr *attr = NULL;
+ int i; /* data[] index */
+
+ *(uint16_t *)(data) = __cpu_to_be16(s->slot_addr);
+ data[2] = s->status;
+ data[3] = 0; /* Reserved */
+ data[4] = (s->asc >> 8) & 0xff; /* Additional Sense Code */
+ data[5] = s->asc & 0xff; /* Additional Sense Code Qualifier */
+ /* [6], [7] & [8] reserved */
+ data[9] = (s->cart_type & 0xf);
+ if (s->last_addr) { /* Source address is valid ? */
+ data[9] |= 0x80;
+ *(uint16_t *)(data + 10) = __cpu_to_be16(s->last_addr);
+ }
+ i = 12;
+ if (voltag) {
+ if (s->barcode[0] == ' ')
+ memset( &data[i], 0x20, 32);
+ else
+ snprintf((char *)&data[i], 32, "%-32s", s->barcode);
+
+ /* Reserve additional 4 bytes if dvcid is set */
+ i += (dvcid) ? 36 : 32;
+ }
+ if (element_type == ELEMENT_DATA_TRANSFER)
+ attr = lu_attr_lookup(s->drive_tid, s->drive_lun);
+ if (dvcid && attr) {
+ data[i] = 2; /* ASCII code set */
+ data[i + 1] = attr->device_type;
+ data[i + 2] = 0; /* reserved */
+ data[i + 3] = 34; /* Length */
+ snprintf((char *)&data[i + 4], 9, "%-8s", attr->vendor_id);
+ snprintf((char *)&data[i + 12], 17, "%-16s", attr->product_id);
+ snprintf((char *)&data[i + 28], 11, "%-10s", attr->scsi_sn);
+ }
+
+return determine_element_sz(dvcid, voltag);
+}
+
+/**
+ * build_element_descriptor -- Fill in Element details
+ * @data; pointer
+ * @head; Slot struct head
+ * @element_type; Slot type we are interested in.
+ * @first: Return address of first slot found
+ * @start; Start processing from this element #
+ * @dvcid; Device ID
+ * @voltag; Volume tag (barcode)
+ *
+ * Fill each Element Descriptor for slot *s
+ * Return number of elements
+ */
+static int build_element_descriptors(uint8_t *data, struct list_head *head,
+ uint8_t elem_type, int *first,
+ uint16_t start,
+ uint8_t dvcid, uint8_t voltag)
+{
+ struct slot *s;
+ int count = 0;
+ int len = 8;
+ int elem_sz = determine_element_sz(dvcid, voltag);
+
+ list_for_each_entry(s, head, slot_siblings) {
+ if (s->element_type == elem_type) {
+ if (s->slot_addr >= start) {
+ count++;
+ len += add_element_descriptor(&data[len],
+ s, elem_type, dvcid, voltag);
+ }
+ }
+ if (count == 1) /* Record first found slot Address */
+ *first = s->slot_addr;
+ }
+
+ /* Fill in Element Status Page Header */
+ data[0] = elem_type;
+ data[1] = (voltag) ? 0x80 : 0;
+ *(uint16_t *)(data + 2) = __cpu_to_be16(elem_sz);
+
+ /* Total number of bytes in all element descriptors */
+ *(uint32_t *)(data + 4) = __cpu_to_be32((elem_sz * count) & 0xffffff);
+
+return count;
+}
+
+/**
+ * smc_read_element_status - READ ELEMENT STATUS op code
+ *
+ * Support the SCSI op code READ ELEMENT STATUS
+ * Ref: smc3r06, 6.10
+ */
+static int smc_read_element_status(int host_no, struct scsi_cmd *cmd)
+{
+ struct smc_info *smc = (struct smc_info *)cmd->dev->smc_p;
+ uint8_t *data;
+ uint8_t *scb;
+ uint8_t element_type;
+ uint8_t voltag;
+ uint16_t req_start_elem;
+ uint8_t dvcid;
+ uint32_t alloc_len;
+ uint16_t count = 0;
+ int first = 0; /* First valid slot location */
+ int len = 8;
+ int elementSize;
+ int ret;
+ unsigned char key = ILLEGAL_REQUEST;
+ uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
+
+ scb = cmd->scb;
+ element_type = scb[1] & 0x0f;
+ voltag = (scb[1] & 0x10) >> 4;
+ dvcid = scb[6] & 0x01;
+ req_start_elem = __be16_to_cpu(*(uint16_t *)(scb + 2));
+ alloc_len = 0xffffff & __be32_to_cpu(*(uint32_t *)(scb + 6));
+
+ elementSize = determine_element_sz(dvcid, voltag);
+
+ cmd->len = 0;
+ if (cmd->dev) {
+ ret = device_reserved(cmd);
+ if (ret) {
+ dprintf("Reservation Conflict\n");
+ return SAM_STAT_RESERVATION_CONFLICT;
+ }
+ }
+
+ if (pagesize < alloc_len) {
+ dprintf("Can't allocate enough memory for cmd\n");
+ key = HARDWARE_ERROR;
+ asc = ASC_INTERNAL_TGT_FAILURE;
+ goto sense;
+ }
+ if ((data = valloc(pagesize)) == NULL) {
+ dprintf("valloc(%lu) failed\n", pagesize);
+ key = HARDWARE_ERROR;
+ asc = ASC_INTERNAL_TGT_FAILURE;
+ goto sense;
+ }
+ memset(data, 0, pagesize);
+
+ if (scb[11]) /* Reserved byte */
+ goto sense;
+
+ switch(element_type) {
+ case ELEMENT_ANY:
+ /* Return element in type order */
+ count = build_element_descriptors(&data[len], &smc->slots,
+ ELEMENT_MEDIUM_TRANSPORT, &first,
+ req_start_elem,
+ dvcid, voltag);
+ len = count * elementSize;
+ count += build_element_descriptors(&data[len], &smc->slots,
+ ELEMENT_STORAGE, &first,
+ req_start_elem,
+ dvcid, voltag);
+ len += count * elementSize;
+ count += build_element_descriptors(&data[len], &smc->slots,
+ ELEMENT_MAP, &first,
+ req_start_elem,
+ dvcid, voltag);
+ len += count * elementSize;
+ count += build_element_descriptors(&data[len], &smc->slots,
+ ELEMENT_DATA_TRANSFER, &first,
+ req_start_elem,
+ dvcid, voltag);
+ break;
+ case ELEMENT_MEDIUM_TRANSPORT:
+ count = build_element_descriptors(&data[len], &smc->slots,
+ ELEMENT_MEDIUM_TRANSPORT, &first,
+ req_start_elem,
+ dvcid, voltag);
+ break;
+ case ELEMENT_STORAGE:
+ count = build_element_descriptors(&data[len], &smc->slots,
+ ELEMENT_STORAGE, &first,
+ req_start_elem,
+ dvcid, voltag);
+ break;
+ case ELEMENT_MAP:
+ count = build_element_descriptors(&data[len], &smc->slots,
+ ELEMENT_MAP, &first,
+ req_start_elem,
+ dvcid, voltag);
+ break;
+ case ELEMENT_DATA_TRANSFER:
+ count = build_element_descriptors(&data[len], &smc->slots,
+ ELEMENT_DATA_TRANSFER, &first,
+ req_start_elem,
+ dvcid, voltag);
+ break;
+ default:
+ goto sense;
+ break;
+ }
+
+ /* Lastly, fill in data header */
+ len = element_status_data_hdr(data, dvcid, voltag, first, count);
+
+ cmd->len = min_t(int, len, alloc_len);
+ cmd->uaddr = (unsigned long) data;
+ cmd->rw = READ;
+
+ return SAM_STAT_GOOD;
+
+sense:
+ cmd->len = 0;
+ sense_data_build(cmd, key, asc);
+ return SAM_STAT_CHECK_CONDITION;
+}
+
+/**
+ * smc_move_medium - MOVE MEDIUM op code
+ *
+ * Support the SCSI op code MOVE MEDIUM
+ * Ref: smc3r06, 6.6
+ */
+static int smc_move_medium(int host_no, struct scsi_cmd *cmd)
+{
+ struct smc_info *smc = (struct smc_info *)cmd->dev->smc_p;
+ uint8_t *scb;
+ uint16_t src;
+ uint16_t dest;
+ uint8_t invert;
+ struct slot *src_slot = NULL;
+ struct slot *dest_slot = NULL;
+ struct slot *s;
+ int key = ILLEGAL_REQUEST;
+ uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
+
+ scb = cmd->scb;
+ src = __be16_to_cpu(*(uint16_t *)(scb + 4));
+ dest = __be16_to_cpu(*(uint16_t *)(scb + 6));
+ invert = scb[10] & 1;
+
+ list_for_each_entry(s, &smc->slots, slot_siblings) {
+ if (s->slot_addr == src)
+ src_slot = s;
+ if (s->slot_addr == dest)
+ dest_slot = s;
+ }
+
+ if (src_slot) {
+ if (!test_slot_full(src_slot)) {
+ asc = ASC_MEDIUM_SRC_EMPTY;
+ goto sense;
+ }
+ } else /* Could not find src slot - Error */
+ goto sense;
+
+ if (dest_slot) {
+ if (test_slot_full(dest_slot)) {
+ asc = ASC_MEDIUM_DEST_FULL;
+ goto sense;
+ }
+ } else /* Could not find dest slot - Error */
+ goto sense;
+
+ if (invert && (s->sides == 1)) /* Use default INVALID FIELD IN CDB */
+ goto sense;
+
+ memcpy(&dest_slot->barcode, &src_slot->barcode, sizeof(s->barcode));
+ if (dest_slot->element_type == ELEMENT_DATA_TRANSFER) {
+ char path[128];
+ int sz;
+ sz = snprintf(path, sizeof(path), "%s/%s",
+ smc->media_home, dest_slot->barcode);
+ if (sz >= sizeof(path)) {
+ dprintf("Path too long: %s\n", path);
+ key = ILLEGAL_REQUEST;
+ asc = ASC_INTERNAL_TGT_FAILURE;
+ memset(&dest_slot->barcode, ' ', sizeof(s->barcode));
+ goto sense;
+ }
+ if (set_slot_full(dest_slot, src, path)) {
+ key = HARDWARE_ERROR;
+ asc = ASC_MECHANICAL_POSITIONING_ERROR;
+ goto sense;
+ }
+ } else
+ set_slot_full(dest_slot, src, NULL);
+
+ set_slot_empty(src_slot);
+
+ cmd->len = 0;
+ return SAM_STAT_GOOD;
+
+sense:
+ cmd->len = 0;
+ sense_data_build(cmd, key, asc);
+ return SAM_STAT_CHECK_CONDITION;
+}
+
+static int smc_lu_init(struct scsi_lu *lu)
+{
+ struct smc_info *smc;
+
+ smc = zalloc(sizeof(struct smc_info));
+ if (smc)
+ lu->smc_p = smc;
+ else
+ return -ENOMEM;
+
+ spc_lu_init(lu);
+
+ strncpy(lu->attrs.product_id, "VIRTUAL-CHANGER",
+ sizeof(lu->attrs.product_id));
+ lu->attrs.version_desc[0] = 0x0480; /* SMC-3 no version claimed */
+ lu->attrs.version_desc[1] = 0x0960; /* iSCSI */
+ lu->attrs.version_desc[2] = 0x0300; /* SPC-3 */
+
+ /* Vendor uniq - However most apps seem to call for mode page 0*/
+ add_mode_page(lu, "0:0:0");
+ /* Control page */
+ add_mode_page(lu, "10:0:10:2:0:0:0:0:0:0:0:2:0");
+ /* Power Condition */
+ add_mode_page(lu, "0x1a:0:10:8:0:0:0:0:0:0:0:0:0");
+ /* Informational Exceptions Control page */
+ add_mode_page(lu, "0x1c:0:10:8:0:0:0:0:0:0:0:0:0");
+ /* Address Assignment */
+ add_mode_page(lu, "0x1d:0:0x12:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0");
+ /* Transport Geometry Params */
+ add_mode_page(lu, "0x1e:0:2:0:0");
+ /* Device Capabilities */
+ add_mode_page(lu, "0x1f:0:0x12:15:7:15:15:15:15:0:0:0:0:15:15:15:15:0:0:0:0");
+
+ INIT_LIST_HEAD(&smc->slots);
+
+ lu->attrs.online = 1; /* Library will now report as Online */
+ lu->attrs.reset = 1; /* Poweron/reset occurred */
+ lu->attrs.removable = 1; /* Default to removable media */
+
+ return 0;
+}
+
+static void smc_lu_exit(struct scsi_lu *lu)
+{
+ struct smc_info *smc = lu->smc_p;
+
+ dprintf("Medium Changer shutdown() called\n");
+
+ free(smc);
+}
+
+static int set_slot_full(struct slot *s, uint16_t src, char *path)
+{
+ int err = 0;
+
+ if (path)
+ err = dtd_load_unload(s->drive_tid, s->drive_lun, LOAD, path);
+ if (err)
+ return err;
+
+ s->status |= 1;
+ s->last_addr=src;
+
+ return err;
+}
+
+static void set_slot_empty(struct slot *s)
+{
+ s-> status &= 0xfe;
+ s->last_addr=0;
+ memset(s->barcode, ' ', sizeof(s->barcode));
+ if (s->element_type == ELEMENT_DATA_TRANSFER)
+ dtd_load_unload(s->drive_tid, s->drive_lun, UNLOAD, NULL);
+}
+
+static int test_slot_full(struct slot *s)
+{
+ return s->status && 1;
+}
+
+static int slot_insert(struct list_head *head, int element_type, int address)
+{
+ struct slot *s;
+
+ if ((s = zalloc(sizeof(struct slot))) == NULL)
+ return TGTADM_NOMEM;
+
+ s->slot_addr = address;
+ s->element_type = element_type;
+ s->sides = 1;
+ if (element_type == ELEMENT_DATA_TRANSFER) /* Drive */
+ s->asc = ASC_INITIALIZING_REQUIRED;
+
+ list_add_tail(&s->slot_siblings, head);
+
+ return 0;
+}
+
+/**
+ * slot_lookup -- Find slot of type 'element_type' & address 'address'
+ * @element_type; Type of slot, 0 == Any type
+ * @address; Slot address, 0 == First found slot
+ *
+ * Return NULL if no match
+ */
+struct slot *slot_lookup(struct list_head *head, int element_type, int address)
+{
+ struct slot *s;
+
+ list_for_each_entry(s, head, slot_siblings) {
+ if (element_type && address) {
+ if ((s->slot_addr == address) &&
+ (s->element_type == element_type))
+ return s;
+ } else if (element_type) {
+ if (s->element_type == element_type)
+ return s;
+ } else if (address) {
+ if (s->slot_addr == address)
+ return s;
+ }
+ }
+ return NULL;
+}
+
+static void slot_dump(struct list_head *head)
+{
+ struct slot *s;
+
+ list_for_each_entry(s, head, slot_siblings)
+ if (s) {
+ dprintf("Slot %d Information\n", s->slot_addr);
+ dprintf(" Last Addr: %d\n", s->last_addr);
+ dprintf(" Type: %d\n", s->element_type);
+ dprintf(" Barcode: %s\n", s->barcode);
+ if (s->drive_tid) {
+ dprintf(" TID : %d\n", s->drive_tid);
+ dprintf(" LUN : %" PRIu64 "\n", s->drive_lun);
+ }
+ dprintf(" ASC/ASCQ : %d\n\n", s->asc);
+ }
+}
+
+static int add_slt(struct scsi_lu *lu, struct tmp_param *tmp)
+{
+ struct smc_info *smc = lu->smc_p;
+ int ret = TGTADM_INVALID_REQUEST;
+ struct mode_pg *pg;
+ struct slot *s;
+ uint16_t *element;
+ int sv_addr;
+ int qnty_save;
+ int i;
+
+ pg = lu->mode_pgs[0x1d];
+ if (!pg) {
+ dprintf("Failed to find Element Address Assignment mode pg\n");
+ return TGTADM_UNKNOWN_ERR;
+ }
+ element = (uint16_t *)pg->mode_data;
+
+ if (tmp->element_type && tmp->start_addr && tmp->quantity) {
+ switch(tmp->element_type) {
+ case ELEMENT_MEDIUM_TRANSPORT:
+ break;
+ case ELEMENT_MAP:
+ element += 4;
+ break;
+ case ELEMENT_STORAGE:
+ element += 2;
+ break;
+ case ELEMENT_DATA_TRANSFER:
+ element += 6;
+ break;
+ default:
+ goto dont_do_slots;
+ break;
+ }
+
+ sv_addr = __be16_to_cpu(element[0]);
+ qnty_save = __be16_to_cpu(element[1]);
+
+ if (sv_addr)
+ element[0] =
+ __cpu_to_be16(min_t(int, tmp->start_addr, sv_addr));
+ else
+ element[0] = __cpu_to_be16(tmp->start_addr);
+ element[1] = __cpu_to_be16(tmp->quantity + qnty_save);
+
+ s = slot_lookup(&smc->slots, tmp->element_type, tmp->start_addr);
+ if (s) // Opps... Found a slot at this address..
+ goto dont_do_slots;
+
+ ret = TGTADM_SUCCESS;
+ for(i=tmp->start_addr; i < (tmp->start_addr + tmp->quantity); i++)
+ if (slot_insert(&smc->slots, tmp->element_type, i))
+ ret = TGTADM_INVALID_REQUEST;
+ }
+
+dont_do_slots:
+ return ret;
+}
+
+static int config_slot(struct scsi_lu *lu, struct tmp_param *tmp)
+{
+ struct smc_info *smc = lu->smc_p;
+ struct mode_pg *m = NULL;
+ struct slot *s = NULL;
+ int ret = TGTADM_INVALID_REQUEST;
+
+ switch(tmp->element_type) {
+ case ELEMENT_MEDIUM_TRANSPORT:
+ /* If medium has more than one side, set the 'rotate' bit */
+ m = lu->mode_pgs[0x1e];
+ if (m) {
+ m->mode_data[0] = (tmp->sides > 1) ? 1 : 0;
+ ret = TGTADM_SUCCESS;
+ }
+ break;
+ case ELEMENT_STORAGE:
+ case ELEMENT_MAP:
+ if ((s = slot_lookup(&smc->slots, tmp->element_type, tmp->address)) == NULL)
+ break; // Slot not found..
+ strncpy(s->barcode, tmp->barcode, sizeof(s->barcode));
+ set_slot_full(s, 0, NULL);
+ ret = TGTADM_SUCCESS;
+ break;
+ case ELEMENT_DATA_TRANSFER:
+ if (!tmp->tid)
+ break; /* Fail if no TID specified */
+ if ((s = slot_lookup(&smc->slots, tmp->element_type, tmp->address)) == NULL)
+ break; // Slot not found..
+ s->asc = NO_ADDITIONAL_SENSE;
+ s->drive_tid = tmp->tid;
+ s->drive_lun = tmp->lun;
+ ret = TGTADM_SUCCESS;
+ break;
+ }
+ return ret;
+}
+
+#define ADD 1
+#define CONFIGURE 2
+
+static int __smc_lu_config(struct scsi_lu *lu, char *params)
+{
+ struct smc_info *smc = (struct smc_info *)lu->smc_p;
+ int err = TGTADM_SUCCESS;
+ char *p;
+ char buf[256];
+
+ while ((p = strsep(¶ms, ",")) != NULL) {
+ substring_t args[MAX_OPT_ARGS];
+ int token;
+ if (!*p)
+ continue;
+ token = match_token(p, tokens, args);
+ switch (token) {
+ case Opt_element_type:
+ match_strncpy(buf, &args[0], sizeof(buf));
+ sv_param.element_type = atoi(buf);
+ break;
+ case Opt_start_address:
+ match_strncpy(buf, &args[0], sizeof(buf));
+ sv_param.start_addr = atoi(buf);
+ sv_param.operation = ADD;
+ break;
+ case Opt_quantity:
+ match_strncpy(buf, &args[0], sizeof(buf));
+ sv_param.quantity = atoi(buf);
+ break;
+ case Opt_sides:
+ match_strncpy(buf, &args[0], sizeof(buf));
+ sv_param.sides = atoi(buf);
+ break;
+ case Opt_address:
+ match_strncpy(buf, &args[0], sizeof(buf));
+ sv_param.address = atoi(buf);
+ sv_param.operation = CONFIGURE;
+ break;
+ case Opt_barcode:
+ match_strncpy(sv_param.barcode, &args[0],
+ sizeof(sv_param.barcode));
+ break;
+ case Opt_tid:
+ match_strncpy(buf, &args[0], sizeof(buf));
+ sv_param.tid = atoi(buf);
+ break;
+ case Opt_lun:
+ match_strncpy(buf, &args[0], sizeof(buf));
+ sv_param.lun = atoi(buf);
+ break;
+ case Opt_dump:
+ slot_dump(&smc->slots);
+ break;
+ case Opt_media_home:
+ if (smc->media_home)
+ free(smc->media_home);
+ match_strncpy(buf, &args[0], sizeof(buf));
+ smc->media_home = strdup(buf);
+ if (!smc->media_home)
+ return TGTADM_NOMEM;
+ break;
+ default:
+ err = TGTADM_UNKNOWN_PARAM;
+ }
+ }
+ return err;
+}
+
+static int smc_lu_config(struct scsi_lu *lu, char *params)
+{
+ int ret = TGTADM_SUCCESS;
+
+ memset(&sv_param, 0, sizeof(struct tmp_param));
+
+ if ((ret = lu_config(lu, params, __smc_lu_config)))
+ return TGTADM_UNKNOWN_PARAM;
+
+ switch(sv_param.operation) {
+ case ADD:
+ ret = add_slt(lu, &sv_param);
+ break;
+ case CONFIGURE:
+ ret = config_slot(lu, &sv_param);
+ break;
+ }
+ return ret;
+}
+
+struct device_type_template smc_template = {
+ .type = TYPE_MEDIUM_CHANGER,
+ .lu_init = smc_lu_init,
+ .lu_exit = smc_lu_exit,
+ .lu_config = smc_lu_config,
+ .ops = {
+ {spc_test_unit,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_request_sense,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+
+ /* 0x10 */
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_inquiry,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_mode_sense,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+
+ [0x20 ... 0x4f] = {spc_illegal_op,},
+
+ /* 0x50 */
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_mode_sense,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+
+ [0x60 ... 0x9f] = {spc_illegal_op,},
+
+ /* 0xA0 */
+ {spc_report_luns,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {smc_move_medium,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_test_unit,},
+
+ /* 0xB0 */
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+
+ {smc_read_element_status,}, // Mandatory
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+ {spc_illegal_op,},
+
+ [0xc0 ... 0xff] = {spc_illegal_op},
+ }
+};
+
+__attribute__((constructor)) static void smc_init(void)
+{
+ device_type_register(&smc_template);
+}
diff --git a/usr/smc.h b/usr/smc.h
new file mode 100644
index 0000000..eab9b49
--- /dev/null
+++ b/usr/smc.h
@@ -0,0 +1,121 @@
+/*
+ * SCSI Medium Changer Command
+ *
+ * Copyright (C) 2007 Mark Harvey markh794 at gmail.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _SMC_H_
+#define _SMC_H_
+
+#define LOAD 1
+#define UNLOAD 0
+
+/**
+ * Define for ELEMENT TYPE
+ */
+#define ELEMENT_ANY 0
+#define ELEMENT_MEDIUM_TRANSPORT 1
+#define ELEMENT_STORAGE 2
+#define ELEMENT_MAP 3
+#define ELEMENT_DATA_TRANSFER 4
+
+/**
+ * struct slot - Virtual Library element
+ *
+ * @slot_addr: Slot address - number between 0 & 65535
+ * @last_addr: Last slot address where media came from
+ * @asc: ASC/ASCQ for this slot
+ * @element_type: 1 = Medium Transport, 2 = Storage, 3 = MAP, 4 = drive
+ * @cart_type: Type of media in this slot
+ * @status: Different bits used depending on element_type
+ * @sides: If media is single or double sided
+ * @barcode: Barcode of media ID
+ * @drive_tid: TID of DATA TRANSFER DEVICE configured at this slot address.
+ * @drive_lun: LUN of DATA TRANSFER DEVICE configured at this slot address.
+ *
+ * A linked list of slots describing each element within the virtual library
+ */
+struct slot {
+ struct list_head slot_siblings;
+ uint16_t slot_addr;
+ uint16_t last_addr;
+ uint16_t asc;
+ uint8_t element_type;
+ uint8_t cart_type;
+ uint8_t status;
+ uint8_t sides;
+ char barcode[11];
+ uint8_t drive_tid;
+ uint64_t drive_lun;
+};
+
+/**
+ * struct smc_info - Data structure for SMC device
+ * @media_home: Home directory for virtual media
+ * @slots: Linked list of 'slots' within the virtual library
+ *
+ */
+struct smc_info {
+ char *media_home;
+ struct list_head slots;
+};
+
+enum {
+ Opt_element_type, Opt_start_address,
+ Opt_quantity, Opt_sides,
+ Opt_address, Opt_barcode,
+ Opt_tid, Opt_lun,
+ Opt_type, Opt_dump,
+ Opt_media_home,
+ Opt_err,
+};
+
+static match_table_t tokens = {
+ {Opt_element_type, "element_type=%s"},
+ {Opt_start_address, "start_address=%s"},
+ {Opt_quantity, "quantity=%s"},
+ {Opt_sides, "sides=%s"},
+ {Opt_address, "address=%s"},
+ {Opt_barcode, "barcode=%s"},
+ {Opt_tid, "tid=%s"},
+ {Opt_lun, "lun=%s"},
+ {Opt_type, "type=%s"},
+ {Opt_dump, "dump=%s"},
+ {Opt_media_home, "media_home=%s"},
+ {Opt_err, NULL},
+};
+
+/**
+ * struct tmp_param{} -- temporary storage of param values from user
+ *
+ * As param parsing is stateless, several params need to be collected
+ * before we know if we are attempting to configure a slot or adding
+ * a number of new slots. This is just a temporary holder for all
+ * possible valid params before processing.
+ */
+struct tmp_param {
+ int operation;
+ int element_type;
+ int start_addr;
+ int quantity;
+ int address;
+ int tid;
+ uint64_t lun;
+ char barcode[20];
+ int sides;
+} sv_param;
+
+#endif // _SMC_H_
diff --git a/usr/spc.c b/usr/spc.c
index 7711078..e696682 100644
--- a/usr/spc.c
+++ b/usr/spc.c
@@ -226,16 +226,28 @@ int spc_test_unit(int host_no, struct scsi_cmd *cmd)
if (device_reserved(cmd))
return SAM_STAT_RESERVATION_CONFLICT;
- else
+ if (cmd->dev->attrs.reset) {
+ cmd->dev->attrs.reset = 0;
+ sense_data_build(cmd, UNIT_ATTENTION, ASC_POWERON_RESET);
+ return SAM_STAT_CHECK_CONDITION;
+ }
+ if (cmd->dev->attrs.online)
return SAM_STAT_GOOD;
+ if (cmd->dev->attrs.removable)
+ sense_data_build(cmd, NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+ else
+ sense_data_build(cmd, NOT_READY, ASC_BECOMING_READY);
+
+ return SAM_STAT_CHECK_CONDITION;
}
-/*
- * Copy mode page data from list into SCSI data so it can be returned
- * to the initiator
+/**
+ * build_mode_page - static routine used by spc_mode_sense()
+ * @data: destination pointer
+ * @m: struct mode pointer (src of data)
*
- * *data -> target address (destination)
- * pg -> Pointer to mode page information (source)
+ * Description: Copy mode page data from list into SCSI data so it can
+ * be returned to the initiator
*
* Returns number of bytes copied.
*/
@@ -254,6 +266,13 @@ static int build_mode_page(uint8_t *data, struct mode_pg *pg)
return len;
}
+/**
+ * spc_mode_sense - Implement SCSI op MODE SENSE(6) and MODE SENSE(10)
+ *
+ * Reference : SPC4r11
+ * 6.11 - MODE SENSE(6)
+ * 6.12 - MODE SENSE(10)
+ */
int spc_mode_sense(int host_no, struct scsi_cmd *cmd)
{
int len = 0;
@@ -263,12 +282,6 @@ int spc_mode_sense(int host_no, struct scsi_cmd *cmd)
uint16_t asc = ASC_INVALID_FIELD_IN_CDB;
struct mode_pg *pg;
- /*
- * Reference : SPC4r11
- * 6.11 - MODE SENSE(6)
- * 6.12 - MODE SENSE(10)
- */
-
scb = cmd->scb;
mode6 = (scb[0] == 0x1a);
dbd = scb[1] & 0x8; /* Disable Block Descriptors */
@@ -480,8 +493,9 @@ static match_table_t tokens = {
{Opt_err, NULL},
};
-int spc_lu_config(struct scsi_lu *lu, char *params) {
- int err = 0;
+int lu_config(struct scsi_lu *lu, char *params, match_fn_t *fn)
+{
+ int err = TGTADM_SUCCESS;
char *p;
char buf[256];
@@ -505,15 +519,15 @@ int spc_lu_config(struct scsi_lu *lu, char *params) {
break;
case Opt_vendor_id:
match_strncpy(lu->attrs.vendor_id, &args[0],
- sizeof(lu->attrs.vendor_id));
+ sizeof(lu->attrs.vendor_id) - 1);
break;
case Opt_product_id:
match_strncpy(lu->attrs.product_id, &args[0],
- sizeof(lu->attrs.product_id));
+ sizeof(lu->attrs.product_id) - 1);
break;
case Opt_product_rev:
match_strncpy(lu->attrs.product_rev, &args[0],
- sizeof(lu->attrs.product_rev));
+ sizeof(lu->attrs.product_rev) - 1);
break;
case Opt_sense_format:
match_strncpy(buf, &args[0], sizeof(buf));
@@ -532,21 +546,22 @@ int spc_lu_config(struct scsi_lu *lu, char *params) {
err = add_mode_page(lu, buf);
break;
default:
- err = TGTADM_INVALID_REQUEST;
+ err |= fn ? fn(lu, p) : TGTADM_INVALID_REQUEST;
}
}
return err;
}
-/*
- * Set initial power-on defaults for lu
- *
- * Currently always return '0'
- */
+int spc_lu_config(struct scsi_lu *lu, char *params)
+{
+ return lu_config(lu, params, NULL);
+}
+
int spc_lu_init(struct scsi_lu *lu)
{
- strncpy(lu->attrs.vendor_id, VENDOR_ID, sizeof(lu->attrs.vendor_id));
- memcpy(lu->attrs.product_rev, "0001", 4);
+ snprintf(lu->attrs.vendor_id, sizeof(lu->attrs.vendor_id) - 1,
+ "%-16s", VENDOR_ID);
+ snprintf(lu->attrs.product_rev, 4, "%s", "0001");
lu->attrs.removable = 0;
lu->attrs.sense_format = 0;
lu->attrs.online = 0;
diff --git a/usr/spc.h b/usr/spc.h
index 4b99268..1cc8623 100644
--- a/usr/spc.h
+++ b/usr/spc.h
@@ -8,7 +8,10 @@ extern int spc_test_unit(int host_no, struct scsi_cmd *cmd);
extern int spc_request_sense(int host_no, struct scsi_cmd *cmd);
extern int spc_illegal_op(int host_no, struct scsi_cmd *cmd);
extern int spc_lu_init(struct scsi_lu *lu);
-extern int spc_lu_config(struct scsi_lu *lu, char * params);
+
+typedef int (match_fn_t)(struct scsi_lu *lu, char *params);
+extern int lu_config(struct scsi_lu *lu, char *params, match_fn_t *);
+extern int spc_lu_config(struct scsi_lu *lu, char *params);
extern void dump_cdb(struct scsi_cmd *cmd);
extern int spc_mode_sense(int host_no, struct scsi_cmd *cmd);
extern int add_mode_page(struct scsi_lu *lu, char *params);
diff --git a/usr/target.c b/usr/target.c
index d3c9cb8..2474d05 100644
--- a/usr/target.c
+++ b/usr/target.c
@@ -200,6 +200,7 @@ static int tgt_device_path_update(struct target *target,
lu->addr = 0;
lu->size = size;
lu->path = path;
+ lu->attrs.online = 1;
return 0;
}
@@ -341,6 +342,61 @@ int tgt_device_destroy(int tid, uint64_t lun, int force)
return 0;
}
+struct lu_phy_attr *lu_attr_lookup(int tid, uint64_t lun)
+{
+ struct target *target;
+ struct scsi_lu *lu;
+
+ lu = __device_lookup(tid, lun, &target);
+ if (!lu)
+ return NULL;
+ return &lu->attrs;
+}
+
+/**
+ * dtd_load_unload -- Load / unload media
+ * @tid: Target ID
+ * @lun: LUN
+ * @load: True if load, not true - unload
+ * @file: filename of 'media' top open
+ *
+ * load/unload media from the DATA TRANSFER DEVICE.
+ */
+int dtd_load_unload(int tid, uint64_t lun, int load, char *file)
+{
+ struct target *target;
+ struct scsi_lu *lu;
+ int err = TGTADM_SUCCESS;
+
+ lu = __device_lookup(tid, lun, &target);
+ if (!lu)
+ return TGTADM_NO_LUN;
+
+ if (!lu->attrs.removable)
+ return TGTADM_INVALID_REQUEST;
+
+ if (lu->path) {
+ close(lu->fd);
+ free(lu->path);
+ lu->path = NULL;
+ }
+
+ lu->size = 0;
+ lu->fd = 0;
+ lu->attrs.online = 0;
+
+ if (load) {
+ lu->path = strdup(file);
+ if (!lu->path)
+ return TGTADM_NOMEM;
+ lu->fd = backed_file_open(file, O_RDWR|O_LARGEFILE, &lu->size);
+ if (lu->fd < 0)
+ return TGTADM_UNSUPPORTED_OPERATION;
+ lu->attrs.online = 1;
+ }
+ return err;
+}
+
int device_reserve(struct scsi_cmd *cmd)
{
struct scsi_lu *lu;
@@ -1200,12 +1256,18 @@ int tgt_target_show_all(char *buf, int rest)
_TAB3 "SCSI ID: %s\n"
_TAB3 "SCSI SN: %s\n"
_TAB3 "Size: %s\n"
+ _TAB3 "Online: %s\n"
+ _TAB3 "Poweron/Reset: %s\n"
+ _TAB3 "Removable media: %s\n"
_TAB3 "Backing store: %s\n",
lu->lun,
print_type(lu->attrs.device_type),
lu->attrs.scsi_id,
lu->attrs.scsi_sn,
print_disksize(lu->size),
+ lu->attrs.online ? "Yes" : "No",
+ lu->attrs.reset ? "Yes" : "No",
+ lu->attrs.removable ? "Yes" : "No",
lu->path ? : "No backing store");
if (!strcmp(tgt_drivers[target->lid]->name, "iscsi")) {
diff --git a/usr/tgtadm.c b/usr/tgtadm.c
index c2315b6..0631a7d 100644
--- a/usr/tgtadm.c
+++ b/usr/tgtadm.c
@@ -300,6 +300,8 @@ static int str_to_device_type(char *str)
exit(EINVAL);
} else if (!strcmp(str, "cd"))
return TYPE_ROM;
+ else if (!strcmp(str, "changer"))
+ return TYPE_MEDIUM_CHANGER;
else if (!strcmp(str, "osd"))
return TYPE_OSD;
else if (!strcmp(str, "pt"))
diff --git a/usr/tgtd.h b/usr/tgtd.h
index 1c1e3e2..112f0c7 100644
--- a/usr/tgtd.h
+++ b/usr/tgtd.h
@@ -73,7 +73,7 @@ struct backingstore_template {
int (*bs_open)(struct scsi_lu *dev, char *path, int *fd, uint64_t *size);
void (*bs_close)(struct scsi_lu *dev);
int (*bs_cmd_submit)(struct scsi_cmd *cmd);
- int (*bs_cmd_done) (struct scsi_cmd *cmd);
+ int (*bs_cmd_done)(struct scsi_cmd *cmd);
};
struct mode_pg {
@@ -107,8 +107,11 @@ struct scsi_lu {
uint8_t mode_block_descriptor[BLOCK_DESCRIPTOR_LEN];
struct mode_pg *mode_pgs[0x3f];
- /* TODO: needs a structure for lots of device parameters */
struct lu_phy_attr attrs;
+
+ /* TODO: needs a structure for lots of device parameters */
+ /* Currently only used by smc module */
+ void *smc_p;
};
struct scsi_cmd {
--
1.5.2.1
More information about the stgt
mailing list