Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/planix-v0/sys/src/cmd/cwfs/juke.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
		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*
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*
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
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
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
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 *
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
498
devisside(Device *d)
499
{
500
	return d->type == Devworm || d->type == Devlworm;
501
}
502
 
503
static Device *
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
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
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
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
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
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
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
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
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
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
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*
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
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
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
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
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
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
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 *
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
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
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
}