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
44 int
ndmca_robot_issue_scsi_req(struct smc_ctrl_block * smc)45 ndmca_robot_issue_scsi_req (struct smc_ctrl_block *smc)
46 {
47 struct ndmconn * conn = (struct ndmconn *) smc->app_data;
48 struct smc_scsi_req * sr = &smc->scsi_req;
49 int rc;
50
51 rc = ndmscsi_execute (conn, (struct ndmscsi_request *) sr, 0);
52 return rc;
53 }
54
55
56 int
ndmca_robot_prep_target(struct ndm_session * sess)57 ndmca_robot_prep_target (struct ndm_session *sess)
58 {
59 struct smc_ctrl_block * smc = sess->control_acb->smc_cb;
60 int rc;
61
62 if (!smc) {
63 ndmalogf (sess, 0, 0,
64 "Allocating robot target");
65 return -1;
66 }
67
68 NDMOS_MACRO_ZEROFILL (smc);
69
70 smc->app_data = sess->plumb.robot;
71 smc->issue_scsi_req = ndmca_robot_issue_scsi_req;
72
73 /*
74 * We are about to start using a Robot Target so allocate it.
75 * Only do this when not allocated yet.
76 */
77 if (!sess->control_acb->job.robot_target) {
78 sess->control_acb->job.robot_target = NDMOS_API_MALLOC (sizeof(struct ndmscsi_target));
79 if (!sess->control_acb->job.robot_target) {
80 ndmalogf (sess, 0, 0,
81 "Failed allocating robot target");
82 return -1;
83 }
84 NDMOS_MACRO_ZEROFILL (sess->control_acb->job.robot_target);
85 }
86
87 rc = ndmscsi_use (sess->plumb.robot,
88 sess->control_acb->job.robot_target);
89 if (rc) return rc;
90
91 return 0;
92 }
93
94 int
ndmca_robot_obtain_info(struct ndm_session * sess)95 ndmca_robot_obtain_info (struct ndm_session *sess)
96 {
97 struct smc_ctrl_block * smc = sess->control_acb->smc_cb;
98 int rc;
99
100 rc = smc_inquire (smc);
101 if (rc) return rc;
102
103 rc = smc_get_elem_aa (smc);
104 if (rc) return rc;
105
106 rc = smc_read_elem_status (smc);
107 if (rc) return rc;
108
109 return 0;
110 }
111
112 int
ndmca_robot_init_elem_status(struct ndm_session * sess)113 ndmca_robot_init_elem_status (struct ndm_session *sess)
114 {
115 struct smc_ctrl_block * smc = sess->control_acb->smc_cb;
116 int rc;
117
118 ndmalogf (sess, 0, 1,
119 "Commanding robot to initialize element status (take inventory)");
120
121 rc = smc_init_elem_status (smc);
122 if (rc) {
123 ndmalogf (sess, 0, 0, "init-elem-status failed");
124 return rc;
125 }
126
127 return 0;
128 }
129
130 int
ndmca_robot_startup(struct ndm_session * sess)131 ndmca_robot_startup (struct ndm_session *sess)
132 {
133 int rc;
134
135 if (!sess->control_acb->job.have_robot)
136 return -1; /* Huh? why were we called */
137
138 if (!sess->control_acb->smc_cb) {
139 sess->control_acb->smc_cb = NDMOS_API_MALLOC (sizeof(struct smc_ctrl_block));
140 NDMOS_MACRO_ZEROFILL (sess->control_acb->smc_cb);
141 }
142
143 rc = ndmca_connect_robot_agent(sess);
144 if (rc) return rc;
145
146 rc = ndmca_robot_prep_target(sess);
147 if (rc) return rc;
148
149 return 0;
150 }
151
152 int
ndmca_robot_move(struct ndm_session * sess,int src_addr,int dst_addr)153 ndmca_robot_move (struct ndm_session *sess, int src_addr, int dst_addr)
154 {
155 struct ndm_control_agent *ca = sess->control_acb;
156 struct smc_ctrl_block * smc = ca->smc_cb;
157 int rc;
158 unsigned int t;
159
160 ndmalogf (sess, 0, 2, "robot moving @%d to @%d",
161 src_addr, dst_addr);
162
163 rc = -1;
164 for (t = 0; t <= ca->job.robot_timeout; t += 10) {
165 if (t > 0) {
166 ndmalogf (sess, 0, 2,
167 "Pausing ten seconds before retry (%d/%d)",
168 t, ca->job.robot_timeout);
169 sleep (10);
170 }
171 rc = smc_move (smc, src_addr, dst_addr,
172 0, smc->elem_aa.mte_addr);
173 if (rc == 0) break;
174 }
175
176 if (rc == 0) {
177 ndmalogf (sess, 0, 2, "robot move OK @%d to @%d",
178 src_addr, dst_addr);
179 } else {
180 ndmalogf (sess, 0, 2, "robot move BAD @%d to @%d",
181 src_addr, dst_addr);
182 }
183
184 return rc;
185 }
186
187 int
ndmca_robot_load(struct ndm_session * sess,int slot_addr)188 ndmca_robot_load (struct ndm_session *sess, int slot_addr)
189 {
190 struct smc_ctrl_block * smc = sess->control_acb->smc_cb;
191 unsigned dte_addr = smc->elem_aa.dte_addr;
192 int rc;
193
194 if (sess->control_acb->job.drive_addr_given)
195 dte_addr = sess->control_acb->job.drive_addr;
196
197 ndmalogf (sess, 0, 1,
198 "Commanding robot to load slot @%d into drive @%d",
199 slot_addr, dte_addr);
200
201 rc = ndmca_robot_move (sess, slot_addr, dte_addr);
202
203 return rc;
204 }
205
206 int
ndmca_robot_unload(struct ndm_session * sess,int slot_addr)207 ndmca_robot_unload (struct ndm_session *sess, int slot_addr)
208 {
209 struct smc_ctrl_block * smc = sess->control_acb->smc_cb;
210 int dte_addr = smc->elem_aa.dte_addr;
211 int rc;
212
213 if (sess->control_acb->job.drive_addr_given)
214 dte_addr = sess->control_acb->job.drive_addr;
215
216 /* tricky part -- some (most?) robots need the drive to eject */
217
218 ndmalogf (sess, 0, 1,
219 "Commanding robot to unload drive @%d to slot @%d",
220 dte_addr, slot_addr);
221
222 rc = ndmca_robot_move (sess, dte_addr, slot_addr);
223
224 return rc;
225 }
226
227
228 struct smc_element_descriptor *
ndmca_robot_find_element(struct ndm_session * sess,int element_address)229 ndmca_robot_find_element (struct ndm_session *sess, int element_address)
230 {
231 struct smc_ctrl_block * smc = sess->control_acb->smc_cb;
232 struct smc_element_descriptor * edp;
233
234 for (edp = smc->elem_desc; edp; edp = edp->next) {
235 if (edp->element_address == element_address)
236 return edp;
237 }
238
239 return 0;
240 }
241
242 int
ndmca_robot_check_ready(struct ndm_session * sess)243 ndmca_robot_check_ready (struct ndm_session *sess)
244 {
245 struct smc_ctrl_block * smc = sess->control_acb->smc_cb;
246 unsigned first_dte_addr;
247 unsigned n_dte_addr;
248 int rc;
249 unsigned int i;
250 int errcnt = 0;
251 struct smc_element_descriptor * edp;
252
253 rc = ndmca_robot_obtain_info (sess);
254 if (rc) return rc;
255
256 if (sess->control_acb->job.remedy_all) {
257 first_dte_addr = smc->elem_aa.dte_addr;
258 n_dte_addr = smc->elem_aa.dte_count;
259 } else {
260 n_dte_addr = 1;
261 if (sess->control_acb->job.drive_addr_given) {
262 first_dte_addr = sess->control_acb->job.drive_addr;
263 } else {
264 first_dte_addr = smc->elem_aa.dte_addr;
265 }
266 }
267
268 for (i = 0; i < n_dte_addr; i++) {
269 edp = ndmca_robot_find_element (sess, first_dte_addr+i);
270
271 if (!edp->Full)
272 continue;
273
274 ndmalogf (sess, 0, 1, "tape drive @%d not empty",
275 edp->element_address);
276 errcnt++;
277 }
278
279 return errcnt;
280 }
281
282 int
ndmca_robot_remedy_ready(struct ndm_session * sess)283 ndmca_robot_remedy_ready (struct ndm_session *sess)
284 {
285 struct smc_ctrl_block * smc = sess->control_acb->smc_cb;
286 int rc;
287 unsigned int i;
288 int errcnt;
289 struct smc_element_descriptor * edp;
290 struct smc_element_descriptor * edp2;
291 unsigned first_dte_addr;
292 unsigned n_dte_addr;
293 char prefix[60];
294
295 errcnt = 0;
296
297 rc = ndmca_robot_obtain_info (sess);
298 if (rc) return rc;
299
300 if (sess->control_acb->job.remedy_all) {
301 first_dte_addr = smc->elem_aa.dte_addr;
302 n_dte_addr = smc->elem_aa.dte_count;
303 } else {
304 n_dte_addr = 1;
305 if (sess->control_acb->job.drive_addr_given) {
306 first_dte_addr = sess->control_acb->job.drive_addr;
307 } else {
308 first_dte_addr = smc->elem_aa.dte_addr;
309 }
310 }
311
312 for (i = 0; i < n_dte_addr; i++) {
313 edp = ndmca_robot_find_element (sess, first_dte_addr+i);
314
315 if (!edp->Full)
316 continue;
317
318 snprintf (prefix, sizeof(prefix), "drive @%d not empty", edp->element_address);
319
320 if (!edp->SValid) {
321 ndmalogf (sess, 0, 1, "%s, invalid source", prefix);
322 errcnt++;
323 continue;
324 }
325
326 sprintf (NDMOS_API_STREND(prefix), ", src @%d",
327 edp->src_se_addr);
328
329 edp2 = ndmca_robot_find_element (sess, edp->src_se_addr);
330
331 if (edp2->element_type_code != SMC_ELEM_TYPE_SE) {
332 ndmalogf (sess, 0, 1, "%s, not slot", prefix);
333 errcnt++;
334 continue;
335 }
336
337 if (edp2->Full) {
338 ndmalogf (sess, 0, 1, "%s, but slot Full", prefix);
339 errcnt++;
340 continue;
341 }
342
343 rc = ndmca_robot_move (sess,
344 edp->element_address, edp->src_se_addr);
345 if (rc) {
346 ndmalogf (sess, 0, 1, "%s, move failed", prefix);
347 errcnt++;
348 continue;
349 }
350 }
351
352 return errcnt;
353 }
354
355
356
357 /*
358 * ndmca_robot_query() incrementally obtains info so that we
359 * can print progress.
360 */
361
362 int
ndmca_robot_query(struct ndm_session * sess)363 ndmca_robot_query (struct ndm_session *sess)
364 {
365 struct smc_ctrl_block * smc = sess->control_acb->smc_cb;
366 int rc;
367 unsigned int i = 0;
368 char buf[111];
369 char lnbuf[30];
370 int lineno, nline = 1;
371 struct smc_element_descriptor * edp;
372
373 ndmalogqr (sess, " Type");
374
375 rc = smc_inquire (smc);
376 if (rc) {
377 ndmalogqr (sess, " ERROR smc_inquire(): %s", smc->errmsg);
378 } else {
379 ndmalogqr (sess, " '%s'", smc->ident);
380 }
381
382
383 ndmalogqr (sess, " Elements");
384 rc = smc_get_elem_aa (smc);
385 if (rc) {
386 ndmalogqr (sess, " ERROR smc_get_elem_aa(): %s", smc->errmsg);
387 } else {
388 strcpy (lnbuf, " ");
389 for (lineno = 0, nline = 1; lineno < nline; lineno++) {
390 rc = smc_pp_element_address_assignments (&smc->elem_aa,
391 lineno, buf);
392 if (rc < 0) {
393 strcpy (buf, "PP-ERROR");
394 }
395 nline = rc;
396 ndmalogqr (sess, "%s %s", lnbuf, buf);
397 }
398 }
399
400 ndmalogqr (sess, " Status");
401 rc = smc_read_elem_status (smc);
402 if (rc) {
403 ndmalogqr (sess, " ERROR smc_read_elem_status(): %s", smc->errmsg);
404 } else {
405 ndmalogqr (sess, " E# Addr Type Status");
406 ndmalogqr (sess, " -- ---- ---- ---------------------");
407 for (edp = smc->elem_desc; edp; edp = edp->next) {
408 for (lineno = 0, nline = 1; lineno < nline; lineno++) {
409 rc = smc_pp_element_descriptor (edp,
410 lineno, buf);
411
412 if (lineno == 0)
413 snprintf (lnbuf, sizeof(lnbuf), " %2d ", i+1);
414 else
415 snprintf (lnbuf, sizeof(lnbuf), " ");
416
417 if (rc < 0) {
418 strcpy (buf, "PP-ERROR");
419 }
420 nline = rc;
421 ndmalogqr (sess, "%s %s", lnbuf, buf);
422 }
423 i++;
424 }
425 }
426
427 return 0;
428 }
429
430
431 int
ndmca_robot_verify_media(struct ndm_session * sess)432 ndmca_robot_verify_media (struct ndm_session *sess)
433 {
434 struct smc_ctrl_block * smc = sess->control_acb->smc_cb;
435 struct ndm_media_table *mtab = &sess->control_acb->job.media_tab;
436 int rc;
437 struct ndmmedia * me;
438 struct smc_element_descriptor *edp;
439 int errcnt = 0;
440
441 rc = ndmca_robot_obtain_info (sess);
442 if (rc) return rc;
443
444 for (me = mtab->head; me; me = me->next) {
445 if (! me->valid_slot) {
446 me->slot_missing = 1;
447 errcnt++;
448 continue; /* what now */
449 }
450
451 for (edp = smc->elem_desc; edp; edp = edp->next) {
452 if (edp->element_type_code != SMC_ELEM_TYPE_SE)
453 continue;
454
455 if (edp->element_address != me->slot_addr)
456 continue;
457
458 if (!edp->Full) {
459 me->slot_empty = 1;
460 errcnt++;
461 } else {
462 me->slot_empty = 0;
463 }
464 break;
465 }
466
467 if (!edp) {
468 me->slot_bad = 1;
469 errcnt++;
470 }
471 }
472
473 return errcnt;
474 }
475
476 /*
477 * For NDM_JOB_OP_LIST_LABELS, fill in media_tab based on non-empty slots.
478 * Note: this might REALLY nerf on a cleaning cartridge.
479 */
480
481 int
ndmca_robot_synthesize_media(struct ndm_session * sess)482 ndmca_robot_synthesize_media (struct ndm_session *sess)
483 {
484 struct smc_ctrl_block * smc = sess->control_acb->smc_cb;
485 struct ndm_media_table *mtab = &sess->control_acb->job.media_tab;
486 int rc;
487 struct smc_element_descriptor *edp;
488
489 rc = ndmca_robot_obtain_info (sess);
490 if (rc) return rc;
491
492 for (edp = smc->elem_desc; edp; edp = edp->next) {
493 if (edp->element_type_code != SMC_ELEM_TYPE_SE)
494 continue;
495
496 if (!edp->Full)
497 continue;
498
499 ndma_store_media (mtab, edp->element_address);
500 }
501
502 return 0;
503 }
504 #endif /* !NDMOS_OPTION_NO_CONTROL_AGENT */
505