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