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