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