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