xref: /plan9/sys/src/cmd/cwfs/juke.c (revision 390ad7e13cfcfef6cf0377dab79d2a0ed3c8f713)
1 /*
2  * drive HP optical-disc jukeboxes (e.g. HP 1200EX).
3  * used to issue SCSI commands directly to the host adapter;
4  * now (via scsi.c) it issues them via scsi(2) using
5  * /dev/sdXX/raw to run the robotics, and uses normal i/o
6  * on /dev/sdXX/data to run the drives.
7  */
8 #include "all.h"
9 #include "io.h"
10 
11 enum {
12 	SCSInone	= SCSIread,
13 	MAXDRIVE	= 10,
14 	MAXSIDE		= 500,		/* max. disc sides */
15 
16 	TWORM		= MINUTE(10),
17 	THYSTER		= SECOND(10),
18 
19 	Sectorsz	= 512,		/* usual disk sector size */
20 
21 	Jukemagic	= 0xbabfece2,
22 };
23 
24 typedef	struct	Side	Side;
25 struct	Side
26 {
27 	QLock;			/* protects loading/unloading */
28 	int	elem;		/* element number */
29 	int	drive;		/* if loaded, where */
30 	uchar	status;		/* Sunload, etc */
31 	uchar	rot;		/* if backside */
32 	int	ord;		/* ordinal number for labeling */
33 
34 	Timet	time;		/* time since last access, to unspin */
35 	Timet	stime;		/* time since last spinup, for hysteresis */
36 	long	nblock;		/* number of native blocks */
37 	long	block;		/* bytes per native block */
38 	long	mult;		/* multiplier to get plan9 blocks */
39 	long	max;		/* max size in plan9 blocks */
40 };
41 
42 typedef	struct	Juke	Juke;
43 struct	Juke
44 {
45 	QLock;				/* protects drive mechanism */
46 	Side	side[MAXSIDE];
47 	int	nside;			/* # of storage elements (*2 if rev) */
48 	int	ndrive;			/* # of transfer elements */
49 	Device*	juke;			/* devworm of changer */
50 	Device*	drive[MAXDRIVE];	/* devworm for i/o */
51 	uchar	offline[MAXDRIVE];	/* drives removed from service */
52 	int	isfixedsize;		/* flag: one size fits all? */
53 	long	fixedsize;		/* the one size that fits all */
54 	int	probeok;		/* wait for init to probe */
55 
56 	Scsi*	robot;			/* scsi(2) interface to robotics */
57 	char*	robotdir;		/* /dev/sdXX name */
58 
59 	/*
60 	 * geometry returned by mode sense.
61 	 * a *0 number (such as mt0) is the `element number' of the
62 	 * first element of that type (e.g., mt, or motor transport).
63 	 * an n* number is the quantity of them.
64 	 */
65 	int	mt0,	nmt;	/* motor transports (robot pickers) */
66 	int	se0,	nse;	/* storage elements (discs, slots) */
67 	int	ie0,	nie;	/* interchange elements (mailbox slots) */
68 	int	dt0,	ndt;	/* drives (data transfer?) */
69 	int	rot;		/* if true, discs are double-sided */
70 
71 	ulong	magic;
72 	Juke*	link;
73 };
74 static	Juke*	jukelist;
75 
76 enum
77 {
78 	Sempty = 0,	/* does not exist */
79 	Sunload,	/* on the shelf */
80 	Sstart,		/* loaded and spinning */
81 };
82 
83 static	int	bestdrive(Juke*, int);
84 static	void	element(Juke*, int);
85 static	int	mmove(Juke*, int, int, int, int);
86 static	void	shelves(void);
87 static	int	waitready(Juke *, Device*);
88 static	int	wormsense(Device*);
89 static	Side*	wormunit(Device*);
90 
91 /* create a new label and try to write it */
92 static void
newlabel(Device * d,Off labelblk,char * labelbuf,unsigned vord)93 newlabel(Device *d, Off labelblk, char *labelbuf, unsigned vord)
94 {
95 	Label *label = (Label *)labelbuf;
96 
97 	memset(labelbuf, 0, RBUFSIZE);
98 	label->magic = Labmagic;
99 	label->ord = vord;
100 	strncpy(label->service, service, sizeof label->service);
101 
102 	if (!okay("write new label"))
103 		print("NOT writing new label\n");
104 	else if (wormwrite(d, labelblk, labelbuf))
105 		/* wormwrite will have complained in detail */
106 		print("can't write new label on side %d\n", vord);
107 	else
108 		print("wrote new label on side %d\n", vord);
109 }
110 
111 /* check for label in last block.  call with v qlocked. */
112 Side*
wormlabel(Device * d,Side * v)113 wormlabel(Device *d, Side *v)
114 {
115 	int vord;
116 	Off labelblk = v->max - 1;	/* last block */
117 	char labelbuf[RBUFSIZE];
118 	Label *label = (Label *)labelbuf;
119 	Juke *w = d->private;
120 
121 	/* wormread calls wormunit, which locks v */
122 	vord = v->ord;
123 	qunlock(v);
124 
125 	memset(label, 0, sizeof *label);
126 	if (wormread(d, labelblk, labelbuf)) {
127 		/*
128 		 * wormread will have complained in detail about the error;
129 		 * no need to repeat most of that detail.
130 		 * probably an unwritten WORM-disc label; write a new one.
131 		 */
132 		print("error reading label block of side %d\n", vord);
133 		newlabel(d, labelblk, labelbuf, vord);
134 	} else if (label->magic != Labmagic) {
135 		swab8(&label->magic);
136 		if (label->magic == Labmagic) {
137 			print(
138 "side %d's label magic byte-swapped; filsys should be configured with xD",
139 				vord);
140 			swab2(&label->ord);
141 			/* could look for Devswab in Juke's filsys */
142 		} else {
143 			/*
144 			 * magic # is wrong in both byte orders, thus
145 			 * probably the label is empty on RW media,
146 			 * so create a new one and try to write it.
147 			 */
148 			print("bad magic number in label of side %d\n", vord);
149 			newlabel(d, labelblk, labelbuf, vord);
150 		}
151 	}
152 
153 	qlock(v);
154 	if (v->ord != vord)
155 		panic("wormlabel: side %d switched ordinal to %d underfoot",
156 			vord, v->ord);
157 	if (label->ord != vord) {
158 		print(
159 	"labelled worm side %Z has wrong ordinal in label (%d, want %d)",
160 			d, label->ord, vord);
161 		qunlock(v);
162 		cmd_wormreset(0, nil);			/* put discs away */
163 		panic("wrong ordinal in label");
164 	}
165 
166 	print("label %Z ordinal %d\n", d, v->ord);
167 	qunlock(v);
168 	/*
169 	 * wormunit should return without calling us again,
170 	 * since v is now known.
171 	 */
172 	if (w != d->private)
173 		panic("wormlabel: w != %Z->private", d);
174 	return wormunit(d);
175 }
176 
177 /*
178  * mounts and spins up the device
179  *	locks the structure
180  */
181 static Side*
wormunit(Device * d)182 wormunit(Device *d)			/* d is l0 or r2 (e.g.) */
183 {
184 	int p, drive;
185 	Device *dr;			/* w0 or w1.2.0 (e.g.) */
186 	Side *v;
187 	Juke *w;
188 	Dir *dir;
189 
190 	w = d->private;
191 	if (w == nil)
192 		panic("wormunit %Z nil juke", d);
193 	if (w->magic != Jukemagic)
194 		panic("bad magic in Juke for %Z", d);
195 	p = d->wren.targ;
196 	if(p < 0 || w && p >= w->nside) {
197 		panic("wormunit: target %d out of range for %Z", p, d);
198 		return 0;
199 	}
200 
201 	/*
202 	 * if disk is unloaded, must load it
203 	 * into next (circular) logical unit
204 	 */
205 	v = &w->side[p];
206 	qlock(v);
207 	if(v->status == Sunload) {
208 		for(;;) {
209 			qlock(w);
210 			drive = bestdrive(w, p);
211 			if(drive >= 0)
212 				break;
213 			qunlock(w);
214 			delay(100);
215 		}
216 		print("\tload   r%ld drive %Z\n", v-w->side, w->drive[drive]);
217 		if(mmove(w, w->mt0, v->elem, w->dt0+drive, v->rot)) {
218 			qunlock(w);
219 			goto sbad;
220 		}
221 		v->drive = drive;
222 		v->status = Sstart;
223 		v->stime = toytime();
224 		qunlock(w);
225 		dr = w->drive[drive];
226 		if (!waitready(w, dr))
227 			goto sbad;
228 		v->stime = toytime();
229 	} else
230 		dr = w->drive[v->drive];
231 	if(v->status != Sstart) {
232 		if(v->status == Sempty)
233 			print("worm: unit empty %Z\n", d);
234 		else
235 			print("worm: not started %Z\n", d);
236 		goto sbad;
237 	}
238 
239 	v->time = toytime();
240 	if(v->block)		/* side is known already */
241 		return v;
242 
243 	/*
244 	 * load and record information about side
245 	 */
246 
247 	if (dr->wren.file)
248 		dr->wren.sddata = dataof(dr->wren.file);
249 	else {
250 		if (dr->wren.sddir == nil) {
251 			if (dr->type == Devwren)
252 				dr->wren.sddir = sdof(dr);
253 			if (dr->wren.sddir == nil)
254 				panic("wormunit: %Z for %Z not a wren", dr, d);
255 		}
256 		dr->wren.sddata = smprint("%s/data", dr->wren.sddir);
257 	}
258 
259 	if (dr->wren.fd == 0)
260 		dr->wren.fd = open(dr->wren.sddata, ORDWR);
261 	if (dr->wren.fd < 0) {
262 		print("wormunit: can't open %s for %Z: %r\n", dr->wren.sddata, d);
263 		goto sbad;
264 	}
265 
266 	v->block = inqsize(dr->wren.sddata);
267 	if(v->block <= 0) {
268 		print("\twormunit %Z block size %ld, setting to %d\n",
269 			d, v->block, Sectorsz);
270 		v->block = Sectorsz;
271 	}
272 
273 	dir = dirfstat(dr->wren.fd);
274 	v->nblock = dir->length / v->block;
275 	free(dir);
276 
277 	v->mult = (RBUFSIZE + v->block - 1) / v->block;
278 	v->max = (v->nblock + 1) / v->mult;
279 
280 	print("\tworm %Z: drive %Z (juke drive %d)\n",
281 		d, w->drive[v->drive], v->drive);
282 	print("\t\t%,ld %ld-byte sectors, ", v->nblock, v->block);
283 	print("%,ld %d-byte blocks\n", v->max, RBUFSIZE);
284 	print("\t\t%ld multiplier\n", v->mult);
285 	if(d->type == Devlworm)
286 		return wormlabel(d, v);
287 	else
288 		return v;
289 
290 sbad:
291 	qunlock(v);
292 	return 0;
293 }
294 
295 /* wait 10s for optical drive to spin up */
296 static int
waitready(Juke * w,Device * d)297 waitready(Juke *w, Device *d)
298 {
299 	int p, e, rv;
300 	char *datanm;
301 
302 	if (w->magic != Jukemagic)
303 		panic("waitready: bad magic in Juke (d->private) for %Z", d);
304 	p = d->wren.targ;
305 	if(p < 0 || p >= w->nside) {
306 		print("waitready: target %d out of range for %Z\n", p, d);
307 		return 0;
308 	}
309 
310 	if (d->type == Devwren && d->wren.file)
311 		datanm = strdup(d->wren.file);
312 	else {
313 		if (d->wren.sddir)
314 			free(d->wren.sddir);
315 		if (d->type == Devwren)
316 			d->wren.sddir = sdof(d);
317 		if (d->wren.sddir == nil)
318 			panic("waitready: d->wren.sddir not set for %Z", d);
319 
320 		datanm = smprint("%s/data", d->wren.sddir);
321 	}
322 
323 	rv = 0;
324 	for(e=0; e < 100; e++) {
325 		if (e == 10)
326 			print("waitready: waiting for %s to exist\n", datanm); // DEBUG
327 		if (access(datanm, AEXIST) >= 0) {
328 			rv = 1;
329 			break;
330 		}
331 		delay(200);
332 	}
333 	if (rv == 0)
334 		print("waitready: %s for %Z didn't come ready\n", datanm, d);
335 	free(datanm);
336 	return rv;
337 }
338 
339 static int
bestdrive(Juke * w,int side)340 bestdrive(Juke *w, int side)
341 {
342 	Side *v, *bv[MAXDRIVE];
343 	int i, e, drive;
344 	Timet t, t0;
345 
346 loop:
347 	/* build table of what platters on what drives */
348 	for(i=0; i<w->ndt; i++)
349 		bv[i] = 0;
350 
351 	v = &w->side[0];
352 	for(i=0; i < w->nside; i++, v++)
353 		if(v->status == Sstart) {
354 			drive = v->drive;
355 			if(drive >= 0 && drive < w->ndt)
356 				bv[drive] = v;
357 		}
358 
359 	/*
360 	 * find oldest drive, but must be
361 	 * at least THYSTER old.
362 	 */
363 	e = w->side[side].elem;
364 	t0 = toytime() - THYSTER;
365 	t = t0;
366 	drive = -1;
367 	for(i=0; i<w->ndt; i++) {
368 		v = bv[i];
369 		if(v == 0) {		/* 2nd priority: empty drive */
370 			if(w->offline[i])
371 				continue;
372 			if(w->drive[i] != devnone) {
373 				drive = i;
374 				t = 0;
375 			}
376 			continue;
377 		}
378 		if(v->elem == e) {	/* 1st priority: other side */
379 			drive = -1;
380 			if(v->stime < t0)
381 				drive = i;
382 			break;
383 		}
384 		if(v->stime < t) {	/* 3rd priority: by time */
385 			drive = i;
386 			t = v->stime;
387 		}
388 	}
389 
390 	if(drive >= 0) {
391 		v = bv[drive];
392 		if(v) {
393 			qlock(v);
394 			if(v->status != Sstart) {
395 				qunlock(v);
396 				goto loop;
397 			}
398 			print("\tunload r%ld drive %Z\n",
399 				v-w->side, w->drive[drive]);
400 			if(mmove(w, w->mt0, w->dt0+drive, v->elem, v->rot)) {
401 				qunlock(v);
402 				goto loop;
403 			}
404 			v->status = Sunload;
405 			qunlock(v);
406 		}
407 	}
408 	return drive;
409 }
410 
411 Devsize
wormsize(Device * d)412 wormsize(Device *d)
413 {
414 	Side *v;
415 	Juke *w;
416 	Devsize size;
417 
418 	w = d->private;
419 	if (w->magic != Jukemagic)
420 		print("wormsize: bad magic in Juke (d->private) for %Z\n", d);
421 	if(w->isfixedsize && w->fixedsize != 0)
422 		size = w->fixedsize;	/* fixed size is now known */
423 	else {
424 		if (w != d->private)
425 			panic("wormsize: w != %Z->private", d);
426 		v = wormunit(d);
427 		if(v == nil)
428 			return 0;
429 		size = v->max;
430 		qunlock(v);
431 		/*
432 		 * set fixed size for whole Juke from
433 		 * size of first disc examined.
434 		 */
435 		if(w->isfixedsize)
436 			w->fixedsize = size;
437 	}
438 	if(d->type == Devlworm)
439 		return size-1;		/* lie: last block is for label */
440 	return size;
441 }
442 
443 /*
444  * return a Devjuke or an mcat (normally of sides) from within d (or nil).
445  * if it's an mcat, the caller must walk it.
446  */
447 static Device *
devtojuke(Device * d,Device * top)448 devtojuke(Device *d, Device *top)
449 {
450 	while (d != nil)
451 		switch(d->type) {
452 		default:
453 			print("devtojuke: type of device %Z of %Z unknown\n",
454 				d, top);
455 			return nil;
456 
457 		case Devjuke:
458 			/* jackpot!  d->private is a (Juke *) with nside, &c. */
459 			/* FALL THROUGH */
460 		case Devmcat:
461 		case Devmlev:
462 		case Devmirr:
463 			/* squint hard & call an mlev or a mirr an mcat */
464 			return d;
465 
466 		case Devworm:
467 		case Devlworm:
468 			/*
469 			 * d->private is a (Juke *) with nside, etc.,
470 			 * but we're not supposed to get here.
471 			 */
472 			print("devtojuke: (l)worm %Z of %Z encountered\n",
473 				d, top);
474 			/* FALL THROUGH */
475 		case Devwren:
476 			return nil;
477 
478 		case Devcw:
479 			d = d->cw.w;			/* usually juke */
480 			break;
481 		case Devro:
482 			d = d->ro.parent;		/* cw */
483 			break;
484 		case Devfworm:
485 			d = d->fw.fw;
486 			break;
487 		case Devpart:
488 			d = d->part.d;
489 			break;
490 		case Devswab:
491 			d = d->swab.d;
492 			break;
493 		}
494 	return d;
495 }
496 
497 static int
devisside(Device * d)498 devisside(Device *d)
499 {
500 	return d->type == Devworm || d->type == Devlworm;
501 }
502 
503 static Device *
findside(Device * juke,int side,Device * top)504 findside(Device *juke, int side, Device *top)
505 {
506 	int i = 0;
507 	Device *mcat = juke->j.m, *x;
508 	Juke *w = juke->private;
509 
510 	for (x = mcat->cat.first; x != nil; x = x->link) {
511 		if (!devisside(x)) {
512 			print("wormsizeside: %Z of %Z of %Z type not (l)worm\n",
513 				x, mcat, top);
514 			return nil;
515 		}
516 		i = x->wren.targ;
517 		if (i < 0 || i >= w->nside)
518 			panic("wormsizeside: side %d in %Z out of range",
519 				i, mcat);
520 		if (i == side)
521 			break;
522 	}
523 	if (x == nil)
524 		return nil;
525 	if (w->side[i].time == 0) {
526 		print("wormsizeside: side %d not in jukebox %Z\n", i, juke);
527 		return nil;
528 	}
529 	return x;
530 }
531 
532 typedef struct {
533 	int	sleft;		/* sides still to visit to reach desired side */
534 	int	starget;	/* side of topdev we want */
535 	Device	*topdev;
536 	int	sawjuke;	/* passed by a jukebox */
537 	int	sized;		/* flag: asked wormsize for size of starget */
538 } Visit;
539 
540 /*
541  * walk the Device tree from d looking for Devjukes, counting sides.
542  * the main complication is mcats and the like with Devjukes in them.
543  * use Devjuke's d->private as Juke* and see sides.
544  */
545 static Off
visitsides(Device * d,Device * parentj,Visit * vp)546 visitsides(Device *d, Device *parentj, Visit *vp)
547 {
548 	Off size = 0;
549 	Device *x;
550 	Juke *w;
551 
552 	/*
553 	 * find the first juke or mcat.
554 	 * d==nil means we couldn't find one; typically harmless, due to a
555 	 * mirror of dissimilar devices.
556 	 */
557 	d = devtojuke(d, vp->topdev);
558 	if (d == nil || vp->sleft < 0)
559 		return 0;
560 	if (d->type == Devjuke) {    /* jackpot!  d->private is a (Juke *) */
561 		vp->sawjuke = 1;
562 		w = d->private;
563 		/*
564 		 * if there aren't enough sides in this jukebox to reach
565 		 * the desired one, subtract these sides and pass.
566 		 */
567 		if (vp->sleft >= w->nside) {
568 			vp->sleft -= w->nside;
569 			return 0;
570 		}
571 		/* else this is the right juke, paw through mcat of sides */
572 		return visitsides(d->j.m, d, vp);
573 	}
574 
575 	/*
576 	 * d will usually be an mcat of sides, but it could be an mcat of
577 	 * jukes, for example.  in that case, we need to walk the mcat,
578 	 * recursing as needed, until we find the right juke, then stop at
579 	 * the right side within its mcat of sides, by comparing side
580 	 * numbers, not just by counting (to allow for unused slots).
581 	 */
582 	x = d->cat.first;
583 	if (x == nil) {
584 		print("visitsides: %Z of %Z: empty mcat\n", d, vp->topdev);
585 		return 0;
586 	}
587 	if (!devisside(x)) {
588 		for (; x != nil && !vp->sized; x = x->link)
589 			size = visitsides(x, parentj, vp);
590 		return size;
591 	}
592 
593 	/* the side we want is in this jukebox, thus this mcat (d) */
594 	if (parentj == nil) {
595 		print("visitsides: no parent juke for sides mcat %Z\n", d);
596 		vp->sleft = -1;
597 		return 0;
598 	}
599 	if (d != parentj->j.m)
600 		panic("visitsides: mcat mismatch %Z vs %Z", d, parentj->j.m);
601 	x = findside(parentj, vp->sleft, vp->topdev);
602 	if (x == nil) {
603 		vp->sleft = -1;
604 		return 0;
605 	}
606 
607 	/* we've turned vp->starget into the right Device* */
608 	vp->sleft = 0;
609 	vp->sized = 1;
610 	return wormsize(x);
611 }
612 
613 /*
614  * d must be, or be within, a filesystem config that also contains
615  * the jukebox that `side' resides on.
616  * d is normally a Devcw, but could be Devwren, Devide, Devpart, Devfworm,
617  * etc. if called from chk.c Ctouch code.  Note too that the worm part of
618  * the Devcw might be other than a Devjuke.
619  */
620 Devsize
wormsizeside(Device * d,int side)621 wormsizeside(Device *d, int side)
622 {
623 	Devsize size;
624 	Visit visit;
625 
626 	memset(&visit, 0, sizeof visit);
627 	visit.starget = visit.sleft = side;
628 	visit.topdev = d;
629 	size = visitsides(d, nil, &visit);
630 	if (visit.sawjuke && (visit.sleft != 0 || !visit.sized)) {
631 		print("wormsizeside: fewer than %d sides in %Z\n", side, d);
632 		return 0;
633 	}
634 	return size;
635 }
636 
637 /*
638  * returns starts (in blocks) of side #side and #(side+1) of dev in *stp.
639  * dev should be a Devcw.
640  */
641 void
wormsidestarts(Device * dev,int side,Sidestarts * stp)642 wormsidestarts(Device *dev, int side, Sidestarts *stp)
643 {
644 	int s;
645 	Devsize dstart;
646 
647 	for (dstart = s = 0; s < side; s++)
648 		dstart += wormsizeside(dev, s);
649 	stp->sstart = dstart;
650 	stp->s1start = dstart + wormsizeside(dev, side);
651 }
652 
653 int
wormread(Device * d,Off b,void * c)654 wormread(Device *d, Off b, void *c)
655 {
656 	int r = 0;
657 	long max;
658 	char name[128];
659 	Side *v = wormunit(d);
660 	Juke *w = d->private;
661 	Device *dr;
662 
663 	if (v == nil)
664 		panic("wormread: nil wormunit(%Z)", d);
665 	dr = w->drive[v->drive];
666 	if (dr->wren.fd < 0)
667 		panic("wormread: unopened fd for %Z", d);
668 	max = (d->type == Devlworm? v->max + 1: v->max);
669 	if(b >= max) {
670 		print("wormread: block out of range %Z(%lld)\n", d, (Wideoff)b);
671 		r = 0x071;
672 	} else if (pread(dr->wren.fd, c, RBUFSIZE, (vlong)b*RBUFSIZE) != RBUFSIZE) {
673 		fd2path(dr->wren.fd, name, sizeof name);
674 		print("wormread: error on %Z(%lld) on %s in %s: %r\n",
675 			d, (Wideoff)b, name, dr->wren.sddir);
676 		cons.nwormre++;
677 		r = 1;
678 	}
679 	qunlock(v);
680 	return r;
681 }
682 
683 int
wormwrite(Device * d,Off b,void * c)684 wormwrite(Device *d, Off b, void *c)
685 {
686 	int r = 0;
687 	long max;
688 	char name[128];
689 	Side *v = wormunit(d);
690 	Juke *w = d->private;
691 	Device *dr;
692 
693 	if (v == nil)
694 		panic("wormwrite: nil wormunit(%Z)", d);
695 	dr = w->drive[v->drive];
696 	if (dr->wren.fd < 0)
697 		panic("wormwrite: unopened fd for %Z", d);
698 	max = (d->type == Devlworm? v->max + 1: v->max);
699 	if(b >= max) {
700 		print("wormwrite: block out of range %Z(%lld)\n",
701 			d, (Wideoff)b);
702 		r = 0x071;
703 	} else if (pwrite(dr->wren.fd, c, RBUFSIZE, (vlong)b*RBUFSIZE) != RBUFSIZE) {
704 		fd2path(dr->wren.fd, name, sizeof name);
705 		print("wormwrwite: error on %Z(%lld) on %s in %s: %r\n",
706 			d, (Wideoff)b, name, dr->wren.sddir);
707 		cons.nwormwe++;
708 		r = 1;
709 	}
710 	qunlock(v);
711 	return r;
712 }
713 
714 static int
mmove(Juke * w,int trans,int from,int to,int rot)715 mmove(Juke *w, int trans, int from, int to, int rot)
716 {
717 	int s;
718 	uchar cmd[12], buf[4];
719 	static int recur = 0;
720 
721 	memset(cmd, 0, sizeof cmd);
722 	cmd[0] = 0xa5;		/* move medium */
723 	cmd[2] = trans>>8;
724 	cmd[3] = trans;
725 	cmd[4] = from>>8;
726 	cmd[5] = from;
727 	cmd[6] = to>>8;
728 	cmd[7] = to;
729 	if(rot)
730 		cmd[10] = 1;
731 	s = scsiio(w->juke, SCSInone, cmd, sizeof cmd, buf, 0);	/* mmove */
732 	if(s) {
733 		print("scsio status #%x\n", s);
734 		print("move medium t=%d fr=%d to=%d rot=%d\n",
735 			trans, from, to, rot);
736 //		panic("mmove");
737 		if(recur == 0) {
738 			recur = 1;
739 			print("element from=%d\n", from);
740 			element(w, from);
741 			print("element to=%d\n", to);
742 			element(w, to);
743 			print("element trans=%d\n", trans);
744 			element(w, trans);
745 			recur = 0;
746 		}
747 		return 1;
748 	}
749 	return 0;
750 }
751 
752 static void
geometry(Juke * w)753 geometry(Juke *w)
754 {
755 	int s;
756 	uchar cmd[6], buf[4+20];
757 
758 	memset(cmd, 0, sizeof cmd);
759 	memset(buf, 0, sizeof buf);
760 	cmd[0] = 0x1a;		/* mode sense */
761 	cmd[2] = 0x1d;		/* element address assignment */
762 	cmd[4] = sizeof buf;	/* allocation length */
763 
764 	s = scsiio(w->juke, SCSIread, cmd, sizeof cmd, buf, sizeof buf); /* mode sense elem addrs */
765 	if(s)
766 		panic("geometry #%x", s);
767 
768 	w->mt0 = (buf[4+2]<<8) | buf[4+3];
769 	w->nmt = (buf[4+4]<<8) | buf[4+5];
770 	w->se0 = (buf[4+6]<<8) | buf[4+7];
771 	w->nse = (buf[4+8]<<8) | buf[4+9];
772 	w->ie0 = (buf[4+10]<<8) | buf[4+11];
773 	w->nie = (buf[4+12]<<8) | buf[4+13];
774 	w->dt0 = (buf[4+14]<<8) | buf[4+15];
775 	w->ndt = (buf[4+16]<<8) | buf[4+17];
776 
777 	memset(cmd, 0, 6);
778 	memset(buf, 0, sizeof buf);
779 	cmd[0] = 0x1a;		/* mode sense */
780 	cmd[2] = 0x1e;		/* transport geometry */
781 	cmd[4] = sizeof buf;	/* allocation length */
782 
783 	s = scsiio(w->juke, SCSIread, cmd, sizeof cmd, buf, sizeof buf); /* mode sense geometry */
784 	if(s)
785 		panic("geometry #%x", s);
786 
787 	w->rot = buf[4+2] & 1;
788 
789 	print("\tmt %d %d\n", w->mt0, w->nmt);
790 	print("\tse %d %d\n", w->se0, w->nse);
791 	print("\tie %d %d\n", w->ie0, w->nie);
792 	print("\tdt %d %d\n", w->dt0, w->ndt);
793 	print("\trot %d\n", w->rot);
794 	prflush();
795 }
796 
797 /*
798  * read element e's status from jukebox w, move any disc in drive back to its
799  * slot, and update and print software status.
800  */
801 static void
element(Juke * w,int e)802 element(Juke *w, int e)
803 {
804 	uchar cmd[12], buf[8+8+88];
805 	int s, t;
806 
807 	memset(cmd, 0, sizeof cmd);
808 	memset(buf, 0, sizeof buf);
809 	cmd[0] = 0xb8;		/* read element status */
810 	cmd[2] = e>>8;		/* starting element */
811 	cmd[3] = e;
812 	cmd[5] = 1;		/* number of elements */
813 	cmd[9] = sizeof buf;	/* allocation length */
814 
815 	s = scsiio(w->juke, SCSIread, cmd, sizeof cmd, buf, sizeof buf); /* read elem sts */
816 	if(s) {
817 		print("scsiio #%x\n", s);
818 		goto bad;
819 	}
820 
821 	s = (buf[0]<<8) | buf[1];
822 	if(s != e) {
823 		print("element = %d\n", s);
824 		goto bad;
825 	}
826 	if(buf[3] != 1) {
827 		print("number reported = %d\n", buf[3]);
828 		goto bad;
829 	}
830 	s = (buf[8+8+0]<<8) | buf[8+8+1];
831 	if(s != e) {
832 		print("element1 = %d\n", s);
833 		goto bad;
834 	}
835 
836 	switch(buf[8+0]) {	/* element type */
837 	default:
838 		print("unknown element %d: %d\n", e, buf[8+0]);
839 		goto bad;
840 	case 1:			/* transport */
841 		s = e - w->mt0;
842 		if(s < 0 || s >= w->nmt)
843 			goto bad;
844 		if(buf[8+8+2] & 1)
845 			print("transport %d full %d.%d\n", s,
846 				(buf[8+8+10]<<8) | buf[8+8+11],
847 				(buf[8+8+9]>>6) & 1);
848 		break;
849 	case 2:			/* storage */
850 		s = e - w->se0;
851 		if(s < 0 || s >= w->nse)
852 			goto bad;
853 		w->side[s].status = Sempty;
854 		if(buf[8+8+2] & 1)
855 			w->side[s].status = Sunload;
856 		if(w->rot)
857 			w->side[w->nse+s].status = w->side[s].status;
858 		break;
859 	case 3:			/* import/export */
860 		s = e - w->ie0;
861 		if(s < 0 || s >= w->nie)
862 			goto bad;
863 		print("import/export %d #%.2x %d.%d\n", s,
864 			buf[8+8+2],
865 			(buf[8+8+10]<<8) | buf[8+8+11],
866 			(buf[8+8+9]>>6) & 1);
867 		break;
868 	case 4:			/* data transfer */
869 		s = e - w->dt0;
870 		if(s < 0 || s >= w->ndt)
871 			goto bad;
872 		print("data transfer %d #%.2x %d.%d\n", s,
873 			buf[8+8+2],
874 			(buf[8+8+10]<<8) | buf[8+8+11],
875 			(buf[8+8+9]>>6) & 1);
876 		if(buf[8+8+2] & 1) {
877 			t = ((buf[8+8+10]<<8) | buf[8+8+11]) - w->se0;
878 			if (t < 0 || t >= w->nse || t >= MAXSIDE ||
879 			    s >= MAXDRIVE) {
880 				print(
881 		"element: juke %Z lies; claims side %d is in drive %d\n",
882 					w->juke, t, s);	/* lying sack of ... */
883 				/*
884 				 * at minimum, we've avoided corrupting our
885 				 * data structures.  if we know that numbers
886 				 * like w->nside are valid here, we could use
887 				 * them in more stringent tests.
888 				 * perhaps should whack the jukebox upside the
889 				 * head here to knock some sense into it.
890 				 */
891 				goto bad;
892 			}
893 			print("r%d in drive %d\n", t, s);
894 			if(mmove(w, w->mt0, w->dt0+s, w->se0+t,
895 			    (buf[8+8+9]>>6) & 1)) {
896 				print("mmove initial unload\n");
897 				goto bad;
898 			}
899 			w->side[t].status = Sunload;
900 			if(w->rot)
901 				w->side[w->nse+t].status = Sunload;
902 		}
903 		if(buf[8+8+2] & 4) {
904 			print("drive w%d has exception #%.2x #%.2x\n", s,
905 				buf[8+8+4], buf[8+8+5]);
906 			goto bad;
907 		}
908 		break;
909 	}
910 	return;
911 bad:
912 	/* panic("element") */ ;
913 }
914 
915 /*
916  * read all elements' status from jukebox w, move any discs in drives back
917  * to their slots, and update and print software status.
918  */
919 static void
positions(Juke * w)920 positions(Juke *w)
921 {
922 	int i, f;
923 
924 	/* mark empty shelves */
925 	for(i=0; i<w->nse; i++)
926 		element(w, w->se0+i);
927 	for(i=0; i<w->nmt; i++)
928 		element(w, w->mt0+i);
929 	for(i=0; i<w->nie; i++)
930 		element(w, w->ie0+i);
931 	for(i=0; i<w->ndt; i++)
932 		element(w, w->dt0+i);
933 
934 	f = 0;
935 	for(i=0; i<w->nse; i++)
936 		if(w->side[i].status == Sempty) {
937 			if(f) {
938 				print("r%d\n", i-1);
939 				f = 0;
940 			}
941 		} else {
942 			if(!f) {
943 				print("\tshelves r%d-", i);
944 				f = 1;
945 			}
946 		}
947 	if(f)
948 		print("r%d\n", i-1);
949 }
950 
951 static void
jinit(Juke * w,Device * d,int o)952 jinit(Juke *w, Device *d, int o)
953 {
954 	int p;
955 	Device *dev = d;
956 
957 	switch(d->type) {
958 	default:
959 		print("juke platter not (devmcat of) dev(l)worm: %Z\n", d);
960 		panic("jinit: type");
961 
962 	case Devmcat:
963 		/*
964 		 * we don't call mcatinit(d) here, so we have to set d->cat.ndev
965 		 * ourselves.
966 		 */
967 		for(d=d->cat.first; d; d=d->link)
968 			jinit(w, d, o++);
969 		dev->cat.ndev = o;
970 		break;
971 
972 	case Devlworm:
973 		p = d->wren.targ;
974 		if(p < 0 || p >= w->nside)
975 			panic("jinit partition %Z", d);
976 		w->side[p].ord = o;
977 		/* FALL THROUGH */
978 	case Devworm:
979 		if(d->private) {
980 			print("juke platter private pointer set %p\n",
981 				d->private);
982 			panic("jinit: private");
983 		}
984 		d->private = w;
985 		break;
986 	}
987 }
988 
989 Side*
wormi(char * arg)990 wormi(char *arg)
991 {
992 	int i, j;
993 	Juke *w;
994 	Side *v;
995 
996 	i = number(arg, -1, 10) - 1;
997 	w = jukelist;
998 	if(i < 0 || i >= w->nside) {
999 		print("bad unit number %s (%d)\n", arg, i+1);
1000 		return 0;
1001 	}
1002 	j = i;
1003 	if(j >= w->nse)
1004 		j -= w->nse;
1005 	if(j < w->nside) {
1006 		v = &w->side[j];
1007 		qlock(v);
1008 		if(v->status == Sstart) {
1009 			if(mmove(w, w->mt0, w->dt0+v->drive, v->elem, v->rot)) {
1010 				qunlock(v);
1011 				return 0;
1012 			}
1013 			v->status = Sunload;
1014 		}
1015 		qunlock(v);
1016 	}
1017 	j += w->nse;
1018 	if(j < w->nside) {
1019 		v = &w->side[j];
1020 		qlock(v);
1021 		if(v->status == Sstart) {
1022 			if(mmove(w, w->mt0, w->dt0+v->drive, v->elem, v->rot)) {
1023 				qunlock(v);
1024 				return 0;
1025 			}
1026 			v->status = Sunload;
1027 		}
1028 		qunlock(v);
1029 	}
1030 	v = &w->side[i];
1031 	qlock(v);
1032 	return v;
1033 }
1034 
1035 static void
cmd_wormoffline(int argc,char * argv[])1036 cmd_wormoffline(int argc, char *argv[])
1037 {
1038 	int u, i;
1039 	Juke *w;
1040 
1041 	if(argc <= 1) {
1042 		print("usage: wormoffline drive\n");
1043 		return;
1044 	}
1045 	u = number(argv[1], -1, 10);
1046 	w = jukelist;
1047 	if(u < 0 || u >= w->ndrive) {
1048 		print("bad drive %s (0<=%d<%d)\n", argv[1], u, w->ndrive);
1049 		return;
1050 	}
1051 	if(w->offline[u])
1052 		print("drive %d already offline\n", u);
1053 	w->offline[u] = 1;
1054 	for(i=0; i<w->ndrive; i++)
1055 		if(w->offline[i] == 0)
1056 			return;
1057 	print("that would take all drives offline\n");
1058 	w->offline[u] = 0;
1059 }
1060 
1061 static void
cmd_wormonline(int argc,char * argv[])1062 cmd_wormonline(int argc, char *argv[])
1063 {
1064 	int u;
1065 	Juke *w;
1066 
1067 	if(argc <= 1) {
1068 		print("usage: wormonline drive\n");
1069 		return;
1070 	}
1071 	u = number(argv[1], -1, 10);
1072 	w = jukelist;
1073 	if(u < 0 || u >= w->ndrive) {
1074 		print("bad drive %s (0<=%d<%d)\n", argv[1], u, w->ndrive);
1075 		return;
1076 	}
1077 	if(w->offline[u] == 0)
1078 		print("drive %d already online\n", u);
1079 	w->offline[u] = 0;
1080 }
1081 
1082 void
cmd_wormreset(int,char * [])1083 cmd_wormreset(int, char *[])
1084 {
1085 	Juke *w;
1086 
1087 	for(w=jukelist; w; w=w->link) {
1088 		qlock(w);
1089 		positions(w);
1090 		qunlock(w);
1091 	}
1092 }
1093 
1094 static void
cmd_wormeject(int argc,char * argv[])1095 cmd_wormeject(int argc, char *argv[])
1096 {
1097 	Juke *w;
1098 	Side *v;
1099 
1100 	if(argc <= 1) {
1101 		print("usage: wormeject unit\n");
1102 		return;
1103 	}
1104 	v = wormi(argv[1]);
1105 	if(v == 0)
1106 		return;
1107 	w = jukelist;
1108 	mmove(w, w->mt0, v->elem, w->ie0, 0);
1109 	qunlock(v);
1110 }
1111 
1112 static void
cmd_wormingest(int argc,char * argv[])1113 cmd_wormingest(int argc, char *argv[])
1114 {
1115 	Juke *w;
1116 	Side *v;
1117 
1118 	if(argc <= 1) {
1119 		print("usage: wormingest unit\n");
1120 		return;
1121 	}
1122 	v = wormi(argv[1]);
1123 	if(v == 0)
1124 		return;
1125 	w = jukelist;
1126 	mmove(w, w->mt0, w->ie0, v->elem, 0);
1127 	qunlock(v);
1128 }
1129 
1130 static void
newside(Side * v,int rot,int elem)1131 newside(Side *v, int rot, int elem)
1132 {
1133 	qlock(v);
1134 	qunlock(v);
1135 //	v->name = "shelf";
1136 	v->elem = elem;
1137 	v->rot = rot;
1138 	v->status = Sempty;
1139 	v->time = toytime();
1140 }
1141 
1142 /*
1143  * query jukebox robotics for geometry;
1144  * argument is the wren dev of the changer.
1145  * result is actually Juke*, but that type is only known in this file.
1146  */
1147 void *
querychanger(Device * xdev)1148 querychanger(Device *xdev)
1149 {
1150 	Juke *w;
1151 	Side *v;
1152 	int i;
1153 
1154 	if (xdev == nil)
1155 		panic("querychanger: nil Device");
1156 	if(xdev->type != Devwren) {
1157 		print("juke changer not wren %Z\n", xdev);
1158 		goto bad;
1159 	}
1160 	for(w=jukelist; w; w=w->link)
1161 		if(xdev == w->juke)
1162 			return w;
1163 
1164 	/*
1165 	 * allocate a juke structure
1166 	 * no locking problems.
1167 	 */
1168 	w = malloc(sizeof(Juke));
1169 	w->magic = Jukemagic;
1170 	w->isfixedsize = FIXEDSIZE;
1171 	w->link = jukelist;
1172 	jukelist = w;
1173 
1174 	print("alloc juke %Z\n", xdev);
1175 	qlock(w);
1176 	qunlock(w);
1177 //	w->name = "juke";
1178 	w->juke = xdev;
1179 	w->robotdir = sdof(xdev);
1180 	w->robot = openscsi(w->robotdir);
1181 	if (w->robot == nil)
1182 		panic("can't openscsi(%s): %r", w->robotdir);
1183 	newscsi(xdev, w->robot);
1184 	geometry(w);
1185 
1186 	/*
1187 	 * pick up each side
1188 	 */
1189 	w->nside = w->nse;
1190 	if(w->rot)
1191 		w->nside += w->nside;
1192 	if(w->nside > MAXSIDE) {
1193 		print("too many sides: %d max %d\n", w->nside, MAXSIDE);
1194 		goto bad;
1195 	}
1196 	for(i=0; i < w->nse; i++) {
1197 		v = &w->side[i];
1198 		newside(v, 0, w->se0 + i);
1199 		if(w->rot)
1200 			newside(v + w->nse, 1, w->se0 + i);
1201 	}
1202 	positions(w);
1203 
1204 	w->ndrive = w->ndt;
1205 	if(w->ndrive > MAXDRIVE) {
1206 		print("ndrives truncated to %d\n", MAXDRIVE);
1207 		w->ndrive = MAXDRIVE;
1208 	}
1209 
1210 	/*
1211 	 * pick up each drive
1212 	 */
1213 	for(i=0; i<w->ndrive; i++)
1214 		w->drive[i] = devnone;
1215 	return w;
1216 bad:
1217 	panic("querychanger: %Z", xdev);
1218 	return nil;
1219 }
1220 
1221 void
jukeinit(Device * d)1222 jukeinit(Device *d)
1223 {
1224 	Juke *w;
1225 	Device *xdev;
1226 	int i;
1227 	static int beenhere = 0;
1228 
1229 	/* j(w<changer>w<station0>...)(r<platters>) */
1230 	if (d == nil)
1231 		panic("jukeinit: nil Device");
1232 	xdev = d->j.j;
1233 	if(xdev == nil || xdev->type != Devmcat) {
1234 		print("juke union not mcat\n");
1235 		goto bad;
1236 	}
1237 
1238 	/*
1239 	 * pick up the changer device
1240 	 */
1241 	xdev = xdev->cat.first;
1242 	w = querychanger(xdev);
1243 
1244 	if (!beenhere) {
1245 		beenhere = 1;
1246 		cmd_install("wormreset",
1247 			"-- put drives back where jukebox thinks they belong",
1248 			cmd_wormreset);
1249 		cmd_install("wormeject", "unit -- shelf to outside",
1250 			cmd_wormeject);
1251 		cmd_install("wormingest", "unit -- outside to shelf",
1252 			cmd_wormingest);
1253 		cmd_install("wormoffline", "unit -- disable drive",
1254 			cmd_wormoffline);
1255 		cmd_install("wormonline", "unit -- enable drive",
1256 			cmd_wormonline);
1257 	}
1258 
1259 	/* walk through the worm drives */
1260 	i = 0;
1261 	while(xdev = xdev->link) {
1262 		if(xdev->type != Devwren) {
1263 			print("drive not devwren: %Z\n", xdev);
1264 			goto bad;
1265 		}
1266 		if(w->drive[i]->type != Devnone &&
1267 		   xdev != w->drive[i]) {
1268 			print("double init drive %d %Z %Z\n",
1269 				i, w->drive[i], xdev);
1270 			goto bad;
1271 		}
1272 		if(i >= w->ndrive) {
1273 			print("too many drives %Z\n", xdev);
1274 			goto bad;
1275 		}
1276 		w->drive[i++] = xdev;
1277 	}
1278 
1279 	if(i <= 0) {
1280 		print("no drives\n");
1281 		goto bad;
1282 	}
1283 
1284 	/*
1285 	 * put w pointer in each platter
1286 	 */
1287 	d->private = w;
1288 	jinit(w, d->j.m, 0);
1289 	w->probeok = 1;
1290 	return;
1291 
1292 bad:
1293 	panic("juke init");
1294 }
1295 
1296 /*
1297  * called periodically
1298  */
1299 void
wormprobe(void)1300 wormprobe(void)
1301 {
1302 	int i, drive;
1303 	Timet t;
1304 	Side *v;
1305 	Juke *w;
1306 
1307 	t = toytime() - TWORM;
1308 	for(w=jukelist; w; w=w->link) {
1309 		if(w->probeok == 0 || !canqlock(w))
1310 			continue;
1311 		for(i=0; i<w->nside; i++) {
1312 			v = &w->side[i];
1313 			if(!canqlock(v))
1314 				continue;
1315 			if(v->status == Sstart && t > v->time) {
1316 				drive = v->drive;
1317 				print("\ttime   r%ld drive %Z\n",
1318 					v-w->side, w->drive[drive]);
1319 				mmove(w, w->mt0, w->dt0+drive, v->elem, v->rot);
1320 				v->status = Sunload;
1321 			}
1322 			qunlock(v);
1323 		}
1324 		qunlock(w);
1325 	}
1326 }
1327