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