Home | History | Annotate | Line # | Download | only in maple
mlcd.c revision 1.1
      1 /*	$NetBSD: mlcd.c,v 1.1 2002/11/15 14:10:51 itohy Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by ITOH Yasufumi.
      9  *
     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 NetBSD
     21  *	Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <sys/param.h>
     40 #include <sys/buf.h>
     41 #include <sys/device.h>
     42 #include <sys/kernel.h>
     43 #include <sys/malloc.h>
     44 #include <sys/proc.h>
     45 #include <sys/systm.h>
     46 #include <sys/vnode.h>
     47 #include <sys/conf.h>
     48 
     49 #include <dreamcast/dev/maple/maple.h>
     50 #include <dreamcast/dev/maple/mapleconf.h>
     51 
     52 #define MLCD_MAXACCSIZE	1012	/* (255*4) - 8  =  253*32 / 8 */
     53 
     54 struct mlcd_funcdef {	/* XXX assuming little-endian structure packing */
     55 	unsigned unused	: 6,
     56 		 bw	: 1,	/* 0: normally white, 1: normally black */
     57 		 hv	: 1,	/* 0: horizontal, 1: vertical */
     58 		 ra	: 4,	/* 0 */
     59 		 wa	: 4,	/* number of access / write */
     60 		 bb	: 8,	/* block size / 32 - 1 */
     61 		 pt	: 8;	/* number of partition - 1 */
     62 };
     63 
     64 struct mlcd_request_write_data {
     65 	u_int32_t	func_code;
     66 	u_int8_t	pt;
     67 	u_int8_t	phase;		/* 0, 1, 2, 3: for each 128 byte */
     68 	u_int16_t	block;
     69 	u_int8_t	data[MLCD_MAXACCSIZE];
     70 };
     71 #define MLCD_SIZE_REQW(sc)	((sc)->sc_waccsz + 8)
     72 
     73 struct mlcd_request_get_media_info {
     74 	u_int32_t	func_code;
     75 	u_int32_t	pt;		/* pt (1 byte) and unused 3 bytes */
     76 };
     77 
     78 struct mlcd_media_info {
     79 	u_int8_t	width;		/* width - 1 */
     80 	u_int8_t	height;		/* height - 1 */
     81 	u_int8_t	rsvd[2];	/* ? 0x10 0x02 */
     82 };
     83 
     84 struct mlcd_response_media_info {
     85 	u_int32_t	func_code;	/* function code (big endian) */
     86 	struct mlcd_media_info info;
     87 };
     88 
     89 struct mlcd_softc {
     90 	struct device	sc_dev;
     91 
     92 	struct device	*sc_parent;
     93 	struct maple_unit *sc_unit;
     94 	enum mlcd_stat {
     95 		MLCD_INIT,	/* during initialization */
     96 		MLCD_INIT2,	/* during initialization */
     97 		MLCD_IDLE,	/* init done, not in I/O */
     98 		MLCD_WRITE,	/* in write operation */
     99 		MLCD_DETACH	/* detaching */
    100 	} sc_stat;
    101 
    102 	int		sc_npt;		/* number of partitions */
    103 	int		sc_bsize;	/* block size */
    104 	int		sc_wacc;	/* number of write access per block */
    105 	int		sc_waccsz;	/* size of a write access */
    106 
    107 	struct mlcd_pt {
    108 		int		pt_flags;
    109 #define MLCD_PT_OK	1	/* partition is alive */
    110 #define MLCD_PT_OPEN	2
    111 		struct mlcd_media_info pt_info;	/* geometry per part */
    112 		int		pt_size;	/* partition size in byte */
    113 		int		pt_nblk;	/* partition size on block */
    114 
    115 		char		pt_name[16 /* see device.h */ + 4 /* ".256" */];
    116 	} *sc_pt;
    117 
    118 	/* write request buffer (only one is used at a time) */
    119 	union {
    120 		struct mlcd_request_write_data req_write;
    121 		struct mlcd_request_get_media_info req_minfo;
    122 	} sc_req;
    123 #define sc_reqw	sc_req.req_write
    124 #define sc_reqm	sc_req.req_minfo
    125 
    126 	/* pending buffers */
    127 	struct bufq_state sc_q;
    128 
    129 	/* current I/O access */
    130 	struct buf	*sc_bp;
    131 	int		sc_retry;
    132 #define MLCD_MAXRETRY	10
    133 };
    134 
    135 /*
    136  * minor number layout (mlcddetach() depends on this layout):
    137  *
    138  * 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
    139  * |---------------------------------| |---------------------|
    140  *                unit                          part
    141  */
    142 #define MLCD_PART(dev)		(minor(dev) & 0xff)
    143 #define MLCD_UNIT(dev)		(minor(dev) >> 8)
    144 #define MLCD_MINOR(unit, part)	(((unit) << 8) | (part))
    145 
    146 static int	mlcdmatch __P((struct device *, struct cfdata *, void *));
    147 static void	mlcdattach __P((struct device *, struct device *, void *));
    148 static int	mlcddetach __P((struct device *, int));
    149 static void	mlcd_intr __P((void *, struct maple_response *, int, int));
    150 static void	mlcd_printerror __P((const char *, u_int32_t));
    151 static void	mlcdstart __P((struct mlcd_softc *));
    152 static void	mlcdstart_bp __P((struct mlcd_softc *, struct buf *bp));
    153 static void	mlcddone __P((struct mlcd_softc *));
    154 
    155 dev_type_open(mlcdopen);
    156 dev_type_close(mlcdclose);
    157 dev_type_write(mlcdwrite);
    158 dev_type_ioctl(mlcdioctl);
    159 dev_type_strategy(mlcdstrategy);
    160 
    161 const struct cdevsw mlcd_cdevsw = {
    162 	mlcdopen, mlcdclose, noread, mlcdwrite, mlcdioctl,
    163 	nostop, notty, nopoll, nommap, nokqfilter
    164 };
    165 
    166 CFATTACH_DECL(mlcd, sizeof(struct mlcd_softc),
    167     mlcdmatch, mlcdattach, mlcddetach, NULL);
    168 
    169 extern struct cfdriver mlcd_cd;
    170 
    171 /* initial image "NetBSD dreamcast" */
    172 static const char initimg48x32[192] = {
    173 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    174 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    175 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    176 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    177 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    178 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0xac, 0xe5, 0x56, 0x70, 0xb8,
    179 	0x14, 0x12, 0x15, 0x49, 0x08, 0xa4, 0x13, 0x1c, 0x15, 0x4e, 0x78, 0xa4,
    180 	0x10, 0x90, 0x15, 0x48, 0x49, 0xa4, 0x7b, 0x0c, 0xe3, 0xc6, 0x36, 0xb8,
    181 	0x10, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
    182 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    183 	0x07, 0xcf, 0x9f, 0xb8, 0x79, 0x8e, 0x19, 0x99, 0xb3, 0x6c, 0x8d, 0x8c,
    184 	0x31, 0xb1, 0x63, 0x4c, 0x0d, 0x8c, 0x31, 0xb0, 0x66, 0x18, 0x3b, 0x58,
    185 	0x63, 0x18, 0x66, 0x19, 0xdb, 0x58, 0x63, 0x0c, 0x3e, 0x7d, 0x93, 0x58,
    186 	0x63, 0x06, 0x46, 0x30, 0xe3, 0x78, 0x66, 0x66, 0xcc, 0x30, 0x06, 0x30,
    187 	0x36, 0x64, 0xcc, 0x00, 0x06, 0x30, 0x0f, 0x38, 0x7e, 0x00, 0x0e, 0x38,
    188 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    189 };
    190 
    191 static int
    192 mlcdmatch(parent, cf, aux)
    193 	struct device *parent;
    194 	struct cfdata *cf;
    195 	void *aux;
    196 {
    197 	struct maple_attach_args *ma = aux;
    198 
    199 	return (ma->ma_function == MAPLE_FN_LCD ? MAPLE_MATCH_FUNC : 0);
    200 }
    201 
    202 static void
    203 mlcdattach(parent, self, aux)
    204 	struct device *parent, *self;
    205 	void *aux;
    206 {
    207 	struct mlcd_softc *sc = (void *) self;
    208 	struct maple_attach_args *ma = aux;
    209 	int i;
    210 	union {
    211 		u_int32_t v;
    212 		struct mlcd_funcdef s;
    213 	} funcdef;
    214 
    215 	sc->sc_parent = parent;
    216 	sc->sc_unit = ma->ma_unit;
    217 
    218 	funcdef.v = maple_get_function_data(ma->ma_devinfo, MAPLE_FN_LCD);
    219 	printf(": LCD display\n");
    220 	printf("%s: %d LCD, %d bytes/block, ",
    221 	    sc->sc_dev.dv_xname,
    222 	    sc->sc_npt = funcdef.s.pt + 1,
    223 	    sc->sc_bsize = (funcdef.s.bb + 1) << 5);
    224 	if ((sc->sc_wacc = funcdef.s.wa) == 0)
    225 		printf("no ");
    226 	else
    227 		printf("%d acc/", sc->sc_wacc);
    228 	printf("write, %s, normally %s\n",
    229 	    funcdef.s.hv ? "vertical" : "horizontal",
    230 	    funcdef.s.bw ? "black" : "white");
    231 
    232 	/*
    233 	 * start init sequence
    234 	 */
    235 	sc->sc_stat = MLCD_INIT;
    236 	bufq_alloc(&sc->sc_q, BUFQ_DISKSORT|BUFQ_SORT_RAWBLOCK);
    237 
    238 	/* check consistency */
    239 	if (sc->sc_wacc != 0) {
    240 		sc->sc_waccsz = sc->sc_bsize / sc->sc_wacc;
    241 		if (sc->sc_bsize != sc->sc_waccsz * sc->sc_wacc) {
    242 			printf("%s: write access isn't equally divided\n",
    243 			    sc->sc_dev.dv_xname);
    244 			sc->sc_wacc = 0;	/* no write */
    245 		} else if (sc->sc_waccsz > MLCD_MAXACCSIZE) {
    246 			printf("%s: write access size is too large\n",
    247 			    sc->sc_dev.dv_xname);
    248 			sc->sc_wacc = 0;	/* no write */
    249 		}
    250 	}
    251 	if (sc->sc_wacc == 0) {
    252 		printf("%s: device doesn't support write\n",
    253 		    sc->sc_dev.dv_xname);
    254 		return;
    255 	}
    256 
    257 	/* per-part structure */
    258 	sc->sc_pt = malloc(sizeof(struct mlcd_pt) * sc->sc_npt, M_DEVBUF,
    259 	    M_WAITOK|M_ZERO);
    260 
    261 	for (i = 0; i < sc->sc_npt; i++) {
    262 		sprintf(sc->sc_pt[i].pt_name, "%s.%d", sc->sc_dev.dv_xname, i);
    263 	}
    264 
    265 	maple_set_callback(parent, sc->sc_unit, MAPLE_FN_LCD,
    266 	    mlcd_intr, sc);
    267 
    268 	/*
    269 	 * get size (start from partition 0)
    270 	 */
    271 	sc->sc_reqm.func_code = htonl(MAPLE_FUNC(MAPLE_FN_LCD));
    272 	sc->sc_reqm.pt = 0;
    273 	maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
    274 	    MAPLE_COMMAND_GETMINFO, sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
    275 }
    276 
    277 static int
    278 mlcddetach(self, flags)
    279 	struct device *self;
    280 	int flags;
    281 {
    282 	struct mlcd_softc *sc = (struct mlcd_softc *) self;
    283 	struct buf *bp;
    284 	int minor_l, minor_h;
    285 
    286 	sc->sc_stat = MLCD_DETACH;	/* just in case */
    287 
    288 	/*
    289 	 * kill pending I/O
    290 	 */
    291 	if ((bp = sc->sc_bp) != NULL) {
    292 		bp->b_error = EIO;
    293 		bp->b_flags |= B_ERROR;
    294 		bp->b_resid = bp->b_bcount;
    295 		biodone(bp);
    296 	}
    297 	while ((bp = BUFQ_GET(&sc->sc_q)) != NULL) {
    298 		bp->b_error = EIO;
    299 		bp->b_flags |= B_ERROR;
    300 		bp->b_resid = bp->b_bcount;
    301 		biodone(bp);
    302 	}
    303 	bufq_free(&sc->sc_q);
    304 
    305 	/*
    306 	 * revoke vnodes
    307 	 */
    308 	minor_l = MLCD_MINOR(self->dv_unit, 0);
    309 	minor_h = MLCD_MINOR(self->dv_unit, sc->sc_npt - 1);
    310 	vdevgone(cdevsw_lookup_major(&mlcd_cdevsw), minor_l, minor_h, VCHR);
    311 
    312 	/*
    313 	 * free per-partition structure
    314 	 */
    315 	if (sc->sc_pt)
    316 		free(sc->sc_pt, M_DEVBUF);
    317 
    318 	return 0;
    319 }
    320 
    321 /*
    322  * called back from maple bus driver
    323  */
    324 static void
    325 mlcd_intr(dev, response, sz, flags)
    326 	void *dev;
    327 	struct maple_response *response;
    328 	int sz, flags;
    329 {
    330 	struct mlcd_softc *sc = dev;
    331 	struct mlcd_response_media_info *rm = (void *) response->data;
    332 	struct buf *bp;
    333 	int part;
    334 	struct mlcd_pt *pt;
    335 
    336 	switch (sc->sc_stat) {
    337 	case MLCD_INIT:
    338 		/* checking part geometry */
    339 		part = sc->sc_reqm.pt;
    340 		pt = &sc->sc_pt[part];
    341 		switch ((maple_response_t) response->response_code) {
    342 		case MAPLE_RESPONSE_DATATRF:
    343 			pt->pt_info = rm->info;
    344 			pt->pt_size = ((pt->pt_info.width + 1) *
    345 			    (pt->pt_info.height + 1) + 7) / 8;
    346 			pt->pt_nblk = pt->pt_size / sc->sc_bsize;
    347 			printf("%s: %dx%d display, %d bytes\n",
    348 			    pt->pt_name,
    349 			    pt->pt_info.width + 1, pt->pt_info.height + 1,
    350 			    pt->pt_size);
    351 
    352 			/* this partition is active */
    353 			pt->pt_flags = MLCD_PT_OK;
    354 
    355 			break;
    356 		default:
    357 			printf("%s: init: unexpected response %#x, sz %d\n",
    358 			    pt->pt_name, ntohl(response->response_code), sz);
    359 			break;
    360 		}
    361 		if (++part == sc->sc_npt) {
    362 			/* init done */
    363 
    364 			/* XXX initial image for Visual Memory */
    365 			if (sc->sc_pt[0].pt_size == sizeof initimg48x32 &&
    366 			    sc->sc_waccsz == sizeof initimg48x32 &&
    367 			    sc->sc_wacc == 1) {
    368 				sc->sc_stat = MLCD_INIT2;
    369 				sc->sc_reqw.func_code =
    370 				    htonl(MAPLE_FUNC(MAPLE_FN_LCD));
    371 				sc->sc_reqw.pt = 0;	/* part 0 */
    372 				sc->sc_reqw.block = 0;
    373 				sc->sc_reqw.phase = 0;
    374 				bcopy(initimg48x32, sc->sc_reqw.data,
    375 				    sizeof initimg48x32);
    376 				maple_command(sc->sc_parent, sc->sc_unit,
    377 				    MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
    378 				    MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
    379 			} else
    380 				sc->sc_stat = MLCD_IDLE;	/* init done */
    381 		} else {
    382 			sc->sc_reqm.pt = part;
    383 			maple_command(sc->sc_parent, sc->sc_unit,
    384 			    MAPLE_FN_LCD, MAPLE_COMMAND_GETMINFO,
    385 			    sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
    386 		}
    387 		break;
    388 
    389 	case MLCD_INIT2:
    390 		sc->sc_stat = MLCD_IDLE;	/* init done */
    391 		break;
    392 
    393 	case MLCD_WRITE:
    394 		bp = sc->sc_bp;
    395 
    396 		switch ((maple_response_t) response->response_code) {
    397 		case MAPLE_RESPONSE_OK:			/* write done */
    398 			if (++sc->sc_reqw.phase == sc->sc_wacc) {
    399 				/* all phase done */
    400 				mlcddone(sc);
    401 			} else {
    402 				/* go next phase */
    403 				bcopy(bp->b_data
    404 					+ sc->sc_waccsz * sc->sc_reqw.phase,
    405 				    sc->sc_reqw.data, sc->sc_waccsz);
    406 				maple_command(sc->sc_parent, sc->sc_unit,
    407 				    MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
    408 				    MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
    409 			}
    410 			break;
    411 		case MAPLE_RESPONSE_LCDERR:
    412 			mlcd_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name,
    413 			    rm->func_code /* XXX */);
    414 			mlcdstart_bp(sc, bp);		/* retry */
    415 			break;
    416 		default:
    417 			printf("%s: write: unexpected response %#x, %#x, sz %d\n",
    418 			    sc->sc_pt[sc->sc_reqw.pt].pt_name,
    419 			    ntohl(response->response_code),
    420 			    ntohl(rm->func_code), sz);
    421 			mlcdstart_bp(sc, bp);		/* retry */
    422 			break;
    423 		}
    424 		break;
    425 
    426 	default:
    427 		break;
    428 	}
    429 }
    430 
    431 static void
    432 mlcd_printerror(head, code)
    433 	const char *head;
    434 	u_int32_t code;
    435 {
    436 
    437 	printf("%s:", head);
    438 	NTOHL(code);
    439 	if (code & 1)
    440 		printf(" PT error");
    441 	if (code & 2)
    442 		printf(" Phase error");
    443 	if (code & 4)
    444 		printf(" Block error");
    445 	if (code & 010)
    446 		printf(" Write error");
    447 	if (code & 020)
    448 		printf(" Length error");
    449 	if (code & ~037)
    450 		printf(" Unknown error %#x", code & ~037);
    451 	printf("\n");
    452 }
    453 
    454 int
    455 mlcdopen(dev, flags, devtype, p)
    456 	dev_t dev;
    457 	int flags, devtype;
    458 	struct proc *p;
    459 {
    460 	int unit, part;
    461 	struct mlcd_softc *sc;
    462 	struct mlcd_pt *pt;
    463 
    464 	unit = MLCD_UNIT(dev);
    465 	part = MLCD_PART(dev);
    466 	if ((sc = device_lookup(&mlcd_cd, unit)) == NULL
    467 	    || sc->sc_stat == MLCD_INIT
    468 	    || sc->sc_stat == MLCD_INIT2
    469 	    || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0)
    470 		return ENXIO;
    471 
    472 	if (pt->pt_flags & MLCD_PT_OPEN)
    473 		return EBUSY;
    474 
    475 	pt->pt_flags |= MLCD_PT_OPEN;
    476 
    477 	return 0;
    478 }
    479 
    480 int
    481 mlcdclose(dev, flags, devtype, p)
    482 	dev_t dev;
    483 	int flags, devtype;
    484 	struct proc *p;
    485 {
    486 	int unit, part;
    487 	struct mlcd_softc *sc;
    488 	struct mlcd_pt *pt;
    489 
    490 	unit = MLCD_UNIT(dev);
    491 	part = MLCD_PART(dev);
    492 	sc = mlcd_cd.cd_devs[unit];
    493 	pt = &sc->sc_pt[part];
    494 
    495 	pt->pt_flags &= ~MLCD_PT_OPEN;
    496 
    497 	return 0;
    498 }
    499 
    500 void
    501 mlcdstrategy(bp)
    502 	struct buf *bp;
    503 {
    504 	int dev, unit, part;
    505 	struct mlcd_softc *sc;
    506 	struct mlcd_pt *pt;
    507 	daddr_t off, nblk, cnt;
    508 
    509 	dev = bp->b_dev;
    510 	unit = MLCD_UNIT(dev);
    511 	part = MLCD_PART(dev);
    512 	sc = mlcd_cd.cd_devs[unit];
    513 	pt = &sc->sc_pt[part];
    514 
    515 #if 0
    516 	printf("%s: mlcdstrategy: blkno %d, count %ld\n",
    517 	    pt->pt_name, bp->b_blkno, bp->b_bcount);
    518 #endif
    519 
    520 	if (bp->b_flags & B_READ)
    521 		goto inval;			/* no read */
    522 
    523 	cnt = howmany(bp->b_bcount, sc->sc_bsize);
    524 	if (cnt == 0)
    525 		goto done;	/* no work */
    526 
    527 	/* XXX We have set the transfer is only one block in mlcd_minphys(). */
    528 	KASSERT(cnt == 1);
    529 
    530 	if (bp->b_blkno & ~(~(daddr_t)0 >> (DEV_BSHIFT + 1 /* sign bit */))
    531 	    /*|| (bp->b_bcount % sc->sc_bsize) != 0*/)
    532 		goto inval;
    533 
    534 	off = bp->b_blkno * DEV_BSIZE / sc->sc_bsize;
    535 	nblk = pt->pt_nblk;
    536 
    537 	/* deal with the EOF condition */
    538 	if (off + cnt > nblk) {
    539 		if (off >= nblk) {
    540 			if (off == nblk) {
    541 				bp->b_resid = bp->b_bcount;
    542 				goto done;
    543 			}
    544 			goto inval;
    545 		}
    546 		cnt = nblk - off;
    547 		bp->b_resid = bp->b_bcount - (cnt * sc->sc_bsize);
    548 	}
    549 
    550 	bp->b_rawblkno = off;
    551 
    552 	/* queue this transfer */
    553 	BUFQ_PUT(&sc->sc_q, bp);
    554 
    555 	if (sc->sc_stat == MLCD_IDLE)
    556 		mlcdstart(sc);
    557 
    558 	return;
    559 
    560 inval:	bp->b_error = EINVAL;
    561 	bp->b_flags |= B_ERROR;
    562 done:	biodone(bp);
    563 }
    564 
    565 /*
    566  * start I/O operations
    567  */
    568 static void
    569 mlcdstart(sc)
    570 	struct mlcd_softc *sc;
    571 {
    572 	struct buf *bp;
    573 
    574 	if ((bp = BUFQ_GET(&sc->sc_q)) == NULL) {
    575 		sc->sc_stat = MLCD_IDLE;
    576 		maple_enable_unit_ping(sc->sc_parent, sc->sc_unit,
    577 		    MAPLE_FN_LCD, 1);
    578 		return;
    579 	}
    580 
    581 	sc->sc_retry = 0;
    582 	mlcdstart_bp(sc, bp);
    583 }
    584 
    585 /*
    586  * start/retry a specified I/O operation
    587  */
    588 static void
    589 mlcdstart_bp(sc, bp)
    590 	struct mlcd_softc *sc;
    591 	struct buf *bp;
    592 {
    593 	int part;
    594 	struct mlcd_pt *pt;
    595 
    596 	part = MLCD_PART(bp->b_dev);
    597 	pt = &sc->sc_pt[part];
    598 
    599 	/* handle retry */
    600 	if (sc->sc_retry++ > MLCD_MAXRETRY) {
    601 		/* retry count exceeded */
    602 		bp->b_error = EIO;
    603 		bp->b_flags |= B_ERROR;
    604 		mlcddone(sc);
    605 		return;
    606 	}
    607 
    608 	sc->sc_bp = bp;
    609 	/* sc->sc_cnt = cnt; */	/* cnt is always 1 */
    610 
    611 	/*
    612 	 * I/O access will fail if the removal detection (by maple driver)
    613 	 * occurs before finishing the I/O, so disable it.
    614 	 * We are sending commands, and the removal detection is still alive.
    615 	 */
    616 	maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD, 0);
    617 
    618 	/*
    619 	 * Start the first phase (phase# = 0).
    620 	 */
    621 	KASSERT((bp->b_flags & B_READ) == 0);
    622 	/* write */
    623 	sc->sc_stat = MLCD_WRITE;
    624 	sc->sc_reqw.func_code = htonl(MAPLE_FUNC(MAPLE_FN_LCD));
    625 	sc->sc_reqw.pt = part;
    626 	sc->sc_reqw.block = htons(bp->b_rawblkno);
    627 	sc->sc_reqw.phase = 0;		/* first phase */
    628 	bcopy(bp->b_data /* + sc->sc_waccsz * phase */,
    629 	    sc->sc_reqw.data, sc->sc_waccsz);
    630 	maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
    631 	    MAPLE_COMMAND_BWRITE, MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
    632 }
    633 
    634 static void
    635 mlcddone(sc)
    636 	struct mlcd_softc *sc;
    637 {
    638 	struct buf *bp;
    639 
    640 	/* terminate current transfer */
    641 	bp = sc->sc_bp;
    642 	KASSERT(bp);
    643 	sc->sc_bp = NULL;
    644 	biodone(bp);
    645 
    646 	/* go next transfer */
    647 	mlcdstart(sc);
    648 }
    649 
    650 static void mlcd_minphys __P((struct buf *));
    651 
    652 static void
    653 mlcd_minphys(bp)
    654 	struct buf *bp;
    655 {
    656 	int unit;
    657 	struct mlcd_softc *sc;
    658 
    659 	unit = MLCD_UNIT(bp->b_dev);
    660 	sc = mlcd_cd.cd_devs[unit];
    661 
    662 	/* XXX one block only */
    663 	if (bp->b_bcount > sc->sc_bsize)
    664 		bp->b_bcount = sc->sc_bsize;
    665 }
    666 
    667 int
    668 mlcdwrite(dev, uio, flags)
    669 	dev_t	dev;
    670 	struct	uio *uio;
    671 	int	flags;
    672 {
    673 
    674 	return (physio(mlcdstrategy, NULL, dev, B_WRITE, mlcd_minphys, uio));
    675 }
    676 
    677 int
    678 mlcdioctl(dev, cmd, data, flag, p)
    679 	dev_t dev;
    680 	u_long cmd;
    681 	caddr_t data;
    682 	int flag;
    683 	struct proc *p;
    684 {
    685 	int unit, part;
    686 	struct mlcd_softc *sc;
    687 	struct mlcd_pt *pt;
    688 
    689 	unit = MLCD_UNIT(dev);
    690 	part = MLCD_PART(dev);
    691 	sc = mlcd_cd.cd_devs[unit];
    692 	pt = &sc->sc_pt[part];
    693 
    694 	switch (cmd) {
    695 
    696 	default:
    697 		/* generic maple ioctl */
    698 		return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data,
    699 		    flag, p);
    700 	}
    701 
    702 	return 0;
    703 }
    704