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.
14 MAXSIDE = 500, /* max. disc sides */
19 Sectorsz = 512, /* usual disk sector size */
21 Jukemagic = 0xbabfece2,
24 typedef struct Side Side;
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 */
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 */
42 typedef struct Juke Juke;
45 QLock; /* protects drive mechanism */
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 */
56 Scsi* robot; /* scsi(2) interface to robotics */
57 char* robotdir; /* /dev/sdXX name */
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.
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 */
74 static Juke* jukelist;
78 Sempty = 0, /* does not exist */
79 Sunload, /* on the shelf */
80 Sstart, /* loaded and spinning */
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*);
91 /* create a new label and try to write it */
93 newlabel(Device *d, Off labelblk, char *labelbuf, unsigned vord)
95 Label *label = (Label *)labelbuf;
97 memset(labelbuf, 0, RBUFSIZE);
98 label->magic = Labmagic;
100 strncpy(label->service, service, sizeof label->service);
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);
108 print("wrote new label on side %d\n", vord);
111 /* check for label in last block. call with v qlocked. */
113 wormlabel(Device *d, Side *v)
116 Off labelblk = v->max - 1; /* last block */
117 char labelbuf[RBUFSIZE];
118 Label *label = (Label *)labelbuf;
119 Juke *w = d->private;
121 /* wormread calls wormunit, which locks v */
125 memset(label, 0, sizeof *label);
126 if (wormread(d, labelblk, labelbuf)) {
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.
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",
140 /* could look for Devswab in Juke's filsys */
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.
147 fprint(2, "bad magic number in label of side %d\n", vord);
148 newlabel(d, labelblk, labelbuf, vord);
154 panic("wormlabel: side %d switched ordinal to %d underfoot",
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);
160 cmd_wormreset(0, nil); /* put discs away */
161 panic("wrong ordinal in label");
164 fprint(2, "label %Z ordinal %d\n", d, v->ord);
167 * wormunit should return without calling us again,
168 * since v is now known.
171 panic("wormlabel: w != %Z->private", d);
176 * mounts and spins up the device
177 * locks the structure
180 wormunit(Device *d) /* d is l0 or r2 (e.g.) */
183 Device *dr; /* w0 or w1.2.0 (e.g.) */
190 panic("wormunit %Z nil juke", d);
191 if (w->magic != Jukemagic)
192 panic("bad magic in Juke for %Z", d);
194 if(p < 0 || w && p >= w->nside) {
195 panic("wormunit: target %d out of range for %Z", p, d);
200 * if disk is unloaded, must load it
201 * into next (circular) logical unit
205 if(v->status == Sunload) {
208 drive = bestdrive(w, p);
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)) {
222 v->stime = toytime();
224 dr = w->drive[drive];
225 if (!waitready(w, dr))
227 v->stime = toytime();
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);
234 fprint(2, "worm: not started %Z\n", d);
239 if(v->block) /* side is known already */
243 * load and record information about side
247 dr->wren.sddata = dataof(dr->wren.file);
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);
255 dr->wren.sddata = smprint("%s/data", dr->wren.sddir);
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);
265 v->block = inqsize(dr->wren.sddata);
268 fprint(2, "\twormunit %Z block size %ld, setting to %d\n",
269 d, v->block, Sectorsz);
273 dir = dirfstat(dr->wren.fd);
274 v->nblock = dir->length / v->block;
277 v->mult = (RBUFSIZE + v->block - 1) / v->block;
278 v->max = (v->nblock + 1) / v->mult;
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);
287 if(d->type == Devlworm)
288 return wormlabel(d, v);
297 /* wait 10s for optical drive to spin up */
299 waitready(Juke *w, Device *d)
304 if (w->magic != Jukemagic)
305 panic("waitready: bad magic in Juke (d->private) for %Z", d);
307 if(p < 0 || p >= w->nside) {
308 fprint(2, "waitready: target %d out of range for %Z\n", p, d);
312 if (d->type == Devwren && d->wren.file)
313 datanm = strdup(d->wren.file);
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);
322 datanm = smprint("%s/data", d->wren.sddir);
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){
336 fprint(2, "waitready: %s for %Z didn't come ready\n", datanm, d);
342 bestdrive(Juke *w, int side)
344 Side *v, *bv[MAXDRIVE];
349 /* build table of what platters on what drives */
350 for(i=0; i<w->ndt; i++)
354 for(i=0; i < w->nside; i++, v++)
355 if(v->status == Sstart) {
357 if(drive >= 0 && drive < w->ndt)
362 * find oldest drive, but must be
363 * at least THYSTER old.
365 e = w->side[side].elem;
366 t0 = toytime() - THYSTER;
369 for(i=0; i<w->ndt; i++) {
371 if(v == 0) { /* 2nd priority: empty drive */
374 if(w->drive[i] != devnone) {
380 if(v->elem == e) { /* 1st priority: other side */
386 if(v->stime < t) { /* 3rd priority: by time */
396 if(v->status != Sstart) {
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)) {
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 */
428 panic("wormsize: w != %Z->private", d);
435 * set fixed size for whole Juke from
436 * size of first disc examined.
441 if(d->type == Devlworm)
442 return size-1; /* lie: last block is for label */
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.
451 devtojuke(Device *d, Device *top)
456 fprint(2, "devtojuke: type of device %Z of %Z unknown\n", d, top);
460 /* jackpot! d->private is a (Juke *) with nside, &c. */
465 /* squint hard & call an mlev or a mirr an mcat */
471 * d->private is a (Juke *) with nside, etc.,
472 * but we're not supposed to get here.
475 fprint(2, "devtojuke: (l)worm %Z of %Z encountered\n", d, top);
481 d = d->cw.w; /* usually juke */
484 d = d->ro.parent; /* cw */
502 return d->type == Devworm || d->type == Devlworm;
506 findside(Device *juke, int side, Device *top)
509 Device *mcat = juke->j.m, *x;
510 Juke *w = juke->private;
512 for (x = mcat->cat.first; x != nil; x = x->link) {
514 fprint(2, "wormsizeside: %Z of %Z of %Z type not (l)worm\n",
519 if (i < 0 || i >= w->nside)
520 panic("wormsizeside: side %d in %Z out of range",
527 if (w->side[i].time == 0) {
528 fprint(2, "wormsizeside: side %d not in jukebox %Z\n", i, juke);
535 int sleft; /* sides still to visit to reach desired side */
536 int starget; /* side of topdev we want */
538 int sawjuke; /* passed by a jukebox */
539 int sized; /* flag: asked wormsize for size of starget */
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.
548 visitsides(Device *d, Device *parentj, Visit *vp)
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.
559 d = devtojuke(d, vp->topdev);
560 if (d == nil || vp->sleft < 0)
562 if (d->type == Devjuke) { /* jackpot! d->private is a (Juke *) */
566 * if there aren't enough sides in this jukebox to reach
567 * the desired one, subtract these sides and pass.
569 if (vp->sleft >= w->nside) {
570 vp->sleft -= w->nside;
573 /* else this is the right juke, paw through mcat of sides */
574 return visitsides(d->j.m, d, vp);
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).
586 fprint(2, "visitsides: %Z of %Z: empty mcat\n", d, vp->topdev);
590 for (; x != nil && !vp->sized; x = x->link)
591 size = visitsides(x, parentj, vp);
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);
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);
609 /* we've turned vp->starget into the right Device* */
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.
623 wormsizeside(Device *d, int side)
628 memset(&visit, 0, sizeof visit);
629 visit.starget = visit.sleft = side;
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);
640 * returns starts (in blocks) of side #side and #(side+1) of dev in *stp.
641 * dev should be a Devcw.
644 wormsidestarts(Device *dev, int side, Sidestarts *stp)
649 for (dstart = s = 0; s < side; s++)
650 dstart += wormsizeside(dev, s);
651 stp->sstart = dstart;
652 stp->s1start = dstart + wormsizeside(dev, side);
656 wormread(Device *d, Off b, void *c)
661 Side *v = wormunit(d);
662 Juke *w = d->private;
666 panic("wormread: nil wormunit(%Z)", d);
667 dr = w->drive[v->drive];
669 panic("wormread: unopened fd for %Z", d);
670 max = (d->type == Devlworm? v->max + 1: v->max);
672 fprint(2, "wormread: block out of range %Z(%lld)\n", d, (Wideoff)b);
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);
686 wormwrite(Device *d, Off b, void *c)
691 Side *v = wormunit(d);
692 Juke *w = d->private;
696 panic("wormwrite: nil wormunit(%Z)", d);
697 dr = w->drive[v->drive];
699 panic("wormwrite: unopened fd for %Z", d);
700 max = (d->type == Devlworm? v->max + 1: v->max);
702 fprint(2, "wormwrite: block out of range %Z(%lld)\n", d, (Wideoff)b);
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);
716 mmove(Juke *w, int trans, int from, int to, int rot)
719 uchar cmd[12], buf[4];
720 static int recur = 0;
722 memset(cmd, 0, sizeof cmd);
723 cmd[0] = 0xa5; /* move medium */
732 s = scsiio(w->juke, SCSInone, cmd, sizeof cmd, buf, 0); /* mmove */
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);
740 fprint(2, "element from=%d\n", from);
742 fprint(2, "element to=%d\n", to);
744 fprint(2, "element trans=%d\n", trans);
757 uchar cmd[6], buf[4+20];
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 */
765 s = scsiio(w->juke, SCSIread, cmd, sizeof cmd, buf, sizeof buf); /* mode sense elem addrs */
767 panic("geometry #%x", s);
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];
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 */
784 s = scsiio(w->juke, SCSIread, cmd, sizeof cmd, buf, sizeof buf); /* mode sense geometry */
786 panic("geometry #%x", s);
788 w->rot = buf[4+2] & 1;
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);
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.
802 element(Juke *w, int e)
804 uchar cmd[12], buf[8+8+88];
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 */
812 cmd[5] = 1; /* number of elements */
813 cmd[9] = sizeof buf; /* allocation length */
815 s = scsiio(w->juke, SCSIread, cmd, sizeof cmd, buf, sizeof buf); /* read elem sts */
817 fprint(2, "scsiio #%x\n", s);
821 s = (buf[0]<<8) | buf[1];
823 fprint(2, "element = %d\n", s);
827 fprint(2, "number reported = %d\n", buf[3]);
830 s = (buf[8+8+0]<<8) | buf[8+8+1];
832 fprint(2, "element1 = %d\n", s);
836 switch(buf[8+0]) { /* element type */
838 fprint(2, "unknown element %d: %d\n", e, buf[8+0]);
840 case 1: /* transport */
842 if(s < 0 || s >= w->nmt)
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);
849 case 2: /* storage */
851 if(s < 0 || s >= w->nse)
853 w->side[s].status = Sempty;
855 w->side[s].status = Sunload;
857 w->side[w->nse+s].status = w->side[s].status;
859 case 3: /* import/export */
861 if(s < 0 || s >= w->nie)
864 fprint(2, "import/export %d #%.2x %d.%d\n", s,
866 (buf[8+8+10]<<8) | buf[8+8+11],
867 (buf[8+8+9]>>6) & 1);
869 case 4: /* data transfer */
871 if(s < 0 || s >= w->ndt)
874 fprint(2, "data transfer %d #%.2x %d.%d\n", s,
876 (buf[8+8+10]<<8) | buf[8+8+11],
877 (buf[8+8+9]>>6) & 1);
879 t = ((buf[8+8+10]<<8) | buf[8+8+11]) - w->se0;
880 if (t < 0 || t >= w->nse || t >= MAXSIDE ||
882 fprint(2, "element: juke %Z lies; claims side %d is in drive %d\n",
883 w->juke, t, s); /* lying sack of ... */
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.
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");
900 w->side[t].status = Sunload;
902 w->side[w->nse+t].status = Sunload;
905 fprint(2, "drive w%d has exception #%.2x #%.2x\n", s,
906 buf[8+8+4], buf[8+8+5]);
913 /* panic("element") */ ;
917 * read all elements' status from jukebox w, move any discs in drives back
918 * to their slots, and update and print software status.
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);
936 for(i=0; i<w->nse; i++)
937 if(w->side[i].status == Sempty) {
944 print("\tshelves r%d-", i);
953 jinit(Juke *w, Device *d, int o)
960 fprint(2, "juke platter not (devmcat of) dev(l)worm: %Z\n", d);
961 panic("jinit: type");
965 * we don't call mcatinit(d) here, so we have to set d->cat.ndev
968 for(d=d->cat.first; d; d=d->link)
975 if(p < 0 || p >= w->nside)
976 panic("jinit partition %Z", d);
981 fprint(2, "juke platter private pointer set %p\n",
983 panic("jinit: private");
997 i = number(arg, -1, 10) - 1;
999 if(i < 0 || i >= w->nside) {
1000 fprint(2, "bad unit number %s (%d)\n", arg, i+1);
1009 if(v->status == Sstart) {
1010 if(mmove(w, w->mt0, w->dt0+v->drive, v->elem, v->rot)) {
1014 v->status = Sunload;
1022 if(v->status == Sstart) {
1023 if(mmove(w, w->mt0, w->dt0+v->drive, v->elem, v->rot)) {
1027 v->status = Sunload;
1037 cmd_wormoffline(int argc, char *argv[])
1043 print("usage: wormoffline drive\n");
1046 u = number(argv[1], -1, 10);
1048 if(u < 0 || u >= w->ndrive) {
1049 fprint(2, "bad drive %s (0<=%d<%d)\n", argv[1], u, w->ndrive);
1053 print("drive %d already offline\n", u);
1055 for(i=0; i<w->ndrive; i++)
1056 if(w->offline[i] == 0)
1058 print("that would take all drives offline\n");
1063 cmd_wormonline(int argc, char *argv[])
1069 print("usage: wormonline drive\n");
1072 u = number(argv[1], -1, 10);
1074 if(u < 0 || u >= w->ndrive) {
1075 print("bad drive %s (0<=%d<%d)\n", argv[1], u, w->ndrive);
1078 if(w->offline[u] == 0)
1079 print("drive %d already online\n", u);
1084 cmd_wormreset(int, char *[])
1088 for(w=jukelist; w; w=w->link) {
1096 cmd_wormeject(int argc, char *argv[])
1102 print("usage: wormeject unit\n");
1109 mmove(w, w->mt0, v->elem, w->ie0, 0);
1114 cmd_wormingest(int argc, char *argv[])
1120 print("usage: wormingest unit\n");
1127 mmove(w, w->mt0, w->ie0, v->elem, 0);
1132 newside(Side *v, int rot, int elem)
1136 // v->name = "shelf";
1140 v->time = toytime();
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.
1149 querychanger(Device *xdev)
1156 panic("querychanger: nil Device");
1157 if(xdev->type != Devwren) {
1158 fprint(2, "juke changer not wren %Z\n", xdev);
1161 for(w=jukelist; w; w=w->link)
1166 * allocate a juke structure
1167 * no locking problems.
1169 w = ialloc(sizeof(Juke), 0);
1170 w->magic = Jukemagic;
1171 w->isfixedsize = FIXEDSIZE;
1176 fprint(2, "alloc juke %Z\n", xdev);
1180 // w->name = "juke";
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);
1194 w->nside += w->nside;
1195 if(w->nside > MAXSIDE) {
1196 fprint(2, "too many sides: %d max %d\n", w->nside, MAXSIDE);
1199 for(i=0; i < w->nse; i++) {
1201 newside(v, 0, w->se0 + i);
1203 newside(v + w->nse, 1, w->se0 + i);
1208 if(w->ndrive > MAXDRIVE) {
1210 fprint(2, "ndrives truncated to %d\n", MAXDRIVE);
1211 w->ndrive = MAXDRIVE;
1215 * pick up each drive
1217 for(i=0; i<w->ndrive; i++)
1218 w->drive[i] = devnone;
1221 panic("querychanger: %Z", xdev);
1231 static int beenhere = 0;
1233 /* j(w<changer>w<station0>...)(r<platters>) */
1235 panic("jukeinit: nil Device");
1237 if(xdev == nil || xdev->type != Devmcat) {
1238 fprint(2, "juke union not mcat\n");
1243 * pick up the changer device
1245 xdev = xdev->cat.first;
1246 w = querychanger(xdev);
1250 cmd_install("wormreset",
1251 "-- put drives back where jukebox thinks they belong",
1253 cmd_install("wormeject", "unit -- shelf to outside",
1255 cmd_install("wormingest", "unit -- outside to shelf",
1257 cmd_install("wormoffline", "unit -- disable drive",
1259 cmd_install("wormonline", "unit -- enable drive",
1263 /* walk through the worm drives */
1265 while(xdev = xdev->link) {
1266 if(xdev->type != Devwren) {
1267 fprint(2, "drive not devwren: %Z\n", xdev);
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);
1276 if(i >= w->ndrive) {
1277 fprint(2, "too many drives %Z\n", xdev);
1280 w->drive[i++] = xdev;
1284 fprint(2, "no drives\n");
1289 * put w pointer in each platter
1292 jinit(w, d->j.m, 0);
1301 * called periodically
1311 t = toytime() - TWORM;
1312 for(w=jukelist; w; w=w->link) {
1313 if(w->probeok == 0 || !canqlock(w))
1315 for(i=0; i<w->nside; i++) {
1319 if(v->status == Sstart && t > v->time) {
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;