--- /dev/null
+/*
+ * Copyright (c) 1998,1999,2000
+ * Traakan, Inc., Los Altos, CA
+ * 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 unmodified, 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.
+ *
+ * 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.
+ */
+
+/*
+ * Project: NDMJOB
+ * Ident: $Id: $
+ *
+ * Description:
+ *
+ */
+
+
+#include "ndmagents.h"
+
+
+#ifndef NDMOS_OPTION_NO_CONTROL_AGENT
+
+
+int
+ndmca_robot_issue_scsi_req (struct smc_ctrl_block *smc)
+{
+ struct ndmconn * conn = (struct ndmconn *) smc->app_data;
+ struct smc_scsi_req * sr = &smc->scsi_req;
+ int rc;
+
+ rc = ndmscsi_execute (conn, (struct ndmscsi_request *) sr, 0);
+ return rc;
+}
+
+
+int
+ndmca_robot_prep_target (struct ndm_session *sess)
+{
+ struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+ int rc;
+
+ NDMOS_MACRO_ZEROFILL (smc);
+
+ smc->app_data = sess->plumb.robot;
+ smc->issue_scsi_req = ndmca_robot_issue_scsi_req;
+
+ rc = ndmscsi_use (sess->plumb.robot,
+ &sess->control_acb.job.robot_target);
+ if (rc) return rc;
+
+ return 0;
+}
+
+int
+ndmca_robot_obtain_info (struct ndm_session *sess)
+{
+ struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+ int rc;
+
+ rc = smc_inquire (smc);
+ if (rc) return rc;
+
+ rc = smc_get_elem_aa (smc);
+ if (rc) return rc;
+
+ rc = smc_read_elem_status (smc);
+ if (rc) return rc;
+
+ return 0;
+}
+
+int
+ndmca_robot_init_elem_status (struct ndm_session *sess)
+{
+ struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+ int rc;
+
+ ndmalogf (sess, 0, 1,
+ "Commanding robot to initialize element status (take inventory)");
+
+ rc = smc_init_elem_status (smc);
+ if (rc) {
+ ndmalogf (sess, 0, 0, "init-elem-status failed");
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+ndmca_robot_startup (struct ndm_session *sess)
+{
+ int rc;
+
+ if (!sess->control_acb.job.have_robot)
+ return -1; /* Huh? why were we called */
+
+ rc = ndmca_connect_robot_agent(sess);
+ if (rc) return rc;
+
+ rc = ndmca_robot_prep_target(sess);
+ if (rc) return rc;
+
+ return 0;
+}
+
+int
+ndmca_robot_move (struct ndm_session *sess, int src_addr, int dst_addr)
+{
+ struct ndm_control_agent *ca = &sess->control_acb;
+ struct smc_ctrl_block * smc = &ca->smc_cb;
+ int rc;
+ unsigned int t;
+
+ ndmalogf (sess, 0, 2, "robot moving @%d to @%d",
+ src_addr, dst_addr);
+
+ rc = -1;
+ for (t = 0; t <= ca->job.robot_timeout; t += 10) {
+ if (t > 0) {
+ ndmalogf (sess, 0, 2,
+ "Pausing ten seconds before retry (%d/%d)",
+ t, ca->job.robot_timeout);
+ sleep (10);
+ }
+ rc = smc_move (smc, src_addr, dst_addr,
+ 0, smc->elem_aa.mte_addr);
+ if (rc == 0) break;
+ }
+
+ if (rc == 0) {
+ ndmalogf (sess, 0, 2, "robot move OK @%d to @%d",
+ src_addr, dst_addr);
+ } else {
+ ndmalogf (sess, 0, 2, "robot move BAD @%d to @%d",
+ src_addr, dst_addr);
+ }
+
+ return rc;
+}
+
+int
+ndmca_robot_load (struct ndm_session *sess, int slot_addr)
+{
+ struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+ unsigned dte_addr = smc->elem_aa.dte_addr;
+ int rc;
+
+ if (sess->control_acb.job.drive_addr_given)
+ dte_addr = sess->control_acb.job.drive_addr;
+
+ ndmalogf (sess, 0, 1,
+ "Commanding robot to load slot @%d into drive @%d",
+ slot_addr, dte_addr);
+
+ rc = ndmca_robot_move (sess, slot_addr, dte_addr);
+
+ return rc;
+}
+
+int
+ndmca_robot_unload (struct ndm_session *sess, int slot_addr)
+{
+ struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+ int dte_addr = smc->elem_aa.dte_addr;
+ int rc;
+
+ if (sess->control_acb.job.drive_addr_given)
+ dte_addr = sess->control_acb.job.drive_addr;
+
+ /* tricky part -- some (most?) robots need the drive to eject */
+
+ ndmalogf (sess, 0, 1,
+ "Commanding robot to unload drive @%d to slot @%d",
+ dte_addr, slot_addr);
+
+ rc = ndmca_robot_move (sess, dte_addr, slot_addr);
+
+ return rc;
+}
+
+
+struct smc_element_descriptor *
+ndmca_robot_find_element (struct ndm_session *sess, int element_address)
+{
+ struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+ unsigned int i;
+ struct smc_element_descriptor * edp;
+
+ for (i = 0; i < smc->n_elem_desc; i++) {
+ edp = &smc->elem_desc[i];
+ if (edp->element_address == element_address)
+ return edp;
+ }
+
+ return 0;
+}
+
+int
+ndmca_robot_check_ready (struct ndm_session *sess)
+{
+ struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+ unsigned first_dte_addr;
+ unsigned n_dte_addr;
+ int rc;
+ unsigned int i;
+ int errcnt = 0;
+ struct smc_element_descriptor * edp;
+
+ rc = ndmca_robot_obtain_info (sess);
+ if (rc) return rc;
+
+ if (sess->control_acb.job.remedy_all) {
+ first_dte_addr = smc->elem_aa.dte_addr;
+ n_dte_addr = smc->elem_aa.dte_count;
+ } else {
+ n_dte_addr = 1;
+ if (sess->control_acb.job.drive_addr_given) {
+ first_dte_addr = sess->control_acb.job.drive_addr;
+ } else {
+ first_dte_addr = smc->elem_aa.dte_addr;
+ }
+ }
+
+ for (i = 0; i < n_dte_addr; i++) {
+ edp = ndmca_robot_find_element (sess, first_dte_addr+i);
+
+ if (!edp->Full)
+ continue;
+
+ ndmalogf (sess, 0, 1, "tape drive @%d not empty",
+ edp->element_address);
+ errcnt++;
+ }
+
+ return errcnt;
+}
+
+int
+ndmca_robot_remedy_ready (struct ndm_session *sess)
+{
+ struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+ int rc;
+ unsigned int i;
+ int errcnt;
+ struct smc_element_descriptor * edp;
+ struct smc_element_descriptor * edp2;
+ unsigned first_dte_addr;
+ unsigned n_dte_addr;
+ char prefix[60];
+
+ errcnt = 0;
+
+ rc = ndmca_robot_obtain_info (sess);
+ if (rc) return rc;
+
+ if (sess->control_acb.job.remedy_all) {
+ first_dte_addr = smc->elem_aa.dte_addr;
+ n_dte_addr = smc->elem_aa.dte_count;
+ } else {
+ n_dte_addr = 1;
+ if (sess->control_acb.job.drive_addr_given) {
+ first_dte_addr = sess->control_acb.job.drive_addr;
+ } else {
+ first_dte_addr = smc->elem_aa.dte_addr;
+ }
+ }
+
+ for (i = 0; i < n_dte_addr; i++) {
+ edp = ndmca_robot_find_element (sess, first_dte_addr+i);
+
+ if (!edp->Full)
+ continue;
+
+ sprintf (prefix, "drive @%d not empty", edp->element_address);
+
+ if (!edp->SValid) {
+ ndmalogf (sess, 0, 1, "%s, invalid source", prefix);
+ errcnt++;
+ continue;
+ }
+
+ sprintf (NDMOS_API_STREND(prefix), ", src @%d",
+ edp->src_se_addr);
+
+ edp2 = ndmca_robot_find_element (sess, edp->src_se_addr);
+
+ if (edp2->element_type_code != SMC_ELEM_TYPE_SE) {
+ ndmalogf (sess, 0, 1, "%s, not slot", prefix);
+ errcnt++;
+ continue;
+ }
+
+ if (edp2->Full) {
+ ndmalogf (sess, 0, 1, "%s, but slot Full", prefix);
+ errcnt++;
+ continue;
+ }
+
+ rc = ndmca_robot_move (sess,
+ edp->element_address, edp->src_se_addr);
+ if (rc) {
+ ndmalogf (sess, 0, 1, "%s, move failed", prefix);
+ errcnt++;
+ continue;
+ }
+ }
+
+ return errcnt;
+}
+
+
+
+/*
+ * ndmca_robot_query() incrementally obtains info so that we
+ * can print progress.
+ */
+
+int
+ndmca_robot_query (struct ndm_session *sess)
+{
+ struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+ int rc;
+ unsigned int i;
+ char buf[100];
+ char lnbuf[30];
+ int lineno, nline = 1;
+
+ ndmalogqr (sess, " Type");
+
+ rc = smc_inquire (smc);
+ if (rc) {
+ ndmalogqr (sess, " ERROR smc_inquire(): %s", smc->errmsg);
+ } else {
+ ndmalogqr (sess, " '%s'", smc->ident);
+ }
+
+
+ ndmalogqr (sess, " Elements");
+ rc = smc_get_elem_aa (smc);
+ if (rc) {
+ ndmalogqr (sess, " ERROR smc_get_elem_aa(): %s", smc->errmsg);
+ } else {
+ strcpy (lnbuf, " ");
+ for (lineno = 0, nline = 1; lineno < nline; lineno++) {
+ rc = smc_pp_element_address_assignments (&smc->elem_aa,
+ lineno, buf);
+ if (rc < 0) {
+ strcpy (buf, "PP-ERROR");
+ }
+ nline = rc;
+ ndmalogqr (sess, "%s %s", lnbuf, buf);
+ }
+ }
+
+ ndmalogqr (sess, " Status");
+ rc = smc_read_elem_status (smc);
+ if (rc) {
+ ndmalogqr (sess, " ERROR smc_read_elem_status(): %s", smc->errmsg);
+ } else {
+ ndmalogqr (sess, " E# Addr Type Status");
+ ndmalogqr (sess, " -- ---- ---- ---------------------");
+ for (i = 0; i < smc->n_elem_desc; i++) {
+ struct smc_element_descriptor * edp;
+
+ edp = &smc->elem_desc[i];
+
+ for (lineno = 0, nline = 1; lineno < nline; lineno++) {
+ rc = smc_pp_element_descriptor (edp,
+ lineno, buf);
+
+ if (lineno == 0)
+ sprintf (lnbuf, " %2d ", i+1);
+ else
+ sprintf (lnbuf, " ");
+
+ if (rc < 0) {
+ strcpy (buf, "PP-ERROR");
+ }
+ nline = rc;
+ ndmalogqr (sess, "%s %s", lnbuf, buf);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+int
+ndmca_robot_verify_media (struct ndm_session *sess)
+{
+ struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+ struct ndm_media_table *mtab = &sess->control_acb.job.media_tab;
+ int rc;
+ struct ndmmedia * me;
+ struct smc_element_descriptor *edp;
+ int i;
+ unsigned int j;
+ int errcnt = 0;
+
+ rc = ndmca_robot_obtain_info (sess);
+ if (rc) return rc;
+
+ for (i = 0; i < mtab->n_media; i++) {
+ me = &mtab->media[i];
+
+ if (! me->valid_slot) {
+ me->slot_missing = 1;
+ errcnt++;
+ continue; /* what now */
+ }
+
+ for (j = 0; j < smc->n_elem_desc; j++) {
+ edp = &smc->elem_desc[j];
+
+ if (edp->element_type_code != SMC_ELEM_TYPE_SE)
+ continue;
+
+ if (edp->element_address != me->slot_addr)
+ continue;
+
+ if (!edp->Full) {
+ me->slot_empty = 1;
+ errcnt++;
+ } else {
+ me->slot_empty = 0;
+ }
+ break;
+ }
+ if (j >= smc->n_elem_desc) {
+ me->slot_bad = 1;
+ errcnt++;
+ }
+ }
+
+ return errcnt;
+}
+
+/*
+ * For NDM_JOB_OP_LIST_LABELS, fill in media_tab based on non-empty slots.
+ * Note: this might REALLY nerf on a cleaning cartridge.
+ */
+
+int
+ndmca_robot_synthesize_media (struct ndm_session *sess)
+{
+ struct smc_ctrl_block * smc = &sess->control_acb.smc_cb;
+ struct ndm_media_table *mtab = &sess->control_acb.job.media_tab;
+ int rc;
+ struct ndmmedia * me;
+ struct smc_element_descriptor *edp;
+ unsigned int i;
+
+ rc = ndmca_robot_obtain_info (sess);
+ if (rc) return rc;
+
+ for (i = 0; i < smc->n_elem_desc; i++) {
+ edp = &smc->elem_desc[i];
+
+ if (edp->element_type_code != SMC_ELEM_TYPE_SE)
+ continue;
+
+ if (!edp->Full)
+ continue;
+
+ me = &mtab->media[mtab->n_media++];
+ NDMOS_MACRO_ZEROFILL (me);
+
+ me->valid_slot = 1;
+ me->slot_addr = edp->element_address;
+ }
+
+ return 0;
+}
+#endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */