summaryrefslogtreecommitdiffstats
path: root/sys/cam/scsi/scsi_ch.c
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>1998-09-15 06:36:34 +0000
committergibbs <gibbs@FreeBSD.org>1998-09-15 06:36:34 +0000
commitcb986cde46f00a045e2662a010c6eec29b3fbe8a (patch)
tree036628a681da2a8894c183b293e14fc3c31dbc8b /sys/cam/scsi/scsi_ch.c
parent855593c295ac3878b7f73a83edd07f899eccc38d (diff)
downloadFreeBSD-src-cb986cde46f00a045e2662a010c6eec29b3fbe8a.zip
FreeBSD-src-cb986cde46f00a045e2662a010c6eec29b3fbe8a.tar.gz
SCSI Peripheral drivers for CAM:
da - Direct Access Devices (disks, optical devices, SS disks) cd - CDROM (or devices that can act like them, WORM, CD-RW, etc) ch - Medium Changer devices. sa - Sequential Access Devices (tape drives) pass - Application pass-thru driver targ - Target Mode "Processor Target" Emulator pt - Processor Target Devices (scanners, cpus, etc.) Submitted by: The CAM Team
Diffstat (limited to 'sys/cam/scsi/scsi_ch.c')
-rw-r--r--sys/cam/scsi/scsi_ch.c1584
1 files changed, 1584 insertions, 0 deletions
diff --git a/sys/cam/scsi/scsi_ch.c b/sys/cam/scsi/scsi_ch.c
new file mode 100644
index 0000000..0f5d9dc
--- /dev/null
+++ b/sys/cam/scsi/scsi_ch.c
@@ -0,0 +1,1584 @@
+/*
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 1997, 1998 Kenneth D. Merry.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: scsi_ch.c,v 1.1 1998/05/17 13:08:49 hans Exp hans $
+ */
+/*
+ * Derived from the NetBSD SCSI changer driver.
+ *
+ * $NetBSD: ch.c,v 1.32 1998/01/12 09:49:12 thorpej Exp $
+ *
+ */
+/*
+ * Copyright (c) 1996, 1997 Jason R. Thorpe <thorpej@and.com>
+ * All rights reserved.
+ *
+ * Partially based on an autochanger driver written by Stefan Grefen
+ * and on an autochanger driver written by the Systems Programming Group
+ * at the University of Utah Computer Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgements:
+ * This product includes software developed by Jason R. Thorpe
+ * for And Communications, http://www.and.com/
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+#include <sys/dkbad.h>
+#include <sys/malloc.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/buf.h>
+#include <sys/chio.h>
+#include <sys/errno.h>
+#include <sys/devicestat.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_extend.h>
+#include <cam/cam_periph.h>
+#include <cam/cam_xpt_periph.h>
+#include <cam/cam_queue.h>
+#include <cam/cam_debug.h>
+
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
+#include <cam/scsi/scsi_ch.h>
+
+/*
+ * Timeout definitions for various changer related commands. They may
+ * be too short for some devices (especially the timeout for INITIALIZE
+ * ELEMENT STATUS).
+ */
+
+const u_int32_t CH_TIMEOUT_MODE_SENSE = 6000;
+const u_int32_t CH_TIMEOUT_MOVE_MEDIUM = 100000;
+const u_int32_t CH_TIMEOUT_EXCHANGE_MEDIUM = 100000;
+const u_int32_t CH_TIMEOUT_POSITION_TO_ELEMENT = 100000;
+const u_int32_t CH_TIMEOUT_READ_ELEMENT_STATUS = 10000;
+const u_int32_t CH_TIMEOUT_SEND_VOLTAG = 10000;
+const u_int32_t CH_TIMEOUT_INITIALIZE_ELEMENT_STATUS = 500000;
+
+typedef enum {
+ CH_FLAG_INVALID = 0x001,
+ CH_FLAG_OPEN = 0x002
+} ch_flags;
+
+typedef enum {
+ CH_STATE_PROBE,
+ CH_STATE_NORMAL
+} ch_state;
+
+typedef enum {
+ CH_CCB_PROBE,
+ CH_CCB_WAITING
+} ch_ccb_types;
+
+#define ccb_state ppriv_field0
+#define ccb_bp ppriv_ptr1
+
+struct scsi_mode_sense_data {
+ struct scsi_mode_header_6 header;
+ union {
+ struct page_element_address_assignment ea;
+ struct page_transport_geometry_parameters tg;
+ struct page_device_capabilities cap;
+ } pages;
+};
+
+struct ch_softc {
+ ch_flags flags;
+ ch_state state;
+ union ccb saved_ccb;
+ struct devstat device_stats;
+
+ int sc_picker; /* current picker */
+
+ /*
+ * The following information is obtained from the
+ * element address assignment page.
+ */
+ int sc_firsts[4]; /* firsts, indexed by CHET_* */
+ int sc_counts[4]; /* counts, indexed by CHET_* */
+
+ /*
+ * The following mask defines the legal combinations
+ * of elements for the MOVE MEDIUM command.
+ */
+ u_int8_t sc_movemask[4];
+
+ /*
+ * As above, but for EXCHANGE MEDIUM.
+ */
+ u_int8_t sc_exchangemask[4];
+
+ /*
+ * Quirks; see below. XXX KDM not implemented yet
+ */
+ int sc_settledelay; /* delay for settle */
+};
+
+#define CHUNIT(x) (minor((x)))
+#define CH_CDEV_MAJOR 17
+
+static d_open_t chopen;
+static d_close_t chclose;
+static d_ioctl_t chioctl;
+static periph_init_t chinit;
+static periph_ctor_t chregister;
+static periph_dtor_t chcleanup;
+static periph_start_t chstart;
+static void chasync(void *callback_arg, u_int32_t code,
+ struct cam_path *path, void *arg);
+static void chdone(struct cam_periph *periph,
+ union ccb *done_ccb);
+static int cherror(union ccb *ccb, u_int32_t cam_flags,
+ u_int32_t sense_flags);
+static int chmove(struct cam_periph *periph,
+ struct changer_move *cm);
+static int chexchange(struct cam_periph *periph,
+ struct changer_exchange *ce);
+static int chposition(struct cam_periph *periph,
+ struct changer_position *cp);
+static int chgetelemstatus(struct cam_periph *periph,
+ struct changer_element_status_request *csr);
+static int chsetvoltag(struct cam_periph *periph,
+ struct changer_set_voltag_request *csvr);
+static int chielem(struct cam_periph *periph,
+ unsigned int timeout);
+static int chgetparams(struct cam_periph *periph);
+
+static struct periph_driver chdriver =
+{
+ chinit, "ch",
+ TAILQ_HEAD_INITIALIZER(chdriver.units), /* generation */ 0
+};
+
+DATA_SET(periphdriver_set, chdriver);
+
+static struct cdevsw ch_cdevsw =
+{
+ /*d_open*/ chopen,
+ /*d_close*/ chclose,
+ /*d_read*/ noread,
+ /*d_write*/ nowrite,
+ /*d_ioctl*/ chioctl,
+ /*d_stop*/ nostop,
+ /*d_reset*/ noreset,
+ /*d_devtotty*/ nodevtotty,
+ /*d_poll*/ seltrue,
+ /*d_mmap*/ nommap,
+ /*d_strategy*/ nostrategy,
+ /*d_name*/ "ch",
+ /*d_spare*/ NULL,
+ /*d_maj*/ -1,
+ /*d_dump*/ nodump,
+ /*d_psize*/ nopsize,
+ /*d_flags*/ 0,
+ /*d_maxio*/ 0,
+ /*b_maj*/ -1
+};
+
+static struct extend_array *chperiphs;
+
+void
+chinit(void)
+{
+ cam_status status;
+ struct cam_path *path;
+
+ /*
+ * Create our extend array for storing the devices we attach to.
+ */
+ chperiphs = cam_extend_new();
+ if (chperiphs == NULL) {
+ printf("ch: Failed to alloc extend array!\n");
+ return;
+ }
+
+ /*
+ * Install a global async callback. This callback will
+ * receive async callbacks like "new device found".
+ */
+ status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
+ CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
+
+ if (status == CAM_REQ_CMP) {
+ struct ccb_setasync csa;
+
+ xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = AC_FOUND_DEVICE;
+ csa.callback = chasync;
+ csa.callback_arg = NULL;
+ xpt_action((union ccb *)&csa);
+ status = csa.ccb_h.status;
+ xpt_free_path(path);
+ }
+
+ if (status != CAM_REQ_CMP) {
+ printf("ch: Failed to attach master async callback "
+ "due to status 0x%x!\n", status);
+ } else {
+ dev_t dev;
+
+ /* If we were successfull, register our devsw */
+ dev = makedev(CH_CDEV_MAJOR, 0);
+ cdevsw_add(&dev, &ch_cdevsw, NULL);
+ }
+}
+
+static void
+chcleanup(struct cam_periph *periph)
+{
+
+ cam_extend_release(chperiphs, periph->unit_number);
+ xpt_print_path(periph->path);
+ printf("removing device entry\n");
+ free(periph->softc, M_DEVBUF);
+}
+
+static void
+chasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
+{
+ struct cam_periph *periph;
+
+ periph = (struct cam_periph *)callback_arg;
+
+ switch(code) {
+ case AC_FOUND_DEVICE:
+ {
+ struct ccb_getdev *cgd;
+ cam_status status;
+
+ cgd = (struct ccb_getdev *)arg;
+
+ if (cgd->pd_type != T_CHANGER)
+ break;
+
+ /*
+ * Allocate a peripheral instance for
+ * this device and start the probe
+ * process.
+ */
+ status = cam_periph_alloc(chregister, chcleanup, chstart,
+ "ch", CAM_PERIPH_BIO, cgd->ccb_h.path,
+ chasync, AC_FOUND_DEVICE, cgd);
+
+ if (status != CAM_REQ_CMP
+ && status != CAM_REQ_INPROG)
+ printf("chasync: Unable to probe new device "
+ "due to status 0x%x\n", status);
+
+ break;
+
+ }
+ case AC_LOST_DEVICE:
+ {
+ int s;
+ struct ch_softc *softc;
+ struct ccb_setasync csa;
+
+ softc = (struct ch_softc *)periph->softc;
+
+ /*
+ * Insure that no other async callbacks that
+ * might affect this peripheral can come through.
+ */
+ s = splcam();
+
+ /*
+ * De-register any async callbacks.
+ */
+ xpt_setup_ccb(&csa.ccb_h, periph->path,
+ /* priority */ 5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = 0;
+ csa.callback = chasync;
+ csa.callback_arg = periph;
+ xpt_action((union ccb *)&csa);
+
+ softc->flags |= CH_FLAG_INVALID;
+
+ devstat_remove_entry(&softc->device_stats);
+
+ xpt_print_path(periph->path);
+ printf("lost device\n");
+
+ splx(s);
+
+ cam_periph_invalidate(periph);
+ break;
+ }
+ case AC_TRANSFER_NEG:
+ case AC_SENT_BDR:
+ case AC_SCSI_AEN:
+ case AC_UNSOL_RESEL:
+ case AC_BUS_RESET:
+ default:
+ break;
+ }
+}
+
+static cam_status
+chregister(struct cam_periph *periph, void *arg)
+{
+ int s;
+ struct ch_softc *softc;
+ struct ccb_setasync csa;
+ struct ccb_getdev *cgd;
+
+ cgd = (struct ccb_getdev *)arg;
+ if (periph == NULL) {
+ printf("chregister: periph was NULL!!\n");
+ return(CAM_REQ_CMP_ERR);
+ }
+
+ if (cgd == NULL) {
+ printf("chregister: no getdev CCB, can't register device\n");
+ return(CAM_REQ_CMP_ERR);
+ }
+
+ softc = (struct ch_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
+
+ if (softc == NULL) {
+ printf("chregister: Unable to probe new device. "
+ "Unable to allocate softc\n");
+ return(CAM_REQ_CMP_ERR);
+ }
+
+ bzero(softc, sizeof(*softc));
+ softc->state = CH_STATE_PROBE;
+ periph->softc = softc;
+ cam_extend_set(chperiphs, periph->unit_number, periph);
+
+ /*
+ * Changers don't have a blocksize, and obviously don't support
+ * tagged queueing.
+ */
+ devstat_add_entry(&softc->device_stats, "ch",
+ periph->unit_number, 0,
+ DEVSTAT_NO_BLOCKSIZE | DEVSTAT_NO_ORDERED_TAGS,
+ cgd->pd_type | DEVSTAT_TYPE_IF_SCSI);
+
+ /*
+ * Add an async callback so that we get
+ * notified if this device goes away.
+ */
+ xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = AC_LOST_DEVICE;
+ csa.callback = chasync;
+ csa.callback_arg = periph;
+ xpt_action((union ccb *)&csa);
+
+ /*
+ * Lock this peripheral until we are setup.
+ * This first call can't block
+ */
+ (void)cam_periph_lock(periph, PRIBIO);
+ xpt_schedule(periph, /*priority*/5);
+
+ return(CAM_REQ_CMP);
+}
+
+static int
+chopen(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ struct cam_periph *periph;
+ struct ch_softc *softc;
+ int unit, error;
+
+ unit = CHUNIT(dev);
+ periph = cam_extend_get(chperiphs, unit);
+
+ if (periph == NULL)
+ return(ENXIO);
+
+ softc = (struct ch_softc *)periph->softc;
+
+ if (softc->flags & CH_FLAG_INVALID)
+ return(ENXIO);
+
+ if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0)
+ return (error);
+
+ if ((softc->flags & CH_FLAG_OPEN) == 0) {
+ if (cam_periph_acquire(periph) != CAM_REQ_CMP)
+ return(ENXIO);
+ softc->flags |= CH_FLAG_OPEN;
+ }
+
+ /*
+ * Load information about this changer device into the softc.
+ */
+ if ((error = chgetparams(periph)) != 0) {
+ softc->flags &= ~CH_FLAG_OPEN;
+ cam_periph_unlock(periph);
+ cam_periph_release(periph);
+ return(error);
+ }
+
+ cam_periph_unlock(periph);
+
+ return(error);
+}
+
+static int
+chclose(dev_t dev, int flag, int fmt, struct proc *p)
+{
+ struct cam_periph *periph;
+ struct ch_softc *softc;
+ int unit, error;
+
+ error = 0;
+
+ unit = CHUNIT(dev);
+ periph = cam_extend_get(chperiphs, unit);
+ if (periph == NULL)
+ return(ENXIO);
+
+ softc = (struct ch_softc *)periph->softc;
+
+ if ((error = cam_periph_lock(periph, PRIBIO)) != 0)
+ return(error);
+
+ softc->flags &= ~CH_FLAG_OPEN;
+
+ cam_periph_unlock(periph);
+ cam_periph_release(periph);
+
+ return(0);
+}
+
+static void
+chstart(struct cam_periph *periph, union ccb *start_ccb)
+{
+ struct ch_softc *softc;
+ int s;
+
+ softc = (struct ch_softc *)periph->softc;
+
+ switch (softc->state) {
+ case CH_STATE_NORMAL:
+ {
+ s = splbio();
+ if (periph->immediate_priority <= periph->pinfo.priority){
+ start_ccb->ccb_h.ccb_state = CH_CCB_WAITING;
+
+ SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
+ periph_links.sle);
+ periph->immediate_priority = CAM_PRIORITY_NONE;
+ splx(s);
+ wakeup(&periph->ccb_list);
+ } else
+ splx(s);
+ break;
+ }
+ case CH_STATE_PROBE:
+ {
+ struct scsi_mode_sense_data *sense_data;
+
+ sense_data = (struct scsi_mode_sense_data *)malloc(
+ sizeof(*sense_data),
+ M_TEMP, M_NOWAIT);
+
+ if (sense_data == NULL) {
+ printf("chstart: couldn't malloc mode sense data\n");
+ break;
+ }
+ bzero(sense_data, sizeof(*sense_data));
+
+ /*
+ * Get the element address assignment page.
+ */
+ scsi_mode_sense(&start_ccb->csio,
+ /* retries */ 1,
+ /* cbfcnp */ chdone,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* dbd */ TRUE,
+ /* page_code */ SMS_PAGE_CTRL_CURRENT,
+ /* page */ CH_ELEMENT_ADDR_ASSIGN_PAGE,
+ /* param_buf */ (u_int8_t *)sense_data,
+ /* param_len */ sizeof(*sense_data),
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ CH_TIMEOUT_MODE_SENSE);
+
+ start_ccb->ccb_h.ccb_bp = NULL;
+ start_ccb->ccb_h.ccb_state = CH_CCB_PROBE;
+ xpt_action(start_ccb);
+ break;
+ }
+ }
+}
+
+static void
+chdone(struct cam_periph *periph, union ccb *done_ccb)
+{
+ struct ch_softc *softc;
+ struct ccb_scsiio *csio;
+
+ softc = (struct ch_softc *)periph->softc;
+ csio = &done_ccb->csio;
+
+ switch(done_ccb->ccb_h.ccb_state) {
+ case CH_CCB_PROBE:
+ {
+ struct scsi_mode_sense_data *sense_data;
+ char announce_buf[80];
+
+ sense_data = (struct scsi_mode_sense_data *)csio->data_ptr;
+
+ if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP){
+
+ softc->sc_firsts[CHET_MT] =
+ scsi_2btoul(sense_data->pages.ea.mtea);
+ softc->sc_counts[CHET_MT] =
+ scsi_2btoul(sense_data->pages.ea.nmte);
+ softc->sc_firsts[CHET_ST] =
+ scsi_2btoul(sense_data->pages.ea.fsea);
+ softc->sc_counts[CHET_ST] =
+ scsi_2btoul(sense_data->pages.ea.nse);
+ softc->sc_firsts[CHET_IE] =
+ scsi_2btoul(sense_data->pages.ea.fieea);
+ softc->sc_counts[CHET_IE] =
+ scsi_2btoul(sense_data->pages.ea.niee);
+ softc->sc_firsts[CHET_DT] =
+ scsi_2btoul(sense_data->pages.ea.fdtea);
+ softc->sc_counts[CHET_DT] =
+ scsi_2btoul(sense_data->pages.ea.ndte);
+ softc->sc_picker = softc->sc_firsts[CHET_MT];
+
+#define PLURAL(c) (c) == 1 ? "" : "s"
+ sprintf(announce_buf, "%d slot%s, %d drive%s, "
+ "%d picker%s, %d portal%s",
+ softc->sc_counts[CHET_ST],
+ PLURAL(softc->sc_counts[CHET_ST]),
+ softc->sc_counts[CHET_DT],
+ PLURAL(softc->sc_counts[CHET_DT]),
+ softc->sc_counts[CHET_MT],
+ PLURAL(softc->sc_counts[CHET_MT]),
+ softc->sc_counts[CHET_IE],
+ PLURAL(softc->sc_counts[CHET_IE]));
+#undef PLURAL
+ } else {
+ int error;
+
+ error = cherror(done_ccb, 0, SF_RETRY_UA);
+ /*
+ * Retry any UNIT ATTENTION type errors. They
+ * are expected at boot.
+ */
+ if (error == ERESTART) {
+ /*
+ * A retry was scheuled, so
+ * just return.
+ */
+ return;
+ } else if (error != 0) {
+ /* Don't wedge this device's queue */
+ cam_release_devq(done_ccb->ccb_h.path,
+ /*relsim_flags*/0,
+ /*reduction*/0,
+ /*timeout*/0,
+ /*getcount_only*/0);
+ sprintf(announce_buf,
+ "Attempt to query device parameters"
+ " failed");
+ }
+ }
+ xpt_announce_periph(periph, announce_buf);
+ softc->state = CH_STATE_NORMAL;
+ free(sense_data, M_TEMP);
+ cam_periph_unlock(periph);
+ break;
+ }
+ case CH_CCB_WAITING:
+ {
+ /* Caller will release the CCB */
+ wakeup(&done_ccb->ccb_h.cbfcnp);
+ return;
+ }
+ }
+ xpt_release_ccb(done_ccb);
+}
+
+static int
+cherror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
+{
+ struct ch_softc *softc;
+ struct cam_periph *periph;
+
+ periph = xpt_path_periph(ccb->ccb_h.path);
+ softc = (struct ch_softc *)periph->softc;
+
+ return (cam_periph_error(ccb, cam_flags, sense_flags,
+ &softc->saved_ccb));
+}
+
+static int
+chioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
+{
+ struct cam_periph *periph;
+ struct ch_softc *softc;
+ u_int8_t unit;
+ int error;
+
+ unit = CHUNIT(dev);
+
+ periph = cam_extend_get(chperiphs, unit);
+ if (periph == NULL)
+ return(ENXIO);
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering chioctl\n"));
+
+ softc = (struct ch_softc *)periph->softc;
+
+ error = 0;
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
+ ("trying to do ioctl %#x\n", cmd));
+
+ /*
+ * If this command can change the device's state, we must
+ * have the device open for writing.
+ */
+ switch (cmd) {
+ case CHIOGPICKER:
+ case CHIOGPARAMS:
+ case CHIOGSTATUS:
+ break;
+
+ default:
+ if ((flag & FWRITE) == 0)
+ return (EBADF);
+ }
+
+ switch (cmd) {
+ case CHIOMOVE:
+ error = chmove(periph, (struct changer_move *)addr);
+ break;
+
+ case CHIOEXCHANGE:
+ error = chexchange(periph, (struct changer_exchange *)addr);
+ break;
+
+ case CHIOPOSITION:
+ error = chposition(periph, (struct changer_position *)addr);
+ break;
+
+ case CHIOGPICKER:
+ *(int *)addr = softc->sc_picker - softc->sc_firsts[CHET_MT];
+ break;
+
+ case CHIOSPICKER:
+ {
+ int new_picker = *(int *)addr;
+
+ if (new_picker > (softc->sc_counts[CHET_MT] - 1))
+ return (EINVAL);
+ softc->sc_picker = softc->sc_firsts[CHET_MT] + new_picker;
+ break;
+ }
+ case CHIOGPARAMS:
+ {
+ struct changer_params *cp = (struct changer_params *)addr;
+
+ cp->cp_npickers = softc->sc_counts[CHET_MT];
+ cp->cp_nslots = softc->sc_counts[CHET_ST];
+ cp->cp_nportals = softc->sc_counts[CHET_IE];
+ cp->cp_ndrives = softc->sc_counts[CHET_DT];
+ break;
+ }
+ case CHIOIELEM:
+ error = chielem(periph, *(unsigned int *)addr);
+ break;
+
+ case CHIOGSTATUS:
+ {
+ error = chgetelemstatus(periph,
+ (struct changer_element_status_request *) addr);
+ break;
+ }
+
+ case CHIOSETVOLTAG:
+ {
+ error = chsetvoltag(periph,
+ (struct changer_set_voltag_request *) addr);
+ break;
+ }
+
+ /* Implement prevent/allow? */
+
+ default:
+ error = cam_periph_ioctl(periph, cmd, addr, cherror);
+ break;
+ }
+
+ return (error);
+}
+
+static int
+chmove(struct cam_periph *periph, struct changer_move *cm)
+{
+ struct ch_softc *softc;
+ u_int16_t fromelem, toelem;
+ union ccb *ccb;
+ int error;
+
+ error = 0;
+ softc = (struct ch_softc *)periph->softc;
+
+ /*
+ * Check arguments.
+ */
+ if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT))
+ return (EINVAL);
+ if ((cm->cm_fromunit > (softc->sc_counts[cm->cm_fromtype] - 1)) ||
+ (cm->cm_tounit > (softc->sc_counts[cm->cm_totype] - 1)))
+ return (ENODEV);
+
+ /*
+ * Check the request against the changer's capabilities.
+ */
+ if ((softc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0)
+ return (EINVAL);
+
+ /*
+ * Calculate the source and destination elements.
+ */
+ fromelem = softc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit;
+ toelem = softc->sc_firsts[cm->cm_totype] + cm->cm_tounit;
+
+ ccb = cam_periph_getccb(periph, /*priority*/ 1);
+
+ scsi_move_medium(&ccb->csio,
+ /* retries */ 1,
+ /* cbfcnp */ chdone,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* tea */ softc->sc_picker,
+ /* src */ fromelem,
+ /* dst */ toelem,
+ /* invert */ (cm->cm_flags & CM_INVERT) ? TRUE : FALSE,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ CH_TIMEOUT_MOVE_MEDIUM);
+
+ error = cam_periph_runccb(ccb, cherror, /*cam_flags*/0,
+ /*sense_flags*/ SF_RETRY_UA,
+ &softc->device_stats);
+
+ xpt_release_ccb(ccb);
+
+ return(error);
+}
+
+static int
+chexchange(struct cam_periph *periph, struct changer_exchange *ce)
+{
+ struct ch_softc *softc;
+ u_int16_t src, dst1, dst2;
+ union ccb *ccb;
+ int error;
+
+ error = 0;
+ softc = (struct ch_softc *)periph->softc;
+ /*
+ * Check arguments.
+ */
+ if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) ||
+ (ce->ce_sdsttype > CHET_DT))
+ return (EINVAL);
+ if ((ce->ce_srcunit > (softc->sc_counts[ce->ce_srctype] - 1)) ||
+ (ce->ce_fdstunit > (softc->sc_counts[ce->ce_fdsttype] - 1)) ||
+ (ce->ce_sdstunit > (softc->sc_counts[ce->ce_sdsttype] - 1)))
+ return (ENODEV);
+
+ /*
+ * Check the request against the changer's capabilities.
+ */
+ if (((softc->sc_exchangemask[ce->ce_srctype] &
+ (1 << ce->ce_fdsttype)) == 0) ||
+ ((softc->sc_exchangemask[ce->ce_fdsttype] &
+ (1 << ce->ce_sdsttype)) == 0))
+ return (EINVAL);
+
+ /*
+ * Calculate the source and destination elements.
+ */
+ src = softc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit;
+ dst1 = softc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit;
+ dst2 = softc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit;
+
+ ccb = cam_periph_getccb(periph, /*priority*/ 1);
+
+ scsi_exchange_medium(&ccb->csio,
+ /* retries */ 1,
+ /* cbfcnp */ chdone,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* tea */ softc->sc_picker,
+ /* src */ src,
+ /* dst1 */ dst1,
+ /* dst2 */ dst2,
+ /* invert1 */ (ce->ce_flags & CE_INVERT1) ?
+ TRUE : FALSE,
+ /* invert2 */ (ce->ce_flags & CE_INVERT2) ?
+ TRUE : FALSE,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ CH_TIMEOUT_EXCHANGE_MEDIUM);
+
+ error = cam_periph_runccb(ccb, cherror, /*cam_flags*/0,
+ /*sense_flags*/ SF_RETRY_UA,
+ &softc->device_stats);
+
+ xpt_release_ccb(ccb);
+
+ return(error);
+}
+
+static int
+chposition(struct cam_periph *periph, struct changer_position *cp)
+{
+ struct ch_softc *softc;
+ u_int16_t dst;
+ union ccb *ccb;
+ int error;
+
+ error = 0;
+ softc = (struct ch_softc *)periph->softc;
+
+ /*
+ * Check arguments.
+ */
+ if (cp->cp_type > CHET_DT)
+ return (EINVAL);
+ if (cp->cp_unit > (softc->sc_counts[cp->cp_type] - 1))
+ return (ENODEV);
+
+ /*
+ * Calculate the destination element.
+ */
+ dst = softc->sc_firsts[cp->cp_type] + cp->cp_unit;
+
+ ccb = cam_periph_getccb(periph, /*priority*/ 1);
+
+ scsi_position_to_element(&ccb->csio,
+ /* retries */ 1,
+ /* cbfcnp */ chdone,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* tea */ softc->sc_picker,
+ /* dst */ dst,
+ /* invert */ (cp->cp_flags & CP_INVERT) ?
+ TRUE : FALSE,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ CH_TIMEOUT_POSITION_TO_ELEMENT);
+
+ error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
+ /*sense_flags*/ SF_RETRY_UA,
+ &softc->device_stats);
+
+ xpt_release_ccb(ccb);
+
+ return(error);
+}
+
+/*
+ * Copy a volume tag to a volume_tag struct, converting SCSI byte order
+ * to host native byte order in the volume serial number. The volume
+ * label as returned by the changer is transferred to user mode as
+ * nul-terminated string. Volume labels are truncated at the first
+ * space, as suggested by SCSI-2.
+ */
+static void
+copy_voltag(struct changer_voltag *uvoltag, struct volume_tag *voltag)
+{
+ int i;
+ for (i=0; i<CH_VOLTAG_MAXLEN; i++) {
+ char c = voltag->vif[i];
+ if (c && c != ' ')
+ uvoltag->cv_volid[i] = c;
+ else
+ break;
+ }
+ uvoltag->cv_serial = scsi_2btoul(voltag->vsn);
+}
+
+/*
+ * Copy an an element status descriptor to a user-mode
+ * changer_element_status structure.
+ */
+
+static void
+copy_element_status(struct ch_softc *softc,
+ u_int16_t flags,
+ struct read_element_status_descriptor *desc,
+ struct changer_element_status *ces)
+{
+ u_int16_t eaddr = scsi_2btoul(desc->eaddr);
+ u_int16_t et;
+
+ ces->ces_int_addr = eaddr;
+ /* set up logical address in element status */
+ for (et = CHET_MT; et <= CHET_DT; et++) {
+ if ((softc->sc_firsts[et] <= eaddr)
+ && ((softc->sc_firsts[et] + softc->sc_counts[et])
+ > eaddr)) {
+ ces->ces_addr = eaddr - softc->sc_firsts[et];
+ ces->ces_type = et;
+ break;
+ }
+ }
+
+ ces->ces_flags = desc->flags1;
+
+ ces->ces_sensecode = desc->sense_code;
+ ces->ces_sensequal = desc->sense_qual;
+
+ if (desc->flags2 & READ_ELEMENT_STATUS_INVERT)
+ ces->ces_flags |= CES_INVERT;
+
+ if (desc->flags2 & READ_ELEMENT_STATUS_SVALID) {
+
+ eaddr = scsi_2btoul(desc->ssea);
+
+ /* convert source address to logical format */
+ for (et = CHET_MT; et <= CHET_DT; et++) {
+ if ((softc->sc_firsts[et] <= eaddr)
+ && ((softc->sc_firsts[et] + softc->sc_counts[et])
+ > eaddr)) {
+ ces->ces_source_addr =
+ eaddr - softc->sc_firsts[et];
+ ces->ces_source_type = et;
+ ces->ces_flags |= CES_SOURCE_VALID;
+ break;
+ }
+ }
+
+ if (!(ces->ces_flags & CES_SOURCE_VALID))
+ printf("ch: warning: could not map element source "
+ "address %ud to a valid element type",
+ eaddr);
+ }
+
+
+ if (flags & READ_ELEMENT_STATUS_PVOLTAG)
+ copy_voltag(&(ces->ces_pvoltag), &(desc->pvoltag));
+ if (flags & READ_ELEMENT_STATUS_AVOLTAG)
+ copy_voltag(&(ces->ces_avoltag), &(desc->avoltag));
+
+ if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_IDVALID) {
+ ces->ces_flags |= CES_SCSIID_VALID;
+ ces->ces_scsi_id = desc->dt_scsi_addr;
+ }
+
+ if (desc->dt_scsi_addr & READ_ELEMENT_STATUS_DT_LUVALID) {
+ ces->ces_flags |= CES_LUN_VALID;
+ ces->ces_scsi_lun =
+ desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_LUNMASK;
+ }
+}
+
+static int
+chgetelemstatus(struct cam_periph *periph,
+ struct changer_element_status_request *cesr)
+{
+ struct read_element_status_header *st_hdr;
+ struct read_element_status_page_header *pg_hdr;
+ struct read_element_status_descriptor *desc;
+ caddr_t data = NULL;
+ size_t size, desclen;
+ int avail, i, error = 0;
+ struct changer_element_status *user_data = NULL;
+ struct ch_softc *softc;
+ union ccb *ccb;
+ int chet = cesr->cesr_element_type;
+ int want_voltags = (cesr->cesr_flags & CESR_VOLTAGS) ? 1 : 0;
+
+ softc = (struct ch_softc *)periph->softc;
+
+ /* perform argument checking */
+
+ /*
+ * Perform a range check on the cesr_element_{base,count}
+ * request argument fields.
+ */
+ if ((softc->sc_counts[chet] - cesr->cesr_element_base) <= 0
+ || (cesr->cesr_element_base + cesr->cesr_element_count)
+ > softc->sc_counts[chet])
+ return (EINVAL);
+
+ /*
+ * Request one descriptor for the given element type. This
+ * is used to determine the size of the descriptor so that
+ * we can allocate enough storage for all of them. We assume
+ * that the first one can fit into 1k.
+ */
+ data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK);
+
+ ccb = cam_periph_getccb(periph, /*priority*/ 1);
+
+ scsi_read_element_status(&ccb->csio,
+ /* retries */ 1,
+ /* cbfcnp */ chdone,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* voltag */ want_voltags,
+ /* sea */ softc->sc_firsts[chet],
+ /* count */ 1,
+ /* data_ptr */ data,
+ /* dxfer_len */ 1024,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ CH_TIMEOUT_READ_ELEMENT_STATUS);
+
+ error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
+ /* sense_flags */ SF_RETRY_UA,
+ &softc->device_stats);
+
+ if (error)
+ goto done;
+
+ st_hdr = (struct read_element_status_header *)data;
+ pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr +
+ sizeof(struct read_element_status_header));
+ desclen = scsi_2btoul(pg_hdr->edl);
+
+ size = sizeof(struct read_element_status_header) +
+ sizeof(struct read_element_status_page_header) +
+ (desclen * cesr->cesr_element_count);
+
+ /*
+ * Reallocate storage for descriptors and get them from the
+ * device.
+ */
+ free(data, M_DEVBUF);
+ data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK);
+
+ scsi_read_element_status(&ccb->csio,
+ /* retries */ 1,
+ /* cbfcnp */ chdone,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* voltag */ want_voltags,
+ /* sea */ softc->sc_firsts[chet]
+ + cesr->cesr_element_base,
+ /* count */ cesr->cesr_element_count,
+ /* data_ptr */ data,
+ /* dxfer_len */ size,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ CH_TIMEOUT_READ_ELEMENT_STATUS);
+
+ error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
+ /* sense_flags */ SF_RETRY_UA,
+ &softc->device_stats);
+
+ if (error)
+ goto done;
+
+ /*
+ * Fill in the user status array.
+ */
+ st_hdr = (struct read_element_status_header *)data;
+ avail = scsi_2btoul(st_hdr->count);
+
+ if (avail != cesr->cesr_element_count) {
+ xpt_print_path(periph->path);
+ printf("warning, READ ELEMENT STATUS avail != count\n");
+ }
+
+ user_data = (struct changer_element_status *)
+ malloc(avail * sizeof(struct changer_element_status),
+ M_DEVBUF, M_WAITOK);
+ bzero(user_data, avail * sizeof(struct changer_element_status));
+
+ desc = (struct read_element_status_descriptor *)((u_long)data +
+ sizeof(struct read_element_status_header) +
+ sizeof(struct read_element_status_page_header));
+ /*
+ * Set up the individual element status structures
+ */
+ for (i = 0; i < avail; ++i) {
+ struct changer_element_status *ces = &(user_data[i]);
+
+ copy_element_status(softc, pg_hdr->flags, desc, ces);
+
+ (u_long)desc += desclen;
+ }
+
+ /* Copy element status structures out to userspace. */
+ error = copyout(user_data,
+ cesr->cesr_element_status,
+ avail * sizeof(struct changer_element_status));
+
+ done:
+ xpt_release_ccb(ccb);
+
+ if (data != NULL)
+ free(data, M_DEVBUF);
+ if (user_data != NULL)
+ free(user_data, M_DEVBUF);
+
+ return (error);
+}
+
+static int
+chielem(struct cam_periph *periph,
+ unsigned int timeout)
+{
+ union ccb *ccb;
+ struct ch_softc *softc;
+ int error;
+
+ if (!timeout) {
+ timeout = CH_TIMEOUT_INITIALIZE_ELEMENT_STATUS;
+ } else {
+ timeout *= 1000;
+ }
+
+ error = 0;
+ softc = (struct ch_softc *)periph->softc;
+
+ ccb = cam_periph_getccb(periph, /*priority*/ 1);
+
+ scsi_initialize_element_status(&ccb->csio,
+ /* retries */ 1,
+ /* cbfcnp */ chdone,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ timeout);
+
+ error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
+ /* sense_flags */ SF_RETRY_UA,
+ &softc->device_stats);
+
+ xpt_release_ccb(ccb);
+
+ return(error);
+}
+
+static int
+chsetvoltag(struct cam_periph *periph,
+ struct changer_set_voltag_request *csvr)
+{
+ union ccb *ccb;
+ struct ch_softc *softc;
+ u_int16_t ea;
+ u_int8_t sac;
+ struct scsi_send_volume_tag_parameters ssvtp;
+ int error;
+ int i;
+
+ error = 0;
+ softc = (struct ch_softc *)periph->softc;
+
+ bzero(&ssvtp, sizeof(ssvtp));
+ for (i=0; i<sizeof(ssvtp.vitf); i++) {
+ ssvtp.vitf[i] = ' ';
+ }
+
+ /*
+ * Check arguments.
+ */
+ if (csvr->csvr_type > CHET_DT)
+ return EINVAL;
+ if (csvr->csvr_addr > (softc->sc_counts[csvr->csvr_type] - 1))
+ return ENODEV;
+
+ ea = softc->sc_firsts[csvr->csvr_type] + csvr->csvr_addr;
+
+ if (csvr->csvr_flags & CSVR_ALTERNATE) {
+ switch (csvr->csvr_flags & CSVR_MODE_MASK) {
+ case CSVR_MODE_SET:
+ sac = SEND_VOLUME_TAG_ASSERT_ALTERNATE;
+ break;
+ case CSVR_MODE_REPLACE:
+ sac = SEND_VOLUME_TAG_REPLACE_ALTERNATE;
+ break;
+ case CSVR_MODE_CLEAR:
+ sac = SEND_VOLUME_TAG_UNDEFINED_ALTERNATE;
+ break;
+ default:
+ error = EINVAL;
+ goto out;
+ }
+ } else {
+ switch (csvr->csvr_flags & CSVR_MODE_MASK) {
+ case CSVR_MODE_SET:
+ sac = SEND_VOLUME_TAG_ASSERT_PRIMARY;
+ break;
+ case CSVR_MODE_REPLACE:
+ sac = SEND_VOLUME_TAG_REPLACE_PRIMARY;
+ break;
+ case CSVR_MODE_CLEAR:
+ sac = SEND_VOLUME_TAG_UNDEFINED_PRIMARY;
+ break;
+ default:
+ error = EINVAL;
+ goto out;
+ }
+ }
+
+ memcpy(ssvtp.vitf, csvr->csvr_voltag.cv_volid,
+ min(strlen(csvr->csvr_voltag.cv_volid), sizeof(ssvtp.vitf)));
+ scsi_ulto2b(csvr->csvr_voltag.cv_serial, ssvtp.minvsn);
+
+ ccb = cam_periph_getccb(periph, /*priority*/ 1);
+
+ scsi_send_volume_tag(&ccb->csio,
+ /* retries */ 1,
+ /* cbfcnp */ chdone,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* element_address */ ea,
+ /* send_action_code */ sac,
+ /* parameters */ &ssvtp,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ CH_TIMEOUT_SEND_VOLTAG);
+
+ error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
+ /*sense_flags*/ SF_RETRY_UA,
+ &softc->device_stats);
+
+ xpt_release_ccb(ccb);
+
+ out:
+ return error;
+}
+
+static int
+chgetparams(struct cam_periph *periph)
+{
+ union ccb *ccb;
+ struct ch_softc *softc;
+ struct scsi_mode_sense_data *sense_data;
+ int error, from;
+ u_int8_t *moves, *exchanges;
+
+ error = 0;
+
+ softc = (struct ch_softc *)periph->softc;
+
+ ccb = cam_periph_getccb(periph, /*priority*/ 1);
+
+ sense_data = (struct scsi_mode_sense_data *)malloc(sizeof(*sense_data),
+ M_TEMP, M_NOWAIT);
+ if (sense_data == NULL) {
+ printf("chgetparams: couldn't malloc mode sense data\n");
+ return(ENOSPC);
+ }
+
+ bzero(sense_data, sizeof(*sense_data));
+
+ /*
+ * Get the element address assignment page.
+ */
+ scsi_mode_sense(&ccb->csio,
+ /* retries */ 1,
+ /* cbfcnp */ chdone,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* dbd */ TRUE,
+ /* page_code */ SMS_PAGE_CTRL_CURRENT,
+ /* page */ CH_ELEMENT_ADDR_ASSIGN_PAGE,
+ /* param_buf */ (u_int8_t *)sense_data,
+ /* param_len */ sizeof(*sense_data),
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ CH_TIMEOUT_MODE_SENSE);
+
+ error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
+ /* sense_flags */ SF_RETRY_UA,
+ &softc->device_stats);
+
+ if (error) {
+ xpt_print_path(periph->path);
+ printf("chgetparams: error getting element address page\n");
+ xpt_release_ccb(ccb);
+ return(error);
+ }
+
+ softc->sc_firsts[CHET_MT] = scsi_2btoul(sense_data->pages.ea.mtea);
+ softc->sc_counts[CHET_MT] = scsi_2btoul(sense_data->pages.ea.nmte);
+ softc->sc_firsts[CHET_ST] = scsi_2btoul(sense_data->pages.ea.fsea);
+ softc->sc_counts[CHET_ST] = scsi_2btoul(sense_data->pages.ea.nse);
+ softc->sc_firsts[CHET_IE] = scsi_2btoul(sense_data->pages.ea.fieea);
+ softc->sc_counts[CHET_IE] = scsi_2btoul(sense_data->pages.ea.niee);
+ softc->sc_firsts[CHET_DT] = scsi_2btoul(sense_data->pages.ea.fdtea);
+ softc->sc_counts[CHET_DT] = scsi_2btoul(sense_data->pages.ea.ndte);
+
+ bzero(sense_data, sizeof(*sense_data));
+
+ /*
+ * Now get the device capabilities page.
+ */
+ scsi_mode_sense(&ccb->csio,
+ /* retries */ 1,
+ /* cbfcnp */ chdone,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* dbd */ TRUE,
+ /* page_code */ SMS_PAGE_CTRL_CURRENT,
+ /* page */ CH_DEVICE_CAP_PAGE,
+ /* param_buf */ (u_int8_t *)sense_data,
+ /* param_len */ sizeof(*sense_data),
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ CH_TIMEOUT_MODE_SENSE);
+
+ error = cam_periph_runccb(ccb, cherror, /*cam_flags*/ 0,
+ /* sense_flags */ SF_RETRY_UA,
+ &softc->device_stats);
+
+ xpt_release_ccb(ccb);
+
+ if (error) {
+ xpt_print_path(periph->path);
+ printf("chgetparams: error getting device capabilities page\n");
+ return(error);
+ }
+
+
+ bzero(softc->sc_movemask, sizeof(softc->sc_movemask));
+ bzero(softc->sc_exchangemask, sizeof(softc->sc_exchangemask));
+ moves = &sense_data->pages.cap.move_from_mt;
+ exchanges = &sense_data->pages.cap.exchange_with_mt;
+ for (from = CHET_MT; from <= CHET_DT; ++from) {
+ softc->sc_movemask[from] = moves[from];
+ softc->sc_exchangemask[from] = exchanges[from];
+ }
+
+ return(error);
+}
+
+void
+scsi_move_medium(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ u_int8_t tag_action, u_int32_t tea, u_int32_t src,
+ u_int32_t dst, int invert, u_int8_t sense_len,
+ u_int32_t timeout)
+{
+ struct scsi_move_medium *scsi_cmd;
+
+ scsi_cmd = (struct scsi_move_medium *)&csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+
+ scsi_cmd->opcode = MOVE_MEDIUM;
+
+ scsi_ulto2b(tea, scsi_cmd->tea);
+ scsi_ulto2b(src, scsi_cmd->src);
+ scsi_ulto2b(dst, scsi_cmd->dst);
+
+ if (invert)
+ scsi_cmd->invert |= MOVE_MEDIUM_INVERT;
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/ CAM_DIR_NONE,
+ tag_action,
+ /*data_ptr*/ NULL,
+ /*dxfer_len*/ 0,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
+
+void
+scsi_exchange_medium(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ u_int8_t tag_action, u_int32_t tea, u_int32_t src,
+ u_int32_t dst1, u_int32_t dst2, int invert1,
+ int invert2, u_int8_t sense_len, u_int32_t timeout)
+{
+ struct scsi_exchange_medium *scsi_cmd;
+
+ scsi_cmd = (struct scsi_exchange_medium *)&csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+
+ scsi_cmd->opcode = EXCHANGE_MEDIUM;
+
+ scsi_ulto2b(tea, scsi_cmd->tea);
+ scsi_ulto2b(src, scsi_cmd->src);
+ scsi_ulto2b(dst1, scsi_cmd->fdst);
+ scsi_ulto2b(dst2, scsi_cmd->sdst);
+
+ if (invert1)
+ scsi_cmd->invert |= EXCHANGE_MEDIUM_INV1;
+
+ if (invert2)
+ scsi_cmd->invert |= EXCHANGE_MEDIUM_INV2;
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/ CAM_DIR_NONE,
+ tag_action,
+ /*data_ptr*/ NULL,
+ /*dxfer_len*/ 0,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
+
+void
+scsi_position_to_element(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ u_int8_t tag_action, u_int32_t tea, u_int32_t dst,
+ int invert, u_int8_t sense_len, u_int32_t timeout)
+{
+ struct scsi_position_to_element *scsi_cmd;
+
+ scsi_cmd = (struct scsi_position_to_element *)&csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+
+ scsi_cmd->opcode = POSITION_TO_ELEMENT;
+
+ scsi_ulto2b(tea, scsi_cmd->tea);
+ scsi_ulto2b(dst, scsi_cmd->dst);
+
+ if (invert)
+ scsi_cmd->invert |= POSITION_TO_ELEMENT_INVERT;
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/ CAM_DIR_NONE,
+ tag_action,
+ /*data_ptr*/ NULL,
+ /*dxfer_len*/ 0,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
+
+void
+scsi_read_element_status(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ u_int8_t tag_action, int voltag, u_int32_t sea,
+ u_int32_t count, u_int8_t *data_ptr,
+ u_int32_t dxfer_len, u_int8_t sense_len,
+ u_int32_t timeout)
+{
+ struct scsi_read_element_status *scsi_cmd;
+
+ scsi_cmd = (struct scsi_read_element_status *)&csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+
+ scsi_cmd->opcode = READ_ELEMENT_STATUS;
+
+ scsi_ulto2b(sea, scsi_cmd->sea);
+ scsi_ulto2b(count, scsi_cmd->count);
+ scsi_ulto3b(dxfer_len, scsi_cmd->len);
+
+ if (voltag)
+ scsi_cmd->byte2 |= READ_ELEMENT_STATUS_VOLTAG;
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/ CAM_DIR_IN,
+ tag_action,
+ data_ptr,
+ dxfer_len,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
+
+void
+scsi_initialize_element_status(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ u_int8_t tag_action, u_int8_t sense_len,
+ u_int32_t timeout)
+{
+ struct scsi_initialize_element_status *scsi_cmd;
+
+ scsi_cmd = (struct scsi_initialize_element_status *)
+ &csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+
+ scsi_cmd->opcode = INITIALIZE_ELEMENT_STATUS;
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/ CAM_DIR_NONE,
+ tag_action,
+ /* data_ptr */ NULL,
+ /* dxfer_len */ 0,
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
+
+void
+scsi_send_volume_tag(struct ccb_scsiio *csio, u_int32_t retries,
+ void (*cbfcnp)(struct cam_periph *, union ccb *),
+ u_int8_t tag_action,
+ u_int16_t element_address,
+ u_int8_t send_action_code,
+ struct scsi_send_volume_tag_parameters *parameters,
+ u_int8_t sense_len, u_int32_t timeout)
+{
+ struct scsi_send_volume_tag *scsi_cmd;
+
+ scsi_cmd = (struct scsi_send_volume_tag *) &csio->cdb_io.cdb_bytes;
+ bzero(scsi_cmd, sizeof(*scsi_cmd));
+
+ scsi_cmd->opcode = SEND_VOLUME_TAG;
+ scsi_ulto2b(element_address, scsi_cmd->ea);
+ scsi_cmd->sac = send_action_code;
+ scsi_ulto2b(sizeof(*parameters), scsi_cmd->pll);
+
+ cam_fill_csio(csio,
+ retries,
+ cbfcnp,
+ /*flags*/ CAM_DIR_OUT,
+ tag_action,
+ /* data_ptr */ (u_int8_t *) parameters,
+ sizeof(*parameters),
+ sense_len,
+ sizeof(*scsi_cmd),
+ timeout);
+}
OpenPOWER on IntegriCloud