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