[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(&params, ",")) != 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