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