Home | History | Annotate | Line # | Download | only in maple
mlcd.c revision 1.2
      1 /*	$NetBSD: mlcd.c,v 1.2 2002/12/06 15:59:53 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/device.h>
     41 #include <sys/kernel.h>
     42 #include <sys/malloc.h>
     43 #include <sys/proc.h>
     44 #include <sys/systm.h>
     45 #include <sys/vnode.h>
     46 #include <sys/conf.h>
     47 
     48 #include <dreamcast/dev/maple/maple.h>
     49 #include <dreamcast/dev/maple/mapleconf.h>
     50 
     51 #define MLCD_MAXACCSIZE	1012	/* (255*4) - 8  =  253*32 / 8 */
     52 
     53 struct mlcd_funcdef {	/* XXX assuming little-endian structure packing */
     54 	unsigned unused	: 6,
     55 		 bw	: 1,	/* 0: normally white, 1: normally black */
     56 		 hv	: 1,	/* 0: horizontal, 1: vertical */
     57 		 ra	: 4,	/* 0 */
     58 		 wa	: 4,	/* number of access / write */
     59 		 bb	: 8,	/* block size / 32 - 1 */
     60 		 pt	: 8;	/* number of partition - 1 */
     61 };
     62 
     63 struct mlcd_request_write_data {
     64 	u_int32_t	func_code;
     65 	u_int8_t	pt;
     66 	u_int8_t	phase;		/* 0, 1, 2, 3: for each 128 byte */
     67 	u_int16_t	block;
     68 	u_int8_t	data[MLCD_MAXACCSIZE];
     69 };
     70 #define MLCD_SIZE_REQW(sc)	((sc)->sc_waccsz + 8)
     71 
     72 struct mlcd_request_get_media_info {
     73 	u_int32_t	func_code;
     74 	u_int32_t	pt;		/* pt (1 byte) and unused 3 bytes */
     75 };
     76 
     77 struct mlcd_media_info {
     78 	u_int8_t	width;		/* width - 1 */
     79 	u_int8_t	height;		/* height - 1 */
     80 	u_int8_t	rsvd[2];	/* ? 0x10 0x02 */
     81 };
     82 
     83 struct mlcd_response_media_info {
     84 	u_int32_t	func_code;	/* function code (big endian) */
     85 	struct mlcd_media_info info;
     86 };
     87 
     88 struct mlcd_buf {
     89 	SIMPLEQ_ENTRY(mlcd_buf)	lb_q;
     90 	int		lb_error;
     91 	int		lb_partno;
     92 	int		lb_blkno;
     93 	u_int32_t	lb_data[1];	/* variable length */
     94 };
     95 #define MLCD_BUF_SZ(sc) (offsetof(struct mlcd_buf, lb_data) + (sc)->sc_bsize)
     96 
     97 struct mlcd_softc {
     98 	struct device	sc_dev;
     99 
    100 	struct device	*sc_parent;
    101 	struct maple_unit *sc_unit;
    102 	int		sc_direction;
    103 	enum mlcd_stat {
    104 		MLCD_INIT,	/* during initialization */
    105 		MLCD_INIT2,	/* during initialization */
    106 		MLCD_IDLE,	/* init done, not in I/O */
    107 		MLCD_WRITE,	/* in write operation */
    108 		MLCD_DETACH	/* detaching */
    109 	} sc_stat;
    110 
    111 	int		sc_npt;		/* number of partitions */
    112 	int		sc_bsize;	/* block size */
    113 	int		sc_wacc;	/* number of write access per block */
    114 	int		sc_waccsz;	/* size of a write access */
    115 
    116 	struct mlcd_pt {
    117 		int		pt_flags;
    118 #define MLCD_PT_OK	1	/* partition is alive */
    119 #define MLCD_PT_OPEN	2
    120 		struct mlcd_media_info pt_info;	/* geometry per part */
    121 		int		pt_size;	/* partition size in byte */
    122 		int		pt_nblk;	/* partition size in block */
    123 
    124 		char		pt_name[16 /* see device.h */ + 4 /* ".255" */];
    125 	} *sc_pt;
    126 
    127 	/* write request buffer (only one is used at a time) */
    128 	union {
    129 		struct mlcd_request_write_data req_write;
    130 		struct mlcd_request_get_media_info req_minfo;
    131 	} sc_req;
    132 #define sc_reqw	sc_req.req_write
    133 #define sc_reqm	sc_req.req_minfo
    134 
    135 	/* pending buffers */
    136 	SIMPLEQ_HEAD(mlcd_bufq, mlcd_buf) sc_q;
    137 
    138 	/* current I/O access */
    139 	struct mlcd_buf	*sc_bp;
    140 	int		sc_retry;
    141 #define MLCD_MAXRETRY	10
    142 };
    143 
    144 /*
    145  * minor number layout (mlcddetach() depends on this layout):
    146  *
    147  * 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
    148  * |---------------------------------| |---------------------|
    149  *                unit                          part
    150  */
    151 #define MLCD_PART(dev)		(minor(dev) & 0xff)
    152 #define MLCD_UNIT(dev)		(minor(dev) >> 8)
    153 #define MLCD_MINOR(unit, part)	(((unit) << 8) | (part))
    154 
    155 static int	mlcdmatch __P((struct device *, struct cfdata *, void *));
    156 static void	mlcdattach __P((struct device *, struct device *, void *));
    157 static int	mlcddetach __P((struct device *, int));
    158 static void	mlcd_intr __P((void *, struct maple_response *, int, int));
    159 static void	mlcd_printerror __P((const char *, u_int32_t));
    160 static struct mlcd_buf *mlcd_buf_alloc __P((int /*dev*/, int /*flags*/));
    161 static void	mlcd_buf_free __P((struct mlcd_buf *));
    162 static __inline u_int32_t reverse_32 __P((u_int32_t));
    163 static void	mlcd_rotate_bitmap __P((void *, size_t));
    164 static void	mlcdstart __P((struct mlcd_softc *));
    165 static void	mlcdstart_bp __P((struct mlcd_softc *));
    166 static void	mlcddone __P((struct mlcd_softc *));
    167 
    168 dev_type_open(mlcdopen);
    169 dev_type_close(mlcdclose);
    170 dev_type_write(mlcdwrite);
    171 dev_type_ioctl(mlcdioctl);
    172 
    173 const struct cdevsw mlcd_cdevsw = {
    174 	mlcdopen, mlcdclose, noread, mlcdwrite, mlcdioctl,
    175 	nostop, notty, nopoll, nommap, nokqfilter
    176 };
    177 
    178 CFATTACH_DECL(mlcd, sizeof(struct mlcd_softc),
    179     mlcdmatch, mlcdattach, mlcddetach, NULL);
    180 
    181 extern struct cfdriver mlcd_cd;
    182 
    183 /* initial image "NetBSD dreamcast" */
    184 static const char initimg48x32[192] = {
    185 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    186 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    187 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    188 	0x1c, 0x70, 0x00, 0x7e, 0x1c, 0xf0, 0x0c, 0x60, 0x00, 0x33, 0x26, 0x6c,
    189 	0x0c, 0x60, 0x0c, 0x33, 0x66, 0x66, 0x1e, 0xc7, 0x0c, 0x62, 0x60, 0xc6,
    190 	0x1a, 0xc9, 0xbe, 0x7c, 0x30, 0xc6, 0x1a, 0xdb, 0x98, 0x66, 0x18, 0xc6,
    191 	0x1a, 0xdc, 0x18, 0x66, 0x0d, 0x8c, 0x31, 0xb0, 0x32, 0xc6, 0x8d, 0x8c,
    192 	0x31, 0xb1, 0x36, 0xcd, 0x99, 0x98, 0x71, 0x9e, 0x1d, 0xf9, 0xf3, 0xe0,
    193 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    194 	0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08,
    195 	0x1d, 0x6c, 0x63, 0xc7, 0x30, 0xde, 0x25, 0x92, 0x12, 0xa8, 0x09, 0x08,
    196 	0x25, 0x1e, 0x72, 0xa8, 0x38, 0xc8, 0x25, 0x10, 0x92, 0xa8, 0x48, 0x28,
    197 	0x1d, 0x0e, 0x6a, 0xa7, 0x35, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    198 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    199 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    200 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    201 };
    202 
    203 /* ARGSUSED */
    204 static int
    205 mlcdmatch(parent, cf, aux)
    206 	struct device *parent;
    207 	struct cfdata *cf;
    208 	void *aux;
    209 {
    210 	struct maple_attach_args *ma = aux;
    211 
    212 	return (ma->ma_function == MAPLE_FN_LCD ? MAPLE_MATCH_FUNC : 0);
    213 }
    214 
    215 static void
    216 mlcdattach(parent, self, aux)
    217 	struct device *parent, *self;
    218 	void *aux;
    219 {
    220 	struct mlcd_softc *sc = (void *) self;
    221 	struct maple_attach_args *ma = aux;
    222 	int i;
    223 	union {
    224 		u_int32_t v;
    225 		struct mlcd_funcdef s;
    226 	} funcdef;
    227 
    228 	sc->sc_parent = parent;
    229 	sc->sc_unit = ma->ma_unit;
    230 	sc->sc_direction = ma->ma_basedevinfo->di_connector_direction;
    231 
    232 	funcdef.v = maple_get_function_data(ma->ma_devinfo, MAPLE_FN_LCD);
    233 	printf(": LCD display\n");
    234 	printf("%s: %d LCD, %d bytes/block, ",
    235 	    sc->sc_dev.dv_xname,
    236 	    sc->sc_npt = funcdef.s.pt + 1,
    237 	    sc->sc_bsize = (funcdef.s.bb + 1) << 5);
    238 	if ((sc->sc_wacc = funcdef.s.wa) == 0)
    239 		printf("no ");
    240 	else
    241 		printf("%d acc/", sc->sc_wacc);
    242 	printf("write, %s, norm %s%s\n",
    243 	    funcdef.s.hv ? "vert" : "horiz",
    244 	    funcdef.s.bw ? "black" : "white",
    245 	    sc->sc_direction == MAPLE_CONN_TOP ? ", upside-down" : "");
    246 
    247 	/*
    248 	 * start init sequence
    249 	 */
    250 	sc->sc_stat = MLCD_INIT;
    251 	SIMPLEQ_INIT(&sc->sc_q);
    252 
    253 	/* check consistency */
    254 	if (sc->sc_wacc != 0) {
    255 		sc->sc_waccsz = sc->sc_bsize / sc->sc_wacc;
    256 		if (sc->sc_bsize != sc->sc_waccsz * sc->sc_wacc) {
    257 			printf("%s: write access isn't equally divided\n",
    258 			    sc->sc_dev.dv_xname);
    259 			sc->sc_wacc = 0;	/* no write */
    260 		} else if (sc->sc_waccsz > MLCD_MAXACCSIZE) {
    261 			printf("%s: write access size is too large\n",
    262 			    sc->sc_dev.dv_xname);
    263 			sc->sc_wacc = 0;	/* no write */
    264 		}
    265 	}
    266 	if (sc->sc_wacc == 0) {
    267 		printf("%s: device doesn't support write\n",
    268 		    sc->sc_dev.dv_xname);
    269 		return;
    270 	}
    271 
    272 	/* per-part structure */
    273 	sc->sc_pt = malloc(sizeof(struct mlcd_pt) * sc->sc_npt, M_DEVBUF,
    274 	    M_WAITOK|M_ZERO);
    275 
    276 	for (i = 0; i < sc->sc_npt; i++) {
    277 		sprintf(sc->sc_pt[i].pt_name, "%s.%d", sc->sc_dev.dv_xname, i);
    278 	}
    279 
    280 	maple_set_callback(parent, sc->sc_unit, MAPLE_FN_LCD,
    281 	    mlcd_intr, sc);
    282 
    283 	/*
    284 	 * get size (start from partition 0)
    285 	 */
    286 	sc->sc_reqm.func_code = htonl(MAPLE_FUNC(MAPLE_FN_LCD));
    287 	sc->sc_reqm.pt = 0;
    288 	maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
    289 	    MAPLE_COMMAND_GETMINFO, sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
    290 }
    291 
    292 /* ARGSUSED1 */
    293 static int
    294 mlcddetach(self, flags)
    295 	struct device *self;
    296 	int flags;
    297 {
    298 	struct mlcd_softc *sc = (struct mlcd_softc *) self;
    299 	struct mlcd_buf *bp;
    300 	int minor_l, minor_h;
    301 
    302 	sc->sc_stat = MLCD_DETACH;	/* just in case */
    303 
    304 	/*
    305 	 * kill pending I/O
    306 	 */
    307 	if ((bp = sc->sc_bp) != NULL) {
    308 		bp->lb_error = EIO;
    309 		wakeup(bp);
    310 	}
    311 	while ((bp = SIMPLEQ_FIRST(&sc->sc_q)) != NULL) {
    312 		SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
    313 		bp->lb_error = EIO;
    314 		wakeup(bp);
    315 	}
    316 
    317 	/*
    318 	 * revoke vnodes
    319 	 */
    320 	minor_l = MLCD_MINOR(self->dv_unit, 0);
    321 	minor_h = MLCD_MINOR(self->dv_unit, sc->sc_npt - 1);
    322 	vdevgone(cdevsw_lookup_major(&mlcd_cdevsw), minor_l, minor_h, VCHR);
    323 
    324 	/*
    325 	 * free per-partition structure
    326 	 */
    327 	if (sc->sc_pt)
    328 		free(sc->sc_pt, M_DEVBUF);
    329 
    330 	return 0;
    331 }
    332 
    333 /*
    334  * called back from maple bus driver
    335  */
    336 /* ARGSUSED3 */
    337 static void
    338 mlcd_intr(dev, response, sz, flags)
    339 	void *dev;
    340 	struct maple_response *response;
    341 	int sz, flags;
    342 {
    343 	struct mlcd_softc *sc = dev;
    344 	struct mlcd_response_media_info *rm = (void *) response->data;
    345 	struct mlcd_buf *bp;
    346 	int part;
    347 	struct mlcd_pt *pt;
    348 
    349 	switch (sc->sc_stat) {
    350 	case MLCD_INIT:
    351 		/* checking part geometry */
    352 		part = sc->sc_reqm.pt;
    353 		pt = &sc->sc_pt[part];
    354 		switch ((maple_response_t) response->response_code) {
    355 		case MAPLE_RESPONSE_DATATRF:
    356 			pt->pt_info = rm->info;
    357 			pt->pt_size = ((pt->pt_info.width + 1) *
    358 			    (pt->pt_info.height + 1) + 7) / 8;
    359 			pt->pt_nblk = pt->pt_size / sc->sc_bsize;
    360 			printf("%s: %dx%d display, %d bytes\n",
    361 			    pt->pt_name,
    362 			    pt->pt_info.width + 1, pt->pt_info.height + 1,
    363 			    pt->pt_size);
    364 
    365 			/* this partition is active */
    366 			pt->pt_flags = MLCD_PT_OK;
    367 
    368 			break;
    369 		default:
    370 			printf("%s: init: unexpected response %#x, sz %d\n",
    371 			    pt->pt_name, ntohl(response->response_code), sz);
    372 			break;
    373 		}
    374 		if (++part == sc->sc_npt) {
    375 			/* init done */
    376 
    377 			/* XXX initial image for Visual Memory */
    378 			if (sc->sc_pt[0].pt_size == sizeof initimg48x32 &&
    379 			    sc->sc_waccsz == sizeof initimg48x32 &&
    380 			    sc->sc_wacc == 1) {
    381 				sc->sc_stat = MLCD_INIT2;
    382 				sc->sc_reqw.func_code =
    383 				    htonl(MAPLE_FUNC(MAPLE_FN_LCD));
    384 				sc->sc_reqw.pt = 0;	/* part 0 */
    385 				sc->sc_reqw.block = 0;
    386 				sc->sc_reqw.phase = 0;
    387 				bcopy(initimg48x32, sc->sc_reqw.data,
    388 				    sizeof initimg48x32);
    389 				if (sc->sc_direction == MAPLE_CONN_TOP) {
    390 					/* the LCD is upside-down */
    391 					mlcd_rotate_bitmap(sc->sc_reqw.data,
    392 					    sizeof initimg48x32);
    393 				}
    394 				maple_command(sc->sc_parent, sc->sc_unit,
    395 				    MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
    396 				    MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
    397 			} else
    398 				sc->sc_stat = MLCD_IDLE;	/* init done */
    399 		} else {
    400 			sc->sc_reqm.pt = part;
    401 			maple_command(sc->sc_parent, sc->sc_unit,
    402 			    MAPLE_FN_LCD, MAPLE_COMMAND_GETMINFO,
    403 			    sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
    404 		}
    405 		break;
    406 
    407 	case MLCD_INIT2:
    408 		sc->sc_stat = MLCD_IDLE;	/* init done */
    409 		break;
    410 
    411 	case MLCD_WRITE:
    412 		bp = sc->sc_bp;
    413 
    414 		switch ((maple_response_t) response->response_code) {
    415 		case MAPLE_RESPONSE_OK:			/* write done */
    416 			if (++sc->sc_reqw.phase == sc->sc_wacc) {
    417 				/* all phase done */
    418 				mlcddone(sc);
    419 			} else {
    420 				/* go next phase */
    421 				bcopy((char *)bp->lb_data
    422 					+ sc->sc_waccsz * sc->sc_reqw.phase,
    423 				    sc->sc_reqw.data, sc->sc_waccsz);
    424 				maple_command(sc->sc_parent, sc->sc_unit,
    425 				    MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
    426 				    MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
    427 			}
    428 			break;
    429 		case MAPLE_RESPONSE_LCDERR:
    430 			mlcd_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name,
    431 			    rm->func_code /* XXX */);
    432 			mlcdstart_bp(sc);		/* retry */
    433 			break;
    434 		default:
    435 			printf("%s: write: unexpected response %#x, %#x, sz %d\n",
    436 			    sc->sc_pt[sc->sc_reqw.pt].pt_name,
    437 			    ntohl(response->response_code),
    438 			    ntohl(rm->func_code), sz);
    439 			mlcdstart_bp(sc);		/* retry */
    440 			break;
    441 		}
    442 		break;
    443 
    444 	default:
    445 		break;
    446 	}
    447 }
    448 
    449 static void
    450 mlcd_printerror(head, code)
    451 	const char *head;
    452 	u_int32_t code;
    453 {
    454 
    455 	printf("%s:", head);
    456 	NTOHL(code);
    457 	if (code & 1)
    458 		printf(" PT error");
    459 	if (code & 2)
    460 		printf(" Phase error");
    461 	if (code & 4)
    462 		printf(" Block error");
    463 	if (code & 010)
    464 		printf(" Write error");
    465 	if (code & 020)
    466 		printf(" Length error");
    467 	if (code & ~037)
    468 		printf(" Unknown error %#x", code & ~037);
    469 	printf("\n");
    470 }
    471 
    472 /* ARGSUSED */
    473 int
    474 mlcdopen(dev, flags, devtype, p)
    475 	dev_t dev;
    476 	int flags, devtype;
    477 	struct proc *p;
    478 {
    479 	int unit, part;
    480 	struct mlcd_softc *sc;
    481 	struct mlcd_pt *pt;
    482 
    483 	unit = MLCD_UNIT(dev);
    484 	part = MLCD_PART(dev);
    485 	if ((sc = device_lookup(&mlcd_cd, unit)) == NULL
    486 	    || sc->sc_stat == MLCD_INIT
    487 	    || sc->sc_stat == MLCD_INIT2
    488 	    || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0)
    489 		return ENXIO;
    490 
    491 	if (pt->pt_flags & MLCD_PT_OPEN)
    492 		return EBUSY;
    493 
    494 	pt->pt_flags |= MLCD_PT_OPEN;
    495 
    496 	return 0;
    497 }
    498 
    499 /* ARGSUSED */
    500 int
    501 mlcdclose(dev, flags, devtype, p)
    502 	dev_t dev;
    503 	int flags, devtype;
    504 	struct proc *p;
    505 {
    506 	int unit, part;
    507 	struct mlcd_softc *sc;
    508 	struct mlcd_pt *pt;
    509 
    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 	pt->pt_flags &= ~MLCD_PT_OPEN;
    516 
    517 	return 0;
    518 }
    519 
    520 /*
    521  * start I/O operations
    522  */
    523 static void
    524 mlcdstart(sc)
    525 	struct mlcd_softc *sc;
    526 {
    527 	struct mlcd_buf *bp;
    528 
    529 	if ((bp = SIMPLEQ_FIRST(&sc->sc_q)) == NULL) {
    530 		sc->sc_stat = MLCD_IDLE;
    531 		maple_enable_unit_ping(sc->sc_parent, sc->sc_unit,
    532 		    MAPLE_FN_LCD, 1);
    533 		return;
    534 	}
    535 
    536 	SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
    537 
    538 	sc->sc_bp = bp;
    539 	sc->sc_retry = 0;
    540 	mlcdstart_bp(sc);
    541 }
    542 
    543 /*
    544  * start/retry a specified I/O operation
    545  */
    546 static void
    547 mlcdstart_bp(sc)
    548 	struct mlcd_softc *sc;
    549 {
    550 	struct mlcd_buf *bp;
    551 	struct mlcd_pt *pt;
    552 
    553 	bp = sc->sc_bp;
    554 	pt = &sc->sc_pt[bp->lb_partno];
    555 
    556 	/* handle retry */
    557 	if (sc->sc_retry++ > MLCD_MAXRETRY) {
    558 		/* retry count exceeded */
    559 		bp->lb_error = EIO;
    560 		mlcddone(sc);
    561 		return;
    562 	}
    563 
    564 	/*
    565 	 * I/O access will fail if the removal detection (by maple driver)
    566 	 * occurs before finishing the I/O, so disable it.
    567 	 * We are sending commands, and the removal detection is still alive.
    568 	 */
    569 	maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD, 0);
    570 
    571 	/*
    572 	 * Start the first phase (phase# = 0).
    573 	 */
    574 	/* write */
    575 	sc->sc_stat = MLCD_WRITE;
    576 	sc->sc_reqw.func_code = htonl(MAPLE_FUNC(MAPLE_FN_LCD));
    577 	sc->sc_reqw.pt = bp->lb_partno;
    578 	sc->sc_reqw.block = htons(bp->lb_blkno);
    579 	sc->sc_reqw.phase = 0;		/* first phase */
    580 	bcopy((char *) bp->lb_data /* + sc->sc_waccsz * phase */,
    581 	    sc->sc_reqw.data, sc->sc_waccsz);
    582 	maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
    583 	    MAPLE_COMMAND_BWRITE, MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
    584 }
    585 
    586 static void
    587 mlcddone(sc)
    588 	struct mlcd_softc *sc;
    589 {
    590 	struct mlcd_buf *bp;
    591 
    592 	/* terminate current transfer */
    593 	bp = sc->sc_bp;
    594 	KASSERT(bp);
    595 	sc->sc_bp = NULL;
    596 	wakeup(bp);
    597 
    598 	/* go next transfer */
    599 	mlcdstart(sc);
    600 }
    601 
    602 /*
    603  * allocate a buffer for one block
    604  *
    605  * return NULL if
    606  *	[flags == M_NOWAIT] out of buffer space
    607  *	[flags == M_WAITOK] device detach detected
    608  */
    609 static struct mlcd_buf *
    610 mlcd_buf_alloc(dev, flags)
    611 	int dev;
    612 	int flags;	/* flags for malloc() */
    613 {
    614 	struct mlcd_softc *sc;
    615 	struct mlcd_pt *pt;
    616 	int unit, part;
    617 	struct mlcd_buf *bp;
    618 
    619 	unit = MLCD_UNIT(dev);
    620 	part = MLCD_PART(dev);
    621 	sc = mlcd_cd.cd_devs[unit];
    622 	KASSERT(sc);
    623 	pt = &sc->sc_pt[part];
    624 	KASSERT(pt);
    625 
    626 	if ((bp = malloc(MLCD_BUF_SZ(sc), M_DEVBUF, flags)) == NULL)
    627 		return bp;
    628 
    629 	/*
    630 	 * malloc() may sleep, and the device may be detached during sleep.
    631 	 * XXX this check is not complete.
    632 	 */
    633 	if (sc != device_lookup(&mlcd_cd, unit)
    634 	    || sc->sc_stat == MLCD_INIT
    635 	    || sc->sc_stat == MLCD_INIT2
    636 	    || part >= sc->sc_npt || pt != &sc->sc_pt[part]
    637 	    || pt->pt_flags == 0) {
    638 		free(bp, M_DEVBUF);
    639 		return NULL;
    640 	}
    641 
    642 	bp->lb_error = 0;
    643 
    644 	return bp;
    645 }
    646 
    647 static void
    648 mlcd_buf_free(bp)
    649 	struct mlcd_buf *bp;
    650 {
    651 
    652 	free(bp, M_DEVBUF);
    653 }
    654 
    655 /* invert order of bits */
    656 static __inline u_int32_t
    657 reverse_32(b)
    658 	u_int32_t b;
    659 {
    660 	u_int32_t b1;
    661 
    662 	/* invert every 8bit */
    663 	b1 = (b & 0x55555555) << 1;  b = (b >> 1) & 0x55555555;  b |= b1;
    664 	b1 = (b & 0x33333333) << 2;  b = (b >> 2) & 0x33333333;  b |= b1;
    665 	b1 = (b & 0x0f0f0f0f) << 4;  b = (b >> 4) & 0x0f0f0f0f;  b |= b1;
    666 
    667 	/* invert byte order */
    668 	return bswap32(b);
    669 }
    670 
    671 static void
    672 mlcd_rotate_bitmap(ptr, size)
    673 	void *ptr;
    674 	size_t size;
    675 {
    676 	u_int32_t *p, *q, tmp;
    677 
    678 	KDASSERT(size % sizeof(u_int32_t) == 0);
    679 	for (p = ptr, q = (void *)((char *)ptr + size); p < q; ) {
    680 		tmp = reverse_32(*p);
    681 		*p++ = reverse_32(*--q);
    682 		*q = tmp;
    683 	}
    684 }
    685 
    686 /* ARGSUSED2 */
    687 int
    688 mlcdwrite(dev, uio, flags)
    689 	dev_t	dev;
    690 	struct	uio *uio;
    691 	int	flags;
    692 {
    693 	struct mlcd_softc *sc;
    694 	struct mlcd_pt *pt;
    695 	struct mlcd_buf *bp;
    696 	int part;
    697 	off_t devsize;
    698 	int error = 0;
    699 
    700 	part = MLCD_PART(dev);
    701 	sc = mlcd_cd.cd_devs[MLCD_UNIT(dev)];
    702 	pt = &sc->sc_pt[part];
    703 
    704 #if 0
    705 	printf("%s: mlcdwrite: offset %ld, size %d\n",
    706 	    pt->pt_name, (long) uio->uio_offset, uio->uio_resid);
    707 #endif
    708 
    709 	devsize = pt->pt_nblk * sc->sc_bsize;
    710 	if (uio->uio_offset % sc->sc_bsize || uio->uio_offset > devsize)
    711 		return EINVAL;
    712 
    713 	if ((bp = mlcd_buf_alloc(dev, M_WAITOK)) == NULL)
    714 		return EIO;	/* device is detached during allocation */
    715 
    716 	bp->lb_partno = part;
    717 
    718 	while (uio->uio_offset < devsize
    719 	    && uio->uio_resid >= (size_t) sc->sc_bsize) {
    720 		/* invert block number if upside-down */
    721 		bp->lb_blkno = (sc->sc_direction == MAPLE_CONN_TOP) ?
    722 		    pt->pt_nblk - uio->uio_offset / sc->sc_bsize - 1 :
    723 		    uio->uio_offset / sc->sc_bsize;
    724 
    725 		if ((error = uiomove(bp->lb_data, sc->sc_bsize, uio)) != 0)
    726 			break;
    727 
    728 		if (sc->sc_direction == MAPLE_CONN_TOP) {
    729 			/* the LCD is upside-down */
    730 			mlcd_rotate_bitmap(bp->lb_data, sc->sc_bsize);
    731 		}
    732 
    733 		/* queue this transfer */
    734 		SIMPLEQ_INSERT_TAIL(&sc->sc_q, bp, lb_q);
    735 
    736 		if (sc->sc_stat == MLCD_IDLE)
    737 			mlcdstart(sc);
    738 
    739 		tsleep(bp, PRIBIO + 1, "mlcdbuf", 0);
    740 
    741 		if ((error = bp->lb_error) != 0) {
    742 			uio->uio_resid += sc->sc_bsize;
    743 			break;
    744 		}
    745 	}
    746 
    747 	mlcd_buf_free(bp);
    748 
    749 	return error;
    750 }
    751 
    752 int
    753 mlcdioctl(dev, cmd, data, flag, p)
    754 	dev_t dev;
    755 	u_long cmd;
    756 	caddr_t data;
    757 	int flag;
    758 	struct proc *p;
    759 {
    760 	int unit, part;
    761 	struct mlcd_softc *sc;
    762 	struct mlcd_pt *pt;
    763 
    764 	unit = MLCD_UNIT(dev);
    765 	part = MLCD_PART(dev);
    766 	sc = mlcd_cd.cd_devs[unit];
    767 	pt = &sc->sc_pt[part];
    768 
    769 	switch (cmd) {
    770 
    771 	default:
    772 		/* generic maple ioctl */
    773 		return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data,
    774 		    flag, p);
    775 	}
    776 
    777 	return 0;
    778 }
    779