BSD 4_3_Net_2 release
[unix-history] / usr / src / sys / hp300 / dev / cd.c
CommitLineData
60f56dfc
KM
1/*
2 * Copyright (c) 1988 University of Utah.
3 * Copyright (c) 1990 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * the Systems Programming Group of the University of Utah Computer
8 * Science Department.
9 *
af359dea
C
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
60f56dfc 25 *
af359dea
C
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
60f56dfc 37 *
af359dea
C
38 * from: Utah $Hdr: cd.c 1.6 90/11/28$
39 *
40 * @(#)cd.c 7.4 (Berkeley) 5/7/91
60f56dfc
KM
41 */
42
43/*
44 * "Concatenated" disk driver.
45 */
46#include "cd.h"
47#if NCD > 0
48
b28b3a13
KB
49#include "sys/param.h"
50#include "sys/systm.h"
51#include "sys/errno.h"
52#include "sys/dkstat.h"
53#include "sys/buf.h"
54#include "sys/malloc.h"
55#include "sys/conf.h"
60f56dfc
KM
56
57#include "cdvar.h"
58
59#ifdef DEBUG
60int cddebug = 0x00;
61#define CDB_FOLLOW 0x01
62#define CDB_INIT 0x02
63#define CDB_IO 0x04
64#endif
65
66struct buf cdbuf[NCD];
67struct buf *cdbuffer();
af359dea 68char *cddevtostr();
60f56dfc
KM
69int cdiodone();
70
71#define cdunit(x) ((minor(x) >> 3) & 0x7) /* for consistency */
72
73#define getcbuf() \
74 ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK))
75#define putcbuf(bp) \
76 free((caddr_t)(bp), M_DEVBUF)
77
78struct cd_softc {
79 int sc_flags; /* flags */
80 size_t sc_size; /* size of cd */
81 int sc_ileave; /* interleave */
82 int sc_ncdisks; /* number of components */
83 struct cdcinfo sc_cinfo[NCDISKS]; /* component info */
84 struct cdiinfo *sc_itable; /* interleave table */
85 int sc_usecnt; /* number of requests active */
86 struct buf *sc_bp; /* "current" request */
87 int sc_dk; /* disk index */
88} cd_softc[NCD];
89
90/* sc_flags */
91#define CDF_ALIVE 0x01
92#define CDF_INITED 0x02
93
94cdinit(cd)
95 struct cddevice *cd;
96{
97 register struct cd_softc *cs = &cd_softc[cd->cd_unit];
98 register struct cdcinfo *ci;
99 register size_t size;
100 register int ix;
101 size_t minsize;
102 dev_t dev;
103
104#ifdef DEBUG
105 if (cddebug & (CDB_FOLLOW|CDB_INIT))
106 printf("cdinit: unit %d\n", cd->cd_unit);
107#endif
108 cs->sc_dk = cd->cd_dk;
109 cs->sc_size = 0;
110 cs->sc_ileave = cd->cd_interleave;
111 cs->sc_ncdisks = 0;
112 /*
113 * Verify that each component piece exists and record
114 * relevant information about it.
115 */
116 minsize = 0;
117 for (ix = 0; ix < NCDISKS; ix++) {
118 if ((dev = cd->cd_dev[ix]) == NODEV)
119 break;
120 ci = &cs->sc_cinfo[ix];
121 ci->ci_dev = dev;
122 /*
123 * Calculate size (truncated to interleave boundary
124 * if necessary.
125 */
126 if (bdevsw[major(dev)].d_psize) {
af359dea
C
127 size = (size_t) (*bdevsw[major(dev)].d_psize)(dev);
128 if ((int)size < 0)
60f56dfc
KM
129 size = 0;
130 } else
131 size = 0;
132 if (cs->sc_ileave > 1)
133 size -= size % cs->sc_ileave;
af359dea
C
134 if (size == 0) {
135 printf("cd%d: not configured (component %s missing)\n",
136 cd->cd_unit, cddevtostr(ci->ci_dev));
60f56dfc 137 return(0);
af359dea 138 }
60f56dfc
KM
139 if (minsize == 0 || size < minsize)
140 minsize = size;
141 ci->ci_size = size;
142 cs->sc_size += size;
143 cs->sc_ncdisks++;
144 }
145 /*
146 * If uniform interleave is desired set all sizes to that of
147 * the smallest component.
148 */
149 if (cd->cd_flags & CDF_UNIFORM) {
150 for (ci = cs->sc_cinfo;
151 ci < &cs->sc_cinfo[cs->sc_ncdisks]; ci++)
152 ci->ci_size = minsize;
153 cs->sc_size = cs->sc_ncdisks * minsize;
154 }
155 /*
156 * Construct the interleave table
157 */
158 if (!cdinterleave(cs))
159 return(0);
160 if (cd->cd_dk >= 0)
161 dk_wpms[cd->cd_dk] = 32 * (60 * DEV_BSIZE / 2); /* XXX */
af359dea
C
162 printf("cd%d: %d components ", cd->cd_unit, cs->sc_ncdisks);
163 for (ix = 0; ix < cs->sc_ncdisks; ix++)
164 printf("%c%s%c",
165 ix == 0 ? '(' : ' ',
166 cddevtostr(cs->sc_cinfo[ix].ci_dev),
167 ix == cs->sc_ncdisks - 1 ? ')' : ',');
168 printf(", %d blocks ", cs->sc_size);
60f56dfc 169 if (cs->sc_ileave)
af359dea 170 printf("interleaved at %d blocks\n", cs->sc_ileave);
60f56dfc 171 else
af359dea 172 printf("concatenated\n");
60f56dfc
KM
173 cs->sc_flags = CDF_ALIVE | CDF_INITED;
174 return(1);
175}
176
af359dea
C
177/*
178 * XXX not really cd specific.
179 */
180char *
181cddevtostr(dev)
182 dev_t dev;
183{
184 static char dbuf[5];
185
186 dbuf[1] = 'd';
187 switch (major(dev)) {
188 case 2:
189 dbuf[0] = 'r';
190 break;
191 case 4:
192 dbuf[0] = 's';
193 break;
194 case 5:
195 dbuf[0] = 'c';
196 break;
197 default:
198 dbuf[0] = dbuf[1] = '?';
199 break;
200 }
201 dbuf[2] = (minor(dev) >> 3) + '0';
202 dbuf[3] = (minor(dev) & 7) + 'a';
203 dbuf[4] = '\0';
204 return (dbuf);
205}
206
60f56dfc
KM
207cdinterleave(cs)
208 register struct cd_softc *cs;
209{
210 register struct cdcinfo *ci, *smallci;
211 register struct cdiinfo *ii;
212 register daddr_t bn, lbn;
213 register int ix;
214 u_long size;
215
216#ifdef DEBUG
217 if (cddebug & CDB_INIT)
218 printf("cdinterleave(%x): ileave %d\n", cs, cs->sc_ileave);
219#endif
220 /*
221 * Allocate an interleave table.
222 * Chances are this is too big, but we don't care.
223 */
224 size = (cs->sc_ncdisks + 1) * sizeof(struct cdiinfo);
225 cs->sc_itable = (struct cdiinfo *)malloc(size, M_DEVBUF, M_WAITOK);
226 bzero((caddr_t)cs->sc_itable, size);
227 /*
228 * Trivial case: no interleave (actually interleave of disk size).
229 * Each table entry represent a single component in its entirety.
230 */
231 if (cs->sc_ileave == 0) {
232 bn = 0;
233 ii = cs->sc_itable;
234 for (ix = 0; ix < cs->sc_ncdisks; ix++) {
235 ii->ii_ndisk = 1;
236 ii->ii_startblk = bn;
237 ii->ii_startoff = 0;
238 ii->ii_index[0] = ix;
239 bn += cs->sc_cinfo[ix].ci_size;
240 ii++;
241 }
242 ii->ii_ndisk = 0;
243#ifdef DEBUG
244 if (cddebug & CDB_INIT)
245 printiinfo(cs->sc_itable);
246#endif
247 return(1);
248 }
249 /*
250 * The following isn't fast or pretty; it doesn't have to be.
251 */
252 size = 0;
253 bn = lbn = 0;
254 for (ii = cs->sc_itable; ; ii++) {
255 /*
256 * Locate the smallest of the remaining components
257 */
258 smallci = NULL;
259 for (ci = cs->sc_cinfo;
260 ci < &cs->sc_cinfo[cs->sc_ncdisks]; ci++)
261 if (ci->ci_size > size &&
262 (smallci == NULL ||
263 ci->ci_size < smallci->ci_size))
264 smallci = ci;
265 /*
266 * Nobody left, all done
267 */
268 if (smallci == NULL) {
269 ii->ii_ndisk = 0;
270 break;
271 }
272 /*
273 * Record starting logical block and component offset
274 */
275 ii->ii_startblk = bn / cs->sc_ileave;
276 ii->ii_startoff = lbn;
277 /*
278 * Determine how many disks take part in this interleave
279 * and record their indices.
280 */
281 ix = 0;
282 for (ci = cs->sc_cinfo;
283 ci < &cs->sc_cinfo[cs->sc_ncdisks]; ci++)
284 if (ci->ci_size >= smallci->ci_size)
285 ii->ii_index[ix++] = ci - cs->sc_cinfo;
286 ii->ii_ndisk = ix;
287 bn += ix * (smallci->ci_size - size);
288 lbn = smallci->ci_size / cs->sc_ileave;
289 size = smallci->ci_size;
290 }
291#ifdef DEBUG
292 if (cddebug & CDB_INIT)
293 printiinfo(cs->sc_itable);
294#endif
295 return(1);
296}
297
298#ifdef DEBUG
299printiinfo(ii)
300 struct cdiinfo *ii;
301{
302 register int ix, i;
303
304 for (ix = 0; ii->ii_ndisk; ix++, ii++) {
305 printf(" itab[%d]: #dk %d sblk %d soff %d",
306 ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff);
307 for (i = 0; i < ii->ii_ndisk; i++)
308 printf(" %d", ii->ii_index[i]);
309 printf("\n");
310 }
311}
312#endif
313
314cdopen(dev, flags)
315 dev_t dev;
316{
317 int unit = cdunit(dev);
318 register struct cd_softc *cs = &cd_softc[unit];
319
320#ifdef DEBUG
321 if (cddebug & CDB_FOLLOW)
322 printf("cdopen(%x, %x)\n", dev, flags);
323#endif
324 if (unit >= NCD || (cs->sc_flags & CDF_ALIVE) == 0)
325 return(ENXIO);
326 return(0);
327}
328
329cdstrategy(bp)
330 register struct buf *bp;
331{
332 register int unit = cdunit(bp->b_dev);
333 register struct cd_softc *cs = &cd_softc[unit];
0e1872ad
KM
334 register daddr_t bn;
335 register int sz, s;
60f56dfc
KM
336
337#ifdef DEBUG
338 if (cddebug & CDB_FOLLOW)
339 printf("cdstrategy(%x): unit %d\n", bp, unit);
340#endif
341 if ((cs->sc_flags & CDF_INITED) == 0) {
342 bp->b_error = ENXIO;
0e1872ad
KM
343 bp->b_flags |= B_ERROR;
344 goto done;
60f56dfc
KM
345 }
346 bn = bp->b_blkno;
0e1872ad 347 sz = howmany(bp->b_bcount, DEV_BSIZE);
60f56dfc 348 if (bn < 0 || bn + sz > cs->sc_size) {
0e1872ad
KM
349 sz = cs->sc_size - bn;
350 if (sz == 0) {
351 bp->b_resid = bp->b_bcount;
352 goto done;
353 }
354 if (sz < 0) {
355 bp->b_error = EINVAL;
356 bp->b_flags |= B_ERROR;
60f56dfc 357 goto done;
0e1872ad
KM
358 }
359 bp->b_bcount = dbtob(sz);
60f56dfc 360 }
0e1872ad 361 bp->b_resid = bp->b_bcount;
60f56dfc
KM
362 /*
363 * "Start" the unit.
364 * XXX: the use of sc_bp is just to retain the "traditional"
365 * interface to the start routine.
366 */
367 s = splbio();
368 cs->sc_bp = bp;
369 cdstart(unit);
370 splx(s);
371 return;
60f56dfc 372done:
0e1872ad 373 biodone(bp);
60f56dfc
KM
374}
375
376cdstart(unit)
377 int unit;
378{
379 register struct cd_softc *cs = &cd_softc[unit];
380 register struct buf *bp = cs->sc_bp;
381 register long bcount, rcount;
382 struct buf *cbp;
383 caddr_t addr;
384 daddr_t bn;
385
386#ifdef DEBUG
387 if (cddebug & CDB_FOLLOW)
388 printf("cdstart(%d)\n", unit);
389#endif
390 /*
391 * Instumentation (not real meaningful)
392 */
393 cs->sc_usecnt++;
394 if (cs->sc_dk >= 0) {
395 dk_busy |= 1 << cs->sc_dk;
396 dk_xfer[cs->sc_dk]++;
397 dk_wds[cs->sc_dk] += bp->b_bcount >> 6;
398 }
399 /*
400 * Allocate component buffers and fire off the requests
401 */
402 bn = bp->b_blkno;
403 addr = bp->b_un.b_addr;
404 for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) {
405 cbp = cdbuffer(cs, bp, bn, addr, bcount);
406 rcount = cbp->b_bcount;
407 (*bdevsw[major(cbp->b_dev)].d_strategy)(cbp);
408 bn += btodb(rcount);
409 addr += rcount;
410 }
411}
412
413/*
414 * Build a component buffer header.
415 */
416struct buf *
417cdbuffer(cs, bp, bn, addr, bcount)
418 register struct cd_softc *cs;
419 struct buf *bp;
420 daddr_t bn;
421 caddr_t addr;
422 long bcount;
423{
424 register struct cdcinfo *ci;
425 register struct buf *cbp;
426 register daddr_t cbn, cboff;
427
428#ifdef DEBUG
429 if (cddebug & CDB_IO)
430 printf("cdbuffer(%x, %x, %d, %x, %d)\n",
431 cs, bp, bn, addr, bcount);
432#endif
433 /*
434 * Determine which component bn falls in.
435 */
436 cbn = bn;
437 cboff = 0;
438 /*
439 * Serially concatenated
440 */
441 if (cs->sc_ileave == 0) {
442 register daddr_t sblk;
443
444 sblk = 0;
445 for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++)
446 sblk += ci->ci_size;
447 cbn -= sblk;
448 }
449 /*
450 * Interleaved
451 */
452 else {
453 register struct cdiinfo *ii;
454 int cdisk, off;
455
456 cboff = cbn % cs->sc_ileave;
457 cbn /= cs->sc_ileave;
458 for (ii = cs->sc_itable; ii->ii_ndisk; ii++)
459 if (ii->ii_startblk > cbn)
460 break;
461 ii--;
462 off = cbn - ii->ii_startblk;
463 if (ii->ii_ndisk == 1) {
464 cdisk = ii->ii_index[0];
465 cbn = ii->ii_startoff + off;
466 } else {
467 cdisk = ii->ii_index[off % ii->ii_ndisk];
468 cbn = ii->ii_startoff + off / ii->ii_ndisk;
469 }
470 cbn *= cs->sc_ileave;
471 ci = &cs->sc_cinfo[cdisk];
472 }
473 /*
474 * Fill in the component buf structure.
475 */
476 cbp = getcbuf();
477 cbp->b_flags = bp->b_flags | B_CALL;
478 cbp->b_iodone = cdiodone;
479 cbp->b_proc = bp->b_proc;
480 cbp->b_dev = ci->ci_dev;
481 cbp->b_blkno = cbn + cboff;
482 cbp->b_un.b_addr = addr;
0e1872ad 483 cbp->b_vp = 0;
60f56dfc
KM
484 if (cs->sc_ileave == 0)
485 cbp->b_bcount = dbtob(ci->ci_size - cbn);
486 else
487 cbp->b_bcount = dbtob(cs->sc_ileave - cboff);
488 if (cbp->b_bcount > bcount)
489 cbp->b_bcount = bcount;
490 /*
491 * XXX: context for cdiodone
492 */
0e1872ad 493 cbp->b_saveaddr = (caddr_t)bp;
60f56dfc
KM
494 cbp->b_pfcent = ((cs - cd_softc) << 16) | (ci - cs->sc_cinfo);
495#ifdef DEBUG
496 if (cddebug & CDB_IO)
497 printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n",
498 ci->ci_dev, ci-cs->sc_cinfo, cbp, cbp->b_blkno,
499 cbp->b_un.b_addr, cbp->b_bcount);
500#endif
501 return(cbp);
502}
503
504cdintr(unit)
505 int unit;
506{
507 register struct cd_softc *cs = &cd_softc[unit];
508 register struct buf *bp = cs->sc_bp;
509
510#ifdef DEBUG
511 if (cddebug & CDB_FOLLOW)
0e1872ad 512 printf("cdintr(%d): bp %x\n", unit, bp);
60f56dfc
KM
513#endif
514 /*
515 * Request is done for better or worse, wakeup the top half.
516 */
517 if (--cs->sc_usecnt == 0 && cs->sc_dk >= 0)
518 dk_busy &= ~(1 << cs->sc_dk);
519 if (bp->b_flags & B_ERROR)
520 bp->b_resid = bp->b_bcount;
0e1872ad 521 biodone(bp);
60f56dfc
KM
522}
523
524/*
0e1872ad 525 * Called by biodone at interrupt time.
60f56dfc
KM
526 * Mark the component as done and if all components are done,
527 * take a cd interrupt.
528 */
529cdiodone(cbp)
530 register struct buf *cbp;
531{
0e1872ad 532 register struct buf *bp = (struct buf *)cbp->b_saveaddr;/* XXX */
60f56dfc
KM
533 register int unit = (cbp->b_pfcent >> 16) & 0xFFFF; /* XXX */
534 int count, s;
535
536 s = splbio();
537#ifdef DEBUG
538 if (cddebug & CDB_FOLLOW)
539 printf("cdiodone(%x)\n", cbp);
540 if (cddebug & CDB_IO) {
541 printf("cdiodone: bp %x bcount %d resid %d\n",
542 bp, bp->b_bcount, bp->b_resid);
543 printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n",
544 cbp->b_dev, cbp->b_pfcent & 0xFFFF, cbp,
545 cbp->b_blkno, cbp->b_un.b_addr, cbp->b_bcount);
546 }
547#endif
548
549 if (cbp->b_flags & B_ERROR) {
550 bp->b_flags |= B_ERROR;
0e1872ad 551 bp->b_error = biowait(cbp);
60f56dfc
KM
552#ifdef DEBUG
553 printf("cd%d: error %d on component %d\n",
554 unit, bp->b_error, cbp->b_pfcent & 0xFFFF);
555#endif
556 }
557 count = cbp->b_bcount;
558 putcbuf(cbp);
559
560 /*
561 * If all done, "interrupt".
562 * Again, sc_bp is only used to preserve the traditional interface.
563 */
564 bp->b_resid -= count;
565 if (bp->b_resid < 0)
566 panic("cdiodone: count");
567 if (bp->b_resid == 0) {
568 cd_softc[unit].sc_bp = bp;
569 cdintr(unit);
570 }
571 splx(s);
572}
573
574cdread(dev, uio)
575 dev_t dev;
576 struct uio *uio;
577{
578 register int unit = cdunit(dev);
579
580#ifdef DEBUG
581 if (cddebug & CDB_FOLLOW)
582 printf("cdread(%x, %x)\n", dev, uio);
583#endif
584 return(physio(cdstrategy, &cdbuf[unit], dev, B_READ, minphys, uio));
585}
586
587cdwrite(dev, uio)
588 dev_t dev;
589 struct uio *uio;
590{
591 register int unit = cdunit(dev);
592
593#ifdef DEBUG
594 if (cddebug & CDB_FOLLOW)
595 printf("cdwrite(%x, %x)\n", dev, uio);
596#endif
597 return(physio(cdstrategy, &cdbuf[unit], dev, B_WRITE, minphys, uio));
598}
599
600cdioctl(dev, cmd, data, flag)
601 dev_t dev;
602 int cmd;
603 caddr_t data;
604 int flag;
605{
606 return(EINVAL);
607}
608
609cdsize(dev)
610 dev_t dev;
611{
612 int unit = cdunit(dev);
613 register struct cd_softc *cs = &cd_softc[unit];
614
615 if (unit >= NCD || (cs->sc_flags & CDF_INITED) == 0)
616 return(-1);
617 return(cs->sc_size);
618}
619
620cddump(dev)
621{
622 return(ENXIO);
623}
624#endif