1 /* $NetBSD: ccd.c,v 1.4 1994/08/14 07:41:08 mycroft 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 struct buf *ccdbuffer(); 76 char *ccddevtostr(); 77 void ccdiodone(); 78 79 #define ccdunit(x) ((minor(x) >> 3) & 0xf) /* for consistency */ 80 81 #define getcbuf() \ 82 ((struct buf *)malloc(sizeof(struct buf), M_DEVBUF, M_WAITOK)) 83 #define putcbuf(bp) \ 84 free((caddr_t)(bp), M_DEVBUF) 85 86 struct ccd_softc { 87 int sc_flags; /* flags */ 88 size_t sc_size; /* size of ccd */ 89 int sc_ileave; /* interleave */ 90 int sc_nccdisks; /* number of components */ 91 struct ccdcinfo sc_cinfo[NCCDISKS]; /* component info */ 92 struct ccdiinfo *sc_itable; /* interleave table */ 93 int sc_usecnt; /* number of requests active */ 94 int sc_dk; /* disk index */ 95 }; 96 97 /* sc_flags */ 98 #define CCDF_ALIVE 0x01 99 #define CCDF_INITED 0x02 100 101 struct ccd_softc *ccd_softc; 102 int numccd; 103 104 /* 105 * Since this is called after auto-configuration of devices, 106 * we can handle the initialization here. 107 * 108 * XXX this will not work if you want to use a ccd as your primary 109 * swap device since swapconf() has been called before now. 110 */ 111 void 112 ccdattach(num) 113 int num; 114 { 115 char *mem; 116 register u_long size; 117 register struct ccddevice *ccd; 118 extern int dkn; 119 120 if (num <= 0) 121 return; 122 size = num * sizeof(struct ccd_softc); 123 mem = malloc(size, M_DEVBUF, M_NOWAIT); 124 if (mem == NULL) { 125 printf("WARNING: no memory for concatonated disks\n"); 126 return; 127 } 128 bzero(mem, size); 129 ccd_softc = (struct ccd_softc *)mem; 130 numccd = num; 131 for (ccd = ccddevice; ccd->ccd_unit >= 0; ccd++) { 132 /* 133 * XXX 134 * Assign disk index first so that init routine 135 * can use it (saves having the driver drag around 136 * the ccddevice pointer just to set up the dk_* 137 * info in the open routine). 138 */ 139 if (dkn < DK_NDRIVE) 140 ccd->ccd_dk = dkn++; 141 else 142 ccd->ccd_dk = -1; 143 if (ccdinit(ccd)) 144 printf("ccd%d configured\n", ccd->ccd_unit); 145 else if (ccd->ccd_dk >= 0) { 146 ccd->ccd_dk = -1; 147 dkn--; 148 } 149 } 150 } 151 152 ccdinit(ccd) 153 struct ccddevice *ccd; 154 { 155 register struct ccd_softc *cs = &ccd_softc[ccd->ccd_unit]; 156 register struct ccdcinfo *ci; 157 register size_t size; 158 register int ix; 159 size_t minsize; 160 dev_t dev; 161 struct bdevsw *bsw; 162 int error; 163 struct proc *p = curproc; /* XXX */ 164 165 #ifdef DEBUG 166 if (ccddebug & (CCDB_FOLLOW|CCDB_INIT)) 167 printf("ccdinit: unit %d\n", ccd->ccd_unit); 168 #endif 169 cs->sc_dk = ccd->ccd_dk; 170 cs->sc_size = 0; 171 cs->sc_ileave = ccd->ccd_interleave; 172 cs->sc_nccdisks = 0; 173 /* 174 * Verify that each component piece exists and record 175 * relevant information about it. 176 */ 177 minsize = 0; 178 for (ix = 0; ix < NCCDISKS; ix++) { 179 if ((dev = ccd->ccd_dev[ix]) == NODEV) 180 break; 181 ci = &cs->sc_cinfo[ix]; 182 ci->ci_dev = dev; 183 bsw = &bdevsw[major(dev)]; 184 /* 185 * Open the partition 186 */ 187 if (bsw->d_open && 188 (error = (*bsw->d_open)(dev, 0, S_IFBLK, p))) { 189 printf("ccd%d: component %s open failed, error = %d\n", 190 ccd->ccd_unit, ccddevtostr(dev), error); 191 return(0); 192 } 193 /* 194 * Calculate size (truncated to interleave boundary 195 * if necessary. 196 */ 197 if (bsw->d_psize) { 198 size = (size_t) (*bsw->d_psize)(dev); 199 if ((int)size < 0) 200 size = 0; 201 } else 202 size = 0; 203 if (cs->sc_ileave > 1) 204 size -= size % cs->sc_ileave; 205 if (size == 0) { 206 printf("ccd%d: not configured (component %s missing)\n", 207 ccd->ccd_unit, ccddevtostr(dev)); 208 return(0); 209 } 210 #ifdef COMPAT_NOLABEL 211 /* 212 * XXX if this is a 'c' partition then we need to mark the 213 * label area writeable since there cannot be a label. 214 */ 215 if ((minor(dev) & 7) == 2 && bsw->d_open) { 216 int i, flag; 217 218 for (i = 0; i < nchrdev; i++) 219 if (cdevsw[i].d_open == bsw->d_open) 220 break; 221 if (i != nchrdev && cdevsw[i].d_ioctl) { 222 flag = 1; 223 (void)(*cdevsw[i].d_ioctl)(dev, DIOCWLABEL, 224 (caddr_t)&flag, FWRITE, p); 225 } 226 } 227 #endif 228 if (minsize == 0 || size < minsize) 229 minsize = size; 230 ci->ci_size = size; 231 cs->sc_size += size; 232 cs->sc_nccdisks++; 233 } 234 /* 235 * If uniform interleave is desired set all sizes to that of 236 * the smallest component. 237 */ 238 if (ccd->ccd_flags & CCDF_UNIFORM) { 239 for (ci = cs->sc_cinfo; 240 ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) 241 ci->ci_size = minsize; 242 cs->sc_size = cs->sc_nccdisks * minsize; 243 } 244 /* 245 * Construct the interleave table 246 */ 247 if (!ccdinterleave(cs)) 248 return(0); 249 if (ccd->ccd_dk >= 0) 250 dk_wpms[ccd->ccd_dk] = 32 * (60 * DEV_BSIZE / 2); /* XXX */ 251 printf("ccd%d: %d components ", ccd->ccd_unit, cs->sc_nccdisks); 252 for (ix = 0; ix < cs->sc_nccdisks; ix++) 253 printf("%c%s%c", 254 ix == 0 ? '(' : ' ', 255 ccddevtostr(cs->sc_cinfo[ix].ci_dev), 256 ix == cs->sc_nccdisks - 1 ? ')' : ','); 257 printf(", %d blocks ", cs->sc_size); 258 if (cs->sc_ileave) 259 printf("interleaved at %d blocks\n", cs->sc_ileave); 260 else 261 printf("concatenated\n"); 262 cs->sc_flags = CCDF_ALIVE | CCDF_INITED; 263 return(1); 264 } 265 266 /* 267 * XXX not really ccd specific. 268 * Could be called something like bdevtostr in machine/conf.c. 269 */ 270 char * 271 ccddevtostr(dev) 272 dev_t dev; 273 { 274 static char dbuf[5]; 275 276 switch (major(dev)) { 277 #ifdef hp300 278 case 2: 279 dbuf[0] = 'r'; dbuf[1] = 'd'; 280 break; 281 case 4: 282 dbuf[0] = 's'; dbuf[1] = 'd'; 283 break; 284 case 5: 285 dbuf[0] = 'c'; dbuf[1] = 'd'; 286 break; 287 case 6: 288 dbuf[0] = 'v'; dbuf[1] = 'n'; 289 break; 290 #endif 291 default: 292 dbuf[0] = dbuf[1] = '?'; 293 break; 294 } 295 dbuf[2] = (minor(dev) >> 3) + '0'; 296 dbuf[3] = (minor(dev) & 7) + 'a'; 297 dbuf[4] = '\0'; 298 return (dbuf); 299 } 300 301 ccdinterleave(cs) 302 register struct ccd_softc *cs; 303 { 304 register struct ccdcinfo *ci, *smallci; 305 register struct ccdiinfo *ii; 306 register daddr_t bn, lbn; 307 register int ix; 308 u_long size; 309 310 #ifdef DEBUG 311 if (ccddebug & CCDB_INIT) 312 printf("ccdinterleave(%x): ileave %d\n", cs, cs->sc_ileave); 313 #endif 314 /* 315 * Allocate an interleave table. 316 * Chances are this is too big, but we don't care. 317 */ 318 size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo); 319 cs->sc_itable = (struct ccdiinfo *)malloc(size, M_DEVBUF, M_WAITOK); 320 bzero((caddr_t)cs->sc_itable, size); 321 /* 322 * Trivial case: no interleave (actually interleave of disk size). 323 * Each table entry represent a single component in its entirety. 324 */ 325 if (cs->sc_ileave == 0) { 326 bn = 0; 327 ii = cs->sc_itable; 328 for (ix = 0; ix < cs->sc_nccdisks; ix++) { 329 ii->ii_ndisk = 1; 330 ii->ii_startblk = bn; 331 ii->ii_startoff = 0; 332 ii->ii_index[0] = ix; 333 bn += cs->sc_cinfo[ix].ci_size; 334 ii++; 335 } 336 ii->ii_ndisk = 0; 337 #ifdef DEBUG 338 if (ccddebug & CCDB_INIT) 339 printiinfo(cs->sc_itable); 340 #endif 341 return(1); 342 } 343 /* 344 * The following isn't fast or pretty; it doesn't have to be. 345 */ 346 size = 0; 347 bn = lbn = 0; 348 for (ii = cs->sc_itable; ; ii++) { 349 /* 350 * Locate the smallest of the remaining components 351 */ 352 smallci = NULL; 353 for (ci = cs->sc_cinfo; 354 ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) 355 if (ci->ci_size > size && 356 (smallci == NULL || 357 ci->ci_size < smallci->ci_size)) 358 smallci = ci; 359 /* 360 * Nobody left, all done 361 */ 362 if (smallci == NULL) { 363 ii->ii_ndisk = 0; 364 break; 365 } 366 /* 367 * Record starting logical block and component offset 368 */ 369 ii->ii_startblk = bn / cs->sc_ileave; 370 ii->ii_startoff = lbn; 371 /* 372 * Determine how many disks take part in this interleave 373 * and record their indices. 374 */ 375 ix = 0; 376 for (ci = cs->sc_cinfo; 377 ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++) 378 if (ci->ci_size >= smallci->ci_size) 379 ii->ii_index[ix++] = ci - cs->sc_cinfo; 380 ii->ii_ndisk = ix; 381 bn += ix * (smallci->ci_size - size); 382 lbn = smallci->ci_size / cs->sc_ileave; 383 size = smallci->ci_size; 384 } 385 #ifdef DEBUG 386 if (ccddebug & CCDB_INIT) 387 printiinfo(cs->sc_itable); 388 #endif 389 return(1); 390 } 391 392 #ifdef DEBUG 393 printiinfo(ii) 394 struct ccdiinfo *ii; 395 { 396 register int ix, i; 397 398 for (ix = 0; ii->ii_ndisk; ix++, ii++) { 399 printf(" itab[%d]: #dk %d sblk %d soff %d", 400 ix, ii->ii_ndisk, ii->ii_startblk, ii->ii_startoff); 401 for (i = 0; i < ii->ii_ndisk; i++) 402 printf(" %d", ii->ii_index[i]); 403 printf("\n"); 404 } 405 } 406 #endif 407 408 ccdopen(dev, flags) 409 dev_t dev; 410 { 411 int unit = ccdunit(dev); 412 register struct ccd_softc *cs = &ccd_softc[unit]; 413 414 #ifdef DEBUG 415 if (ccddebug & CCDB_FOLLOW) 416 printf("ccdopen(%x, %x)\n", dev, flags); 417 #endif 418 if (unit >= numccd || (cs->sc_flags & CCDF_ALIVE) == 0) 419 return(ENXIO); 420 return(0); 421 } 422 423 ccdstrategy(bp) 424 register struct buf *bp; 425 { 426 register int unit = ccdunit(bp->b_dev); 427 register struct ccd_softc *cs = &ccd_softc[unit]; 428 register daddr_t bn; 429 register int sz, s; 430 431 #ifdef DEBUG 432 if (ccddebug & CCDB_FOLLOW) 433 printf("ccdstrategy(%x): unit %d\n", bp, unit); 434 #endif 435 if ((cs->sc_flags & CCDF_INITED) == 0) { 436 bp->b_error = ENXIO; 437 bp->b_flags |= B_ERROR; 438 goto done; 439 } 440 bn = bp->b_blkno; 441 sz = howmany(bp->b_bcount, DEV_BSIZE); 442 if (bn < 0 || bn + sz > cs->sc_size) { 443 sz = cs->sc_size - bn; 444 if (sz == 0) { 445 bp->b_resid = bp->b_bcount; 446 goto done; 447 } 448 if (sz < 0) { 449 bp->b_error = EINVAL; 450 bp->b_flags |= B_ERROR; 451 goto done; 452 } 453 bp->b_bcount = dbtob(sz); 454 } 455 bp->b_resid = bp->b_bcount; 456 /* 457 * "Start" the unit. 458 */ 459 s = splbio(); 460 ccdstart(cs, bp); 461 splx(s); 462 return; 463 done: 464 biodone(bp); 465 } 466 467 ccdstart(cs, bp) 468 register struct ccd_softc *cs; 469 register struct buf *bp; 470 { 471 register long bcount, rcount; 472 struct buf *cbp; 473 caddr_t addr; 474 daddr_t bn; 475 476 #ifdef DEBUG 477 if (ccddebug & CCDB_FOLLOW) 478 printf("ccdstart(%x, %x)\n", cs, bp); 479 #endif 480 /* 481 * Instumentation (not real meaningful) 482 */ 483 cs->sc_usecnt++; 484 if (cs->sc_dk >= 0) { 485 dk_busy |= 1 << cs->sc_dk; 486 dk_xfer[cs->sc_dk]++; 487 dk_wds[cs->sc_dk] += bp->b_bcount >> 6; 488 } 489 /* 490 * Allocate component buffers and fire off the requests 491 */ 492 bn = bp->b_blkno; 493 addr = bp->b_data; 494 for (bcount = bp->b_bcount; bcount > 0; bcount -= rcount) { 495 cbp = ccdbuffer(cs, bp, bn, addr, bcount); 496 rcount = cbp->b_bcount; 497 (*bdevsw[major(cbp->b_dev)].d_strategy)(cbp); 498 bn += btodb(rcount); 499 addr += rcount; 500 } 501 } 502 503 /* 504 * Build a component buffer header. 505 */ 506 struct buf * 507 ccdbuffer(cs, bp, bn, addr, bcount) 508 register struct ccd_softc *cs; 509 struct buf *bp; 510 daddr_t bn; 511 caddr_t addr; 512 long bcount; 513 { 514 register struct ccdcinfo *ci; 515 register struct buf *cbp; 516 register daddr_t cbn, cboff; 517 518 #ifdef DEBUG 519 if (ccddebug & CCDB_IO) 520 printf("ccdbuffer(%x, %x, %d, %x, %d)\n", 521 cs, bp, bn, addr, bcount); 522 #endif 523 /* 524 * Determine which component bn falls in. 525 */ 526 cbn = bn; 527 cboff = 0; 528 /* 529 * Serially concatenated 530 */ 531 if (cs->sc_ileave == 0) { 532 register daddr_t sblk; 533 534 sblk = 0; 535 for (ci = cs->sc_cinfo; cbn >= sblk + ci->ci_size; ci++) 536 sblk += ci->ci_size; 537 cbn -= sblk; 538 } 539 /* 540 * Interleaved 541 */ 542 else { 543 register struct ccdiinfo *ii; 544 int ccdisk, off; 545 546 cboff = cbn % cs->sc_ileave; 547 cbn /= cs->sc_ileave; 548 for (ii = cs->sc_itable; ii->ii_ndisk; ii++) 549 if (ii->ii_startblk > cbn) 550 break; 551 ii--; 552 off = cbn - ii->ii_startblk; 553 if (ii->ii_ndisk == 1) { 554 ccdisk = ii->ii_index[0]; 555 cbn = ii->ii_startoff + off; 556 } else { 557 ccdisk = ii->ii_index[off % ii->ii_ndisk]; 558 cbn = ii->ii_startoff + off / ii->ii_ndisk; 559 } 560 cbn *= cs->sc_ileave; 561 ci = &cs->sc_cinfo[ccdisk]; 562 } 563 /* 564 * Fill in the component buf structure. 565 */ 566 cbp = getcbuf(); 567 cbp->b_flags = bp->b_flags | B_CALL; 568 cbp->b_iodone = ccdiodone; 569 cbp->b_proc = bp->b_proc; 570 cbp->b_dev = ci->ci_dev; 571 cbp->b_blkno = cbn + cboff; 572 cbp->b_data = addr; 573 cbp->b_vp = 0; 574 if (cs->sc_ileave == 0) 575 cbp->b_bcount = dbtob(ci->ci_size - cbn); 576 else 577 cbp->b_bcount = dbtob(cs->sc_ileave - cboff); 578 if (cbp->b_bcount > bcount) 579 cbp->b_bcount = bcount; 580 /* 581 * XXX context for ccdiodone 582 */ 583 cbp->b_saveaddr = (caddr_t)bp; 584 cbp->b_pfcent = ((cs - ccd_softc) << 16) | (ci - cs->sc_cinfo); 585 #ifdef DEBUG 586 if (ccddebug & CCDB_IO) 587 printf(" dev %x(u%d): cbp %x bn %d addr %x bcnt %d\n", 588 ci->ci_dev, ci-cs->sc_cinfo, cbp, cbp->b_blkno, 589 cbp->b_data, cbp->b_bcount); 590 #endif 591 return(cbp); 592 } 593 594 ccdintr(cs, bp) 595 register struct ccd_softc *cs; 596 register struct buf *bp; 597 { 598 599 #ifdef DEBUG 600 if (ccddebug & CCDB_FOLLOW) 601 printf("ccdintr(%x, %x)\n", cs, bp); 602 #endif 603 /* 604 * Request is done for better or worse, wakeup the top half. 605 */ 606 if (--cs->sc_usecnt == 0 && cs->sc_dk >= 0) 607 dk_busy &= ~(1 << cs->sc_dk); 608 if (bp->b_flags & B_ERROR) 609 bp->b_resid = bp->b_bcount; 610 biodone(bp); 611 } 612 613 /* 614 * Called by biodone at interrupt time. 615 * Mark the component as done and if all components are done, 616 * take a ccd interrupt. 617 */ 618 void 619 ccdiodone(cbp) 620 register struct buf *cbp; 621 { 622 register struct buf *bp = (struct buf *)cbp->b_saveaddr;/* XXX */ 623 register int unit = (cbp->b_pfcent >> 16) & 0xFFFF; /* XXX */ 624 int count, s; 625 626 s = splbio(); 627 #ifdef DEBUG 628 if (ccddebug & CCDB_FOLLOW) 629 printf("ccdiodone(%x)\n", cbp); 630 if (ccddebug & CCDB_IO) { 631 printf("ccdiodone: bp %x bcount %d resid %d\n", 632 bp, bp->b_bcount, bp->b_resid); 633 printf(" dev %x(u%d), cbp %x bn %d addr %x bcnt %d\n", 634 cbp->b_dev, cbp->b_pfcent & 0xFFFF, cbp, 635 cbp->b_blkno, cbp->b_data, cbp->b_bcount); 636 } 637 #endif 638 639 if (cbp->b_flags & B_ERROR) { 640 bp->b_flags |= B_ERROR; 641 bp->b_error = biowait(cbp); 642 #ifdef DEBUG 643 printf("ccd%d: error %d on component %d\n", 644 unit, bp->b_error, cbp->b_pfcent & 0xFFFF); 645 #endif 646 } 647 count = cbp->b_bcount; 648 putcbuf(cbp); 649 650 /* 651 * If all done, "interrupt". 652 */ 653 bp->b_resid -= count; 654 if (bp->b_resid < 0) 655 panic("ccdiodone: count"); 656 if (bp->b_resid == 0) 657 ccdintr(&ccd_softc[unit], bp); 658 splx(s); 659 } 660 661 ccdread(dev, uio) 662 dev_t dev; 663 struct uio *uio; 664 { 665 register int unit = ccdunit(dev); 666 667 #ifdef DEBUG 668 if (ccddebug & CCDB_FOLLOW) 669 printf("ccdread(%x, %x)\n", dev, uio); 670 #endif 671 return(physio(ccdstrategy, NULL, dev, B_READ, minphys, uio)); 672 } 673 674 ccdwrite(dev, uio) 675 dev_t dev; 676 struct uio *uio; 677 { 678 register int unit = ccdunit(dev); 679 680 #ifdef DEBUG 681 if (ccddebug & CCDB_FOLLOW) 682 printf("ccdwrite(%x, %x)\n", dev, uio); 683 #endif 684 return(physio(ccdstrategy, NULL, dev, B_WRITE, minphys, uio)); 685 } 686 687 ccdioctl(dev, cmd, data, flag) 688 dev_t dev; 689 int cmd; 690 caddr_t data; 691 int flag; 692 { 693 return(EINVAL); 694 } 695 696 ccdsize(dev) 697 dev_t dev; 698 { 699 int unit = ccdunit(dev); 700 register struct ccd_softc *cs = &ccd_softc[unit]; 701 702 if (unit >= numccd || (cs->sc_flags & CCDF_INITED) == 0) 703 return(-1); 704 return(cs->sc_size); 705 } 706 707 ccddump(dev) 708 { 709 return(ENXIO); 710 } 711 #endif 712