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