1 /*
2  * Copyright (c) 1998,1999,2000
3  *      Traakan, Inc., Los Altos, CA
4  *      All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*
30  * Project:  NDMJOB
31  * Ident:    $Id: $
32  *
33  * Description:
34  *
35  */
36 
37 
38 #include "ndmagents.h"
39 
40 
41 #ifndef NDMOS_OPTION_NO_CONTROL_AGENT
42 
43 
ndmca_robot_issue_scsi_req(struct smc_ctrl_block * smc)44 int ndmca_robot_issue_scsi_req(struct smc_ctrl_block* smc)
45 {
46   struct ndmconn* conn = (struct ndmconn*)smc->app_data;
47   struct smc_scsi_req* sr = &smc->scsi_req;
48   int rc;
49 
50   rc = ndmscsi_execute(conn, (struct ndmscsi_request*)sr, 0);
51   return rc;
52 }
53 
54 
ndmca_robot_prep_target(struct ndm_session * sess)55 int ndmca_robot_prep_target(struct ndm_session* sess)
56 {
57   struct smc_ctrl_block* smc = sess->control_acb->smc_cb;
58   int rc;
59 
60   if (!smc) {
61     ndmalogf(sess, 0, 0, "Allocating robot target");
62     return -1;
63   }
64 
65   NDMOS_MACRO_ZEROFILL(smc);
66 
67   smc->app_data = sess->plumb.robot;
68   smc->issue_scsi_req = ndmca_robot_issue_scsi_req;
69 
70   /*
71    * We are about to start using a Robot Target so allocate it.
72    * Only do this when not allocated yet.
73    */
74   if (!sess->control_acb->job.robot_target) {
75     sess->control_acb->job.robot_target =
76         NDMOS_API_MALLOC(sizeof(struct ndmscsi_target));
77     if (!sess->control_acb->job.robot_target) {
78       ndmalogf(sess, 0, 0, "Failed allocating robot target");
79       return -1;
80     }
81     NDMOS_MACRO_ZEROFILL(sess->control_acb->job.robot_target);
82   }
83 
84   rc = ndmscsi_use(sess->plumb.robot, sess->control_acb->job.robot_target);
85   if (rc) return rc;
86 
87   return 0;
88 }
89 
ndmca_robot_obtain_info(struct ndm_session * sess)90 int ndmca_robot_obtain_info(struct ndm_session* sess)
91 {
92   struct smc_ctrl_block* smc = sess->control_acb->smc_cb;
93   int rc;
94 
95   rc = smc_inquire(smc);
96   if (rc) return rc;
97 
98   rc = smc_get_elem_aa(smc);
99   if (rc) return rc;
100 
101   rc = smc_read_elem_status(smc);
102   if (rc) return rc;
103 
104   return 0;
105 }
106 
ndmca_robot_init_elem_status(struct ndm_session * sess)107 int ndmca_robot_init_elem_status(struct ndm_session* sess)
108 {
109   struct smc_ctrl_block* smc = sess->control_acb->smc_cb;
110   int rc;
111 
112   ndmalogf(sess, 0, 1,
113            "Commanding robot to initialize element status (take inventory)");
114 
115   rc = smc_init_elem_status(smc);
116   if (rc) {
117     ndmalogf(sess, 0, 0, "init-elem-status failed");
118     return rc;
119   }
120 
121   return 0;
122 }
123 
ndmca_robot_startup(struct ndm_session * sess)124 int ndmca_robot_startup(struct ndm_session* sess)
125 {
126   int rc;
127 
128   if (!sess->control_acb->job.have_robot)
129     return -1; /* Huh? why were we called */
130 
131   if (!sess->control_acb->smc_cb) {
132     sess->control_acb->smc_cb = NDMOS_API_MALLOC(sizeof(struct smc_ctrl_block));
133     NDMOS_MACRO_ZEROFILL(sess->control_acb->smc_cb);
134   }
135 
136   rc = ndmca_connect_robot_agent(sess);
137   if (rc) return rc;
138 
139   rc = ndmca_robot_prep_target(sess);
140   if (rc) return rc;
141 
142   return 0;
143 }
144 
ndmca_robot_move(struct ndm_session * sess,int src_addr,int dst_addr)145 int ndmca_robot_move(struct ndm_session* sess, int src_addr, int dst_addr)
146 {
147   struct ndm_control_agent* ca = sess->control_acb;
148   struct smc_ctrl_block* smc = ca->smc_cb;
149   int rc;
150   unsigned int t;
151 
152   ndmalogf(sess, 0, 2, "robot moving @%d to @%d", src_addr, dst_addr);
153 
154   rc = -1;
155   for (t = 0; t <= ca->job.robot_timeout; t += 10) {
156     if (t > 0) {
157       ndmalogf(sess, 0, 2, "Pausing ten seconds before retry (%d/%d)", t,
158                ca->job.robot_timeout);
159       sleep(10);
160     }
161     rc = smc_move(smc, src_addr, dst_addr, 0, smc->elem_aa.mte_addr);
162     if (rc == 0) break;
163   }
164 
165   if (rc == 0) {
166     ndmalogf(sess, 0, 2, "robot move OK @%d to @%d", src_addr, dst_addr);
167   } else {
168     ndmalogf(sess, 0, 2, "robot move BAD @%d to @%d", src_addr, dst_addr);
169   }
170 
171   return rc;
172 }
173 
ndmca_robot_load(struct ndm_session * sess,int slot_addr)174 int ndmca_robot_load(struct ndm_session* sess, int slot_addr)
175 {
176   struct smc_ctrl_block* smc = sess->control_acb->smc_cb;
177   unsigned dte_addr = smc->elem_aa.dte_addr;
178   int rc;
179 
180   if (sess->control_acb->job.drive_addr_given)
181     dte_addr = sess->control_acb->job.drive_addr;
182 
183   ndmalogf(sess, 0, 1, "Commanding robot to load slot @%d into drive @%d",
184            slot_addr, dte_addr);
185 
186   rc = ndmca_robot_move(sess, slot_addr, dte_addr);
187 
188   return rc;
189 }
190 
ndmca_robot_unload(struct ndm_session * sess,int slot_addr)191 int ndmca_robot_unload(struct ndm_session* sess, int slot_addr)
192 {
193   struct smc_ctrl_block* smc = sess->control_acb->smc_cb;
194   int dte_addr = smc->elem_aa.dte_addr;
195   int rc;
196 
197   if (sess->control_acb->job.drive_addr_given)
198     dte_addr = sess->control_acb->job.drive_addr;
199 
200   /* tricky part -- some (most?) robots need the drive to eject */
201 
202   ndmalogf(sess, 0, 1, "Commanding robot to unload drive @%d to slot @%d",
203            dte_addr, slot_addr);
204 
205   rc = ndmca_robot_move(sess, dte_addr, slot_addr);
206 
207   return rc;
208 }
209 
210 
ndmca_robot_find_element(struct ndm_session * sess,int element_address)211 struct smc_element_descriptor* ndmca_robot_find_element(
212     struct ndm_session* sess,
213     int element_address)
214 {
215   struct smc_ctrl_block* smc = sess->control_acb->smc_cb;
216   struct smc_element_descriptor* edp;
217 
218   for (edp = smc->elem_desc; edp; edp = edp->next) {
219     if (edp->element_address == element_address) return edp;
220   }
221 
222   return 0;
223 }
224 
ndmca_robot_check_ready(struct ndm_session * sess)225 int ndmca_robot_check_ready(struct ndm_session* sess)
226 {
227   struct smc_ctrl_block* smc = sess->control_acb->smc_cb;
228   unsigned first_dte_addr;
229   unsigned n_dte_addr;
230   int rc;
231   unsigned int i;
232   int errcnt = 0;
233   struct smc_element_descriptor* edp;
234 
235   rc = ndmca_robot_obtain_info(sess);
236   if (rc) return rc;
237 
238   if (sess->control_acb->job.remedy_all) {
239     first_dte_addr = smc->elem_aa.dte_addr;
240     n_dte_addr = smc->elem_aa.dte_count;
241   } else {
242     n_dte_addr = 1;
243     if (sess->control_acb->job.drive_addr_given) {
244       first_dte_addr = sess->control_acb->job.drive_addr;
245     } else {
246       first_dte_addr = smc->elem_aa.dte_addr;
247     }
248   }
249 
250   for (i = 0; i < n_dte_addr; i++) {
251     edp = ndmca_robot_find_element(sess, first_dte_addr + i);
252 
253     if (!edp->Full) continue;
254 
255     ndmalogf(sess, 0, 1, "tape drive @%d not empty", edp->element_address);
256     errcnt++;
257   }
258 
259   return errcnt;
260 }
261 
ndmca_robot_remedy_ready(struct ndm_session * sess)262 int ndmca_robot_remedy_ready(struct ndm_session* sess)
263 {
264   struct smc_ctrl_block* smc = sess->control_acb->smc_cb;
265   int rc;
266   unsigned int i;
267   int errcnt;
268   struct smc_element_descriptor* edp;
269   struct smc_element_descriptor* edp2;
270   unsigned first_dte_addr;
271   unsigned n_dte_addr;
272   char prefix[60];
273 
274   errcnt = 0;
275 
276   rc = ndmca_robot_obtain_info(sess);
277   if (rc) return rc;
278 
279   if (sess->control_acb->job.remedy_all) {
280     first_dte_addr = smc->elem_aa.dte_addr;
281     n_dte_addr = smc->elem_aa.dte_count;
282   } else {
283     n_dte_addr = 1;
284     if (sess->control_acb->job.drive_addr_given) {
285       first_dte_addr = sess->control_acb->job.drive_addr;
286     } else {
287       first_dte_addr = smc->elem_aa.dte_addr;
288     }
289   }
290 
291   for (i = 0; i < n_dte_addr; i++) {
292     edp = ndmca_robot_find_element(sess, first_dte_addr + i);
293 
294     if (!edp->Full) continue;
295 
296     snprintf(prefix, sizeof(prefix), "drive @%d not empty",
297              edp->element_address);
298 
299     if (!edp->SValid) {
300       ndmalogf(sess, 0, 1, "%s, invalid source", prefix);
301       errcnt++;
302       continue;
303     }
304 
305     sprintf(NDMOS_API_STREND(prefix), ", src @%d", edp->src_se_addr);
306 
307     edp2 = ndmca_robot_find_element(sess, edp->src_se_addr);
308 
309     if (edp2->element_type_code != SMC_ELEM_TYPE_SE) {
310       ndmalogf(sess, 0, 1, "%s, not slot", prefix);
311       errcnt++;
312       continue;
313     }
314 
315     if (edp2->Full) {
316       ndmalogf(sess, 0, 1, "%s, but slot Full", prefix);
317       errcnt++;
318       continue;
319     }
320 
321     rc = ndmca_robot_move(sess, edp->element_address, edp->src_se_addr);
322     if (rc) {
323       ndmalogf(sess, 0, 1, "%s, move failed", prefix);
324       errcnt++;
325       continue;
326     }
327   }
328 
329   return errcnt;
330 }
331 
332 
333 /*
334  * ndmca_robot_query() incrementally obtains info so that we
335  * can print progress.
336  */
337 
ndmca_robot_query(struct ndm_session * sess)338 int ndmca_robot_query(struct ndm_session* sess)
339 {
340   struct smc_ctrl_block* smc = sess->control_acb->smc_cb;
341   int rc;
342   unsigned int i = 0;
343   char buf[111];
344   char lnbuf[30];
345   int lineno, nline = 1;
346   struct smc_element_descriptor* edp;
347 
348   ndmalogqr(sess, "  Type");
349 
350   rc = smc_inquire(smc);
351   if (rc) {
352     ndmalogqr(sess, "    ERROR smc_inquire(): %s", smc->errmsg);
353   } else {
354     ndmalogqr(sess, "    '%s'", smc->ident);
355   }
356 
357 
358   ndmalogqr(sess, "  Elements");
359   rc = smc_get_elem_aa(smc);
360   if (rc) {
361     ndmalogqr(sess, "    ERROR smc_get_elem_aa(): %s", smc->errmsg);
362   } else {
363     strcpy(lnbuf, "    ");
364     for (lineno = 0, nline = 1; lineno < nline; lineno++) {
365       rc = smc_pp_element_address_assignments(&smc->elem_aa, lineno, buf);
366       if (rc < 0) { strcpy(buf, "PP-ERROR"); }
367       nline = rc;
368       ndmalogqr(sess, "%s %s", lnbuf, buf);
369     }
370   }
371 
372   ndmalogqr(sess, "  Status");
373   rc = smc_read_elem_status(smc);
374   if (rc) {
375     ndmalogqr(sess, "    ERROR smc_read_elem_status(): %s", smc->errmsg);
376   } else {
377     ndmalogqr(sess, "    E#  Addr Type Status");
378     ndmalogqr(sess, "    --  ---- ---- ---------------------");
379     for (edp = smc->elem_desc; edp; edp = edp->next) {
380       for (lineno = 0, nline = 1; lineno < nline; lineno++) {
381         rc = smc_pp_element_descriptor(edp, lineno, buf);
382 
383         if (lineno == 0)
384           snprintf(lnbuf, sizeof(lnbuf), "    %2d ", i + 1);
385         else
386           snprintf(lnbuf, sizeof(lnbuf), "       ");
387 
388         if (rc < 0) { strcpy(buf, "PP-ERROR"); }
389         nline = rc;
390         ndmalogqr(sess, "%s %s", lnbuf, buf);
391       }
392       i++;
393     }
394   }
395 
396   return 0;
397 }
398 
399 
ndmca_robot_verify_media(struct ndm_session * sess)400 int ndmca_robot_verify_media(struct ndm_session* sess)
401 {
402   struct smc_ctrl_block* smc = sess->control_acb->smc_cb;
403   struct ndm_media_table* mtab = &sess->control_acb->job.media_tab;
404   int rc;
405   struct ndmmedia* me;
406   struct smc_element_descriptor* edp;
407   int errcnt = 0;
408 
409   rc = ndmca_robot_obtain_info(sess);
410   if (rc) return rc;
411 
412   for (me = mtab->head; me; me = me->next) {
413     if (!me->valid_slot) {
414       me->slot_missing = 1;
415       errcnt++;
416       continue; /* what now */
417     }
418 
419     for (edp = smc->elem_desc; edp; edp = edp->next) {
420       if (edp->element_type_code != SMC_ELEM_TYPE_SE) continue;
421 
422       if (edp->element_address != me->slot_addr) continue;
423 
424       if (!edp->Full) {
425         me->slot_empty = 1;
426         errcnt++;
427       } else {
428         me->slot_empty = 0;
429       }
430       break;
431     }
432 
433     if (!edp) {
434       me->slot_bad = 1;
435       errcnt++;
436     }
437   }
438 
439   return errcnt;
440 }
441 
442 /*
443  * For NDM_JOB_OP_LIST_LABELS, fill in media_tab based on non-empty slots.
444  * Note: this might REALLY nerf on a cleaning cartridge.
445  */
446 
ndmca_robot_synthesize_media(struct ndm_session * sess)447 int ndmca_robot_synthesize_media(struct ndm_session* sess)
448 {
449   struct smc_ctrl_block* smc = sess->control_acb->smc_cb;
450   struct ndm_media_table* mtab = &sess->control_acb->job.media_tab;
451   int rc;
452   struct smc_element_descriptor* edp;
453 
454   rc = ndmca_robot_obtain_info(sess);
455   if (rc) return rc;
456 
457   for (edp = smc->elem_desc; edp; edp = edp->next) {
458     if (edp->element_type_code != SMC_ELEM_TYPE_SE) continue;
459 
460     if (!edp->Full) continue;
461 
462     ndma_store_media(mtab, edp->element_address);
463   }
464 
465   return 0;
466 }
467 #endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
468