ccd.c revision 1.6 1 /* $NetBSD: ccd.c,v 1.6 1995/03/02 06:38:11 cgd Exp $ */
2
3 /*
4 * Copyright (c) 1988 University of Utah.
5 * Copyright (c) 1990, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * the Systems Programming Group of the University of Utah Computer
10 * Science Department.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 * from: Utah $Hdr: cd.c 1.6 90/11/28$
41 *
42 * @(#)cd.c 8.2 (Berkeley) 11/16/93
43 */
44
45 /*
46 * "Concatenated" disk driver.
47 */
48 #include "ccd.h"
49 #if NCCD > 0
50
51 #include <sys/param.h>
52 #include <sys/systm.h>
53 #include <sys/proc.h>
54 #include <sys/errno.h>
55 #include <sys/dkstat.h>
56 #include <sys/buf.h>
57 #include <sys/malloc.h>
58 #include <sys/conf.h>
59 #include <sys/stat.h>
60 #ifdef COMPAT_NOLABEL
61 #include <sys/ioctl.h>
62 #include <sys/disklabel.h>
63 #include <sys/fcntl.h>
64 #endif
65
66 #include <dev/ccdvar.h>
67
68 #ifdef DEBUG
69 int ccddebug = 0x00;
70 #define CCDB_FOLLOW 0x01
71 #define CCDB_INIT 0x02
72 #define CCDB_IO 0x04
73 #endif
74
75 #define ccdunit(x) DISKUNIT(x)
76
77 struct ccdbuf {
78 struct buf cb_buf; /* new I/O buf */
79 struct buf *cb_obp; /* ptr. to original I/O buf */
80 int cb_unit; /* target unit */
81 int cb_comp; /* target component */
82 };
83
84 #define getccdbuf() \
85 ((struct ccdbuf *)malloc(sizeof(struct ccdbuf), M_DEVBUF, M_WAITOK))
86 #define putccdbuf(cbp) \
87 free((caddr_t)(cbp), M_DEVBUF)
88
89 struct ccd_softc {
90 int sc_flags; /* flags */
91 size_t sc_size; /* size of ccd */
92 int sc_ileave; /* interleave */
93 int sc_nccdisks; /* number of components */
94 struct ccdcinfo sc_cinfo[NCCDISKS]; /* component info */
95 struct ccdiinfo *sc_itable; /* interleave table */
96 int sc_usecnt; /* number of requests active */
97 int sc_dk; /* disk index */
98 };
99
100 struct ccdbuf *ccdbuffer __P((struct ccd_softc *cs, struct buf *bp,
101 daddr_t bn, caddr_t addr, long bcount));
102 char *ccddevtostr __P((dev_t));
103 void ccdiodone __P((struct ccdbuf *cbp));
104
105 /* sc_flags */
106 #define CCDF_ALIVE 0x01
107 #define CCDF_INITED 0x02
108
109 struct ccd_softc *ccd_softc;
110 int numccd;
111
112 /*
113 * Since this is called after auto-configuration of devices,
114 * we can handle the initialization here.
115 *
116 * XXX this will not work if you want to use a ccd as your primary
117 * swap device since swapconf() has been called before now.
118 */
119 void
120 ccdattach(num)
121 int num;
122 {
123 char *mem;
124 register u_long size;
125 register struct ccddevice *ccd;
126 extern int dkn;
127
128 if (num <= 0)
129 return;
130 size = num * sizeof(struct ccd_softc);
131 mem = malloc(size, M_DEVBUF, M_NOWAIT);
132 if (mem == NULL) {
133 printf("WARNING: no memory for concatonated disks\n");
134 return;
135 }
136 bzero(mem, size);
137 ccd_softc = (struct ccd_softc *)mem;
138 numccd = num;
139 for (ccd = ccddevice; ccd->ccd_unit >= 0; ccd++) {
140 /*
141 * XXX
142 * Assign disk index first so that init routine
143 * can use it (saves having the driver drag around
144 * the ccddevice pointer just to set up the dk_*
145 * info in the open routine).
146 */
147 if (dkn < DK_NDRIVE)
148 ccd->ccd_dk = dkn++;
149 else
150 ccd->ccd_dk = -1;
151 if (ccdinit(ccd))
152 printf("ccd%d configured\n", ccd->ccd_unit);
153 else if (ccd->ccd_dk >= 0) {
154 ccd->ccd_dk = -1;
155 dkn--;
156 }
157 }
158 }
159
160 ccdinit(ccd)
161 struct ccddevice *ccd;
162 {
163 register struct ccd_softc *cs = &ccd_softc[ccd->ccd_unit];
164 register struct ccdcinfo *ci;
165 register size_t size;
166 register int ix;
167 size_t minsize;
168 dev_t dev;
169 struct bdevsw *bsw;
170 int error;
171 struct proc *p = curproc; /* XXX */
172
173 #ifdef DEBUG
174 if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
175 printf("ccdinit: unit %d\n", ccd->ccd_unit);
176 #endif
177 cs->sc_dk = ccd->ccd_dk;
178 cs->sc_size = 0;
179 cs->sc_ileave = ccd->ccd_interleave;
180 cs->sc_nccdisks = 0;
181 /*
182 * Verify that each component piece exists and record
183 * relevant information about it.
184 */
185 minsize = 0;
186 for (ix = 0; ix < NCCDISKS; ix++) {
187 if ((dev = ccd->ccd_dev[ix]) == NODEV)
188 break;
189 ci = &cs->sc_cinfo[ix];
190 ci->ci_dev = dev;
191 bsw = &bdevsw[major(dev)];
192 /*
193 * Open the partition
194 */
195 if (bsw->d_open &&
196 (error = (*bsw->d_open)(dev, 0, S_IFBLK, p))) {
197 printf("ccd%d: component %s open failed, error = %d\n",
198 ccd->ccd_unit, ccddevtostr(dev), error);
199 return(0);
200 }
201 /*
202 * Calculate size (truncated to interleave boundary
203 * if necessary.
204 */
205 if (bsw->d_psize) {
206 size = (size_t) (*bsw->d_psize)(dev);
207 if ((int)size < 0)
208 size = 0;
209 } else
210 size = 0;
211 if (cs->sc_ileave > 1)
212 size -= size % cs->sc_ileave;
213 if (size == 0) {
214 printf("ccd%d: not configured (component %s missing)\n",
215 ccd->ccd_unit, ccddevtostr(dev));
216 return(0);
217 }
218 #ifdef COMPAT_NOLABEL
219 /*
220 * XXX if this is a 'c' partition then we need to mark the
221 * label area writeable since there cannot be a label.
222 */
223 if ((minor(dev) & 7) == 2 && bsw->d_open) {
224 int i, flag;
225
226 for (i = 0; i < nchrdev; i++)
227 if (cdevsw[i].d_open == bsw->d_open)
228 break;
229 if (i != nchrdev && cdevsw[i].d_ioctl) {
230 flag = 1;
231 (void)(*cdevsw[i].d_ioctl)(dev, DIOCWLABEL,
232 (caddr_t)&flag, FWRITE, p);
233 }
234 }
235 #endif
236 if (minsize == 0 || size < minsize)
237 minsize = size;
238 ci->ci_size = size;
239 cs->sc_size += size;
240 cs->sc_nccdisks++;
241 }
242 /*
243 * If uniform interleave is desired set all sizes to that of
244 * the smallest component.
245 */
246 if (ccd->ccd_flags & CCDF_UNIFORM) {
247 for (ci = cs->sc_cinfo;
248 ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
249 ci->ci_size = minsize;
250 cs->sc_size = cs->sc_nccdisks * minsize;
251 }
252 /*
253 * Construct the interleave table
254 */
255 if (!ccdinterleave(cs))
256 return(0);
257 if (ccd->ccd_dk >= 0)
258 dk_wpms[ccd->ccd_dk] = 32 * (60 * DEV_BSIZE / 2); /* XXX */
259 printf("ccd%d: %d components ", ccd->ccd_unit, cs->sc_nccdisks);
260 for (ix = 0; ix < cs->sc_nccdisks; ix++)
261 printf("%c%s%c",
262 ix == 0 ? '(' : ' ',
263 ccddevtostr(cs->sc_cinfo[ix].ci_dev),
264 ix == cs->sc_nccdisks - 1 ? ')' : ',');
265 printf(", %d blocks ", cs->sc_size);
266 if (cs->sc_ileave)
267 printf("interleaved at %d blocks\n", cs->sc_ileave);
268 else
269 printf("concatenated\n");
270 cs->sc_flags = CCDF_ALIVE | CCDF_INITED;
271 return(1);
272 }
273
274 /*
275 * XXX not really ccd specific.
276 * Could be called something like bdevtostr in machine/conf.c.
277 */
278 char *
279 ccddevtostr(dev)
280 dev_t dev;
281 {
282 static char dbuf[5];
283
284 switch (major(dev)) {
285 #ifdef hp300
286 case 2:
287 dbuf[0] = 'r'; dbuf[1] = 'd';
288 break;
289 case 4:
290 dbuf[0] = 's'; dbuf[1] = 'd';
291 break;
292 case 5:
293 dbuf[0] = 'c'; dbuf[1] = 'd';
294 break;
295 case 6:
296 dbuf[0] = 'v'; dbuf[1] = 'n';
297 break;
298 #endif
299 default:
300 dbuf[0] = dbuf[1] = '?';
301 break;
302 }
303 dbuf[2] = (minor(dev) >> 3) + '0';
304 dbuf[3] = (minor(dev) & 7) + 'a';
305 dbuf[4] = '\0';
306 return (dbuf);
307 }
308
309 ccdinterleave(cs)
310 register struct ccd_softc *cs;
311 {
312 register struct ccdcinfo *ci, *smallci;
313 register struct ccdiinfo *ii;
314 register daddr_t bn, lbn;
315 register int ix;
316 u_long size;
317
318 #ifdef DEBUG
319 if (ccddebug & CCDB_INIT)
320 printf("ccdinterleave(%x): ileave %d\n", cs, cs->sc_ileave);
321 #endif
322 /*
323 * Allocate an interleave table.
324 * Chances are this is too big, but we don't care.
325 */
326 size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo);
327 cs->sc_itable = (struct ccdiinfo *)malloc(size, M_DEVBUF, M_WAITOK);
328 bzero((caddr_t)cs->sc_itable, size);
329 /*
330 * Trivial case: no interleave (actually interleave of disk size).
331 * Each table entry represent a single component in its entirety.
332 */
333 if (cs->sc_ileave == 0) {
334 bn = 0;
335 ii = cs->sc_itable;
336 for (ix = 0; ix < cs->sc_nccdisks; ix++) {
337 ii->ii_ndisk = 1;
338 ii->ii_startblk = bn;
339 ii->ii_startoff = 0;
340 ii->ii_index[0] = ix;
341 bn += cs->sc_cinfo[ix].ci_size;
342 ii++;
343 }
344 ii->ii_ndisk = 0;
345 #ifdef DEBUG
346 if (ccddebug & CCDB_INIT)
347 printiinfo(cs->sc_itable);
348 #endif
349 return(1);
350 }
351 /*
352 * The following isn't fast or pretty; it doesn't have to be.
353 */
354 size = 0;
355 bn = lbn = 0;
356 for (ii = cs->sc_itable; ; ii++) {
357 /*
358 * Locate the smallest of the remaining components
359 */
360 smallci = NULL;
361 for (ci = cs->sc_cinfo;
362 ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
363 if (ci->ci_size > size &&
364 (smallci == NULL ||
365 ci->ci_size < smallci->ci_size))
366 smallci = ci;
367 /*
368 * Nobody left, all done
369 */
370 if (smallci == NULL) {
371 ii->ii_ndisk = 0;
372 break;
373 }
374 /*
375 * Record starting logical block and component offset
376 */
377 ii->ii_startblk = bn / cs->sc_ileave;
378 ii->ii_startoff = lbn;
379 /*
380 * Determine how many disks take part in this interleave
381 * and record their indices.
382 */
383 ix = 0;
384 for (ci = cs->sc_cinfo;
385 ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
386 if (ci->ci_size >= smallci->ci_size)
387 ii->ii_index[ix++] = ci - cs->sc_cinfo;
388 ii->ii_ndisk = ix;
389 bn += ix * (smallci->ci_size - size);
390 lbn = smallci->ci_size / cs->sc_ileave;
391 size = smallci->ci_size;
392 }
393 #ifdef DEBUG
394 if (ccddebug & CCDB_INIT)
395 printiinfo(cs->sc_itable);
396 #endif
397 return(1);
398 }
399
400 #ifdef DEBUG
401 printiinfo(ii)
402 struct ccdiinfo *ii;
403 {
404 register int ix, i;
405
406 for (ix = 0; ii->ii_ndisk; ix++, ii++) {
407 printf(" itab[%d]: #dk %d sblk %d soff %d",
408 ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff);
409 for (i = 0; i < ii->ii_ndisk; i++)
410 printf(" %d", ii->ii_index[i]);
411 printf("\n");
412 }
413 }
414 #endif
415
416 ccdopen(dev, flags)
417 dev_t dev;
418 {
419 int unit = ccdunit(dev);
420 register struct ccd_softc *cs = &ccd_softc[unit];
421
422 #ifdef DEBUG
423 if (ccddebug & CCDB_FOLLOW)
424 printf("ccdopen(%x, %x)\n", dev, flags);
425 #endif
426 if (unit >= numccd || (cs->sc_flags & CCDF_ALIVE) == 0)
427 return(ENXIO);
428 return(0);
429 }
430
431 ccdstrategy(bp)
432 register struct buf *bp;
433 {
434 register int unit = ccdunit(bp->b_dev);
435 register struct ccd_softc *cs = &ccd_softc[unit];
436 register daddr_t bn;
437 register int sz, s;
438
439 #ifdef DEBUG
440 if (ccddebug & CCDB_FOLLOW)
441 printf("ccdstrategy(%x): unit %d\n", bp, unit);
442 #endif
443 if ((cs->sc_flags & CCDF_INITED) == 0) {
444 bp->b_error = ENXIO;
445 bp->b_flags |= B_ERROR;
446 goto done;
447 }
448 bn = bp->b_blkno;
449 sz = howmany(bp->b_bcount, DEV_BSIZE);
450 if (bn < 0 || bn + sz > cs->sc_size) {
451 sz = cs->sc_size - bn;
452 if (sz == 0) {
453 bp->b_resid = bp->b_bcount;
454 goto done;
455 }
456 if (sz < 0) {
457 bp->b_error = EINVAL;
458 bp->b_flags |= B_ERROR;
459 goto done;
460 }
461 bp->b_bcount = dbtob(sz);
462 }
463 bp->b_resid = bp->b_bcount;
464 /*
465 * "Start" the unit.
466 */
467 s = splbio();
468 ccdstart(cs, bp);
469 splx(s);
470 return;
471 done:
472 biodone(bp);
473 }
474
475 ccdstart(cs, bp)
476 register struct ccd_softc *cs;
477 register struct buf *bp;
478 {
479 register long bcount, rcount;
480 struct ccdbuf *cbp;
481 caddr_t addr;
482 daddr_t bn;
483
484 #ifdef DEBUG
485 if (ccddebug & CCDB_FOLLOW)
486 printf("ccdstart(%x, %x)\n", cs, bp);
487 #endif
488 /*
489 * Instumentation (not real meaningful)
490 */
491 cs->sc_usecnt++;
492 if (cs->sc_dk >= 0) {
493 dk_busy |= 1 << cs->sc_dk;
494 dk_xfer[cs->sc_dk]++;
495 dk_wds[cs->sc_dk] += bp->b_bcount >> 6;
496 }
497 /*
498 * Allocate component buffers and fire off the requests
499 */
500 bn = bp->b_blkno;
501 addr = bp->b_data;
502 for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) {
503 cbp = ccdbuffer(cs, bp, bn, addr, bcount);
504 rcount = cbp->cb_buf.b_bcount;
505 (*bdevsw[major(cbp->cb_buf.b_dev)].d_strategy)(&cbp->cb_buf);
506 bn += btodb(rcount);
507 addr += rcount;
508 }
509 }
510
511 /*
512 * Build a component buffer header.
513 */
514 struct ccdbuf *
515 ccdbuffer(cs, bp, bn, addr, bcount)
516 register struct ccd_softc *cs;
517 struct buf *bp;
518 daddr_t bn;
519 caddr_t addr;
520 long bcount;
521 {
522 register struct ccdcinfo *ci;
523 register struct ccdbuf *cbp;
524 register daddr_t cbn, cboff;
525
526 #ifdef DEBUG
527 if (ccddebug & CCDB_IO)
528 printf("ccdbuffer(%x, %x, %d, %x, %d)\n",
529 cs, bp, bn, addr, bcount);
530 #endif
531 /*
532 * Determine which component bn falls in.
533 */
534 cbn = bn;
535 cboff = 0;
536 /*
537 * Serially concatenated
538 */
539 if (cs->sc_ileave == 0) {
540 register daddr_t sblk;
541
542 sblk = 0;
543 for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++)
544 sblk += ci->ci_size;
545 cbn -= sblk;
546 }
547 /*
548 * Interleaved
549 */
550 else {
551 register struct ccdiinfo *ii;
552 int ccdisk, off;
553
554 cboff = cbn % cs->sc_ileave;
555 cbn /= cs->sc_ileave;
556 for (ii = cs->sc_itable; ii->ii_ndisk; ii++)
557 if (ii->ii_startblk > cbn)
558 break;
559 ii--;
560 off = cbn - ii->ii_startblk;
561 if (ii->ii_ndisk == 1) {
562 ccdisk = ii->ii_index[0];
563 cbn = ii->ii_startoff + off;
564 } else {
565 ccdisk = ii->ii_index[off % ii->ii_ndisk];
566 cbn = ii->ii_startoff + off / ii->ii_ndisk;
567 }
568 cbn *= cs->sc_ileave;
569 ci = &cs->sc_cinfo[ccdisk];
570 }
571 /*
572 * Fill in the component buf structure.
573 */
574 cbp = getccdbuf();
575 cbp->cb_buf.b_flags = bp->b_flags | B_CALL;
576 cbp->cb_buf.b_iodone = (void (*)())ccdiodone;
577 cbp->cb_buf.b_proc = bp->b_proc;
578 cbp->cb_buf.b_dev = ci->ci_dev;
579 cbp->cb_buf.b_blkno = cbn + cboff;
580 cbp->cb_buf.b_data = addr;
581 cbp->cb_buf.b_vp = 0;
582 if (cs->sc_ileave == 0)
583 cbp->cb_buf.b_bcount = dbtob(ci->ci_size - cbn);
584 else
585 cbp->cb_buf.b_bcount = dbtob(cs->sc_ileave - cboff);
586 if (cbp->cb_buf.b_bcount > bcount)
587 cbp->cb_buf.b_bcount = bcount;
588
589 /*
590 * context for ccdiodone
591 */
592 cbp->cb_obp = bp;
593 cbp->cb_unit = cs - ccd_softc;
594 cbp->cb_comp = ci - cs->sc_cinfo;
595
596 #ifdef DEBUG
597 if (ccddebug & CCDB_IO)
598 printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n",
599 ci->ci_dev, ci-cs->sc_cinfo, cbp, cbp->cb_buf.b_blkno,
600 cbp->cb_buf.b_data, cbp->cb_buf.b_bcount);
601 #endif
602 return (cbp);
603 }
604
605 ccdintr(cs, bp)
606 register struct ccd_softc *cs;
607 register struct buf *bp;
608 {
609
610 #ifdef DEBUG
611 if (ccddebug & CCDB_FOLLOW)
612 printf("ccdintr(%x, %x)\n", cs, bp);
613 #endif
614 /*
615 * Request is done for better or worse, wakeup the top half.
616 */
617 if (--cs->sc_usecnt == 0 && cs->sc_dk >= 0)
618 dk_busy &= ~(1 << cs->sc_dk);
619 if (bp->b_flags & B_ERROR)
620 bp->b_resid = bp->b_bcount;
621 biodone(bp);
622 }
623
624 /*
625 * Called by biodone at interrupt time.
626 * Mark the component as done and if all components are done,
627 * take a ccd interrupt.
628 */
629 void
630 ccdiodone(cbp)
631 register struct ccdbuf *cbp;
632 {
633 register struct buf *bp = cbp->cb_obp;
634 register int unit = cbp->cb_unit;
635 int count, s;
636
637 s = splbio();
638 #ifdef DEBUG
639 if (ccddebug & CCDB_FOLLOW)
640 printf("ccdiodone(%x)\n", cbp);
641 if (ccddebug & CCDB_IO) {
642 printf("ccdiodone: bp %x bcount %d resid %d\n",
643 bp, bp->b_bcount, bp->b_resid);
644 printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n",
645 cbp->cb_buf.b_dev, cbp->cb_comp, cbp,
646 cbp->cb_buf.b_blkno, cbp->cb_buf.b_data,
647 cbp->cb_buf.b_bcount);
648 }
649 #endif
650
651 if (cbp->cb_buf.b_flags & B_ERROR) {
652 bp->b_flags |= B_ERROR;
653 bp->b_error = cbp->cb_buf.b_error ? cbp->cb_buf.b_error : EIO;
654 #ifdef DEBUG
655 printf("ccd%d: error %d on component %d\n",
656 unit, bp->b_error, cbp->cb_comp);
657 #endif
658 }
659 count = cbp->cb_buf.b_bcount;
660 putccdbuf(cbp);
661
662 /*
663 * If all done, "interrupt".
664 */
665 bp->b_resid -= count;
666 if (bp->b_resid < 0)
667 panic("ccdiodone: count");
668 if (bp->b_resid == 0)
669 ccdintr(&ccd_softc[unit], bp);
670 splx(s);
671 }
672
673 ccdread(dev, uio)
674 dev_t dev;
675 struct uio *uio;
676 {
677 register int unit = ccdunit(dev);
678
679 #ifdef DEBUG
680 if (ccddebug & CCDB_FOLLOW)
681 printf("ccdread(%x, %x)\n", dev, uio);
682 #endif
683 return(physio(ccdstrategy, NULL, dev, B_READ, minphys, uio));
684 }
685
686 ccdwrite(dev, uio)
687 dev_t dev;
688 struct uio *uio;
689 {
690 register int unit = ccdunit(dev);
691
692 #ifdef DEBUG
693 if (ccddebug & CCDB_FOLLOW)
694 printf("ccdwrite(%x, %x)\n", dev, uio);
695 #endif
696 return(physio(ccdstrategy, NULL, dev, B_WRITE, minphys, uio));
697 }
698
699 ccdioctl(dev, cmd, data, flag)
700 dev_t dev;
701 u_long cmd;
702 caddr_t data;
703 int flag;
704 {
705 return(EINVAL);
706 }
707
708 ccdsize(dev)
709 dev_t dev;
710 {
711 int unit = ccdunit(dev);
712 register struct ccd_softc *cs = &ccd_softc[unit];
713
714 if (unit >= numccd || (cs->sc_flags & CCDF_INITED) == 0)
715 return(-1);
716 return(cs->sc_size);
717 }
718
719 ccddump(dev)
720 {
721 return(ENXIO);
722 }
723 #endif
724