Home | History | Annotate | Line # | Download | only in maple
      1 /*	$NetBSD: mlcd.c,v 1.18 2014/07/25 08:10:32 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.18 2014/07/25 08:10:32 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_discard = nodiscard,
    183 	.d_flag = 0
    184 };
    185 
    186 CFATTACH_DECL_NEW(mlcd, sizeof(struct mlcd_softc),
    187     mlcdmatch, mlcdattach, mlcddetach, NULL);
    188 
    189 /* initial image "NetBSD dreamcast" */
    190 static const char initimg48x32[192] = {
    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 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    194 	0x1c, 0x70, 0x00, 0x7e, 0x1c, 0xf0, 0x0c, 0x60, 0x00, 0x33, 0x26, 0x6c,
    195 	0x0c, 0x60, 0x0c, 0x33, 0x66, 0x66, 0x1e, 0xc7, 0x0c, 0x62, 0x60, 0xc6,
    196 	0x1a, 0xc9, 0xbe, 0x7c, 0x30, 0xc6, 0x1a, 0xdb, 0x98, 0x66, 0x18, 0xc6,
    197 	0x1a, 0xdc, 0x18, 0x66, 0x0d, 0x8c, 0x31, 0xb0, 0x32, 0xc6, 0x8d, 0x8c,
    198 	0x31, 0xb1, 0x36, 0xcd, 0x99, 0x98, 0x71, 0x9e, 0x1d, 0xf9, 0xf3, 0xe0,
    199 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    200 	0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08,
    201 	0x1d, 0x6c, 0x63, 0xc7, 0x30, 0xde, 0x25, 0x92, 0x12, 0xa8, 0x09, 0x08,
    202 	0x25, 0x1e, 0x72, 0xa8, 0x38, 0xc8, 0x25, 0x10, 0x92, 0xa8, 0x48, 0x28,
    203 	0x1d, 0x0e, 0x6a, 0xa7, 0x35, 0xc6, 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 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    207 };
    208 
    209 /* ARGSUSED */
    210 static int
    211 mlcdmatch(device_t parent, cfdata_t cf, 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(device_t parent, device_t self, void *aux)
    220 {
    221 	struct mlcd_softc *sc = device_private(self);
    222 	struct maple_attach_args *ma = aux;
    223 	int i;
    224 	union {
    225 		uint32_t v;
    226 		struct mlcd_funcdef s;
    227 	} funcdef;
    228 
    229 	sc->sc_dev = self;
    230 	sc->sc_parent = parent;
    231 	sc->sc_unit = ma->ma_unit;
    232 	sc->sc_direction = ma->ma_basedevinfo->di_connector_direction;
    233 
    234 	funcdef.v = maple_get_function_data(ma->ma_devinfo, MAPLE_FN_LCD);
    235 	printf(": LCD display\n");
    236 	printf("%s: %d LCD, %d bytes/block, ",
    237 	    device_xname(self),
    238 	    sc->sc_npt = funcdef.s.pt + 1,
    239 	    sc->sc_bsize = (funcdef.s.bb + 1) << 5);
    240 	if ((sc->sc_wacc = funcdef.s.wa) == 0)
    241 		printf("no ");
    242 	else
    243 		printf("%d acc/", sc->sc_wacc);
    244 	printf("write, %s, norm %s%s\n",
    245 	    funcdef.s.hv ? "vert" : "horiz",
    246 	    funcdef.s.bw ? "black" : "white",
    247 	    sc->sc_direction == MAPLE_CONN_TOP ? ", upside-down" : "");
    248 
    249 	/*
    250 	 * start init sequence
    251 	 */
    252 	sc->sc_stat = MLCD_INIT;
    253 	SIMPLEQ_INIT(&sc->sc_q);
    254 
    255 	/* check consistency */
    256 	if (sc->sc_wacc != 0) {
    257 		sc->sc_waccsz = sc->sc_bsize / sc->sc_wacc;
    258 		if (sc->sc_bsize != sc->sc_waccsz * sc->sc_wacc) {
    259 			printf("%s: write access isn't equally divided\n",
    260 			    device_xname(self));
    261 			sc->sc_wacc = 0;	/* no write */
    262 		} else if (sc->sc_waccsz > MLCD_MAXACCSIZE) {
    263 			printf("%s: write access size is too large\n",
    264 			    device_xname(self));
    265 			sc->sc_wacc = 0;	/* no write */
    266 		}
    267 	}
    268 	if (sc->sc_wacc == 0) {
    269 		printf("%s: device doesn't support write\n",
    270 		    device_xname(self));
    271 		return;
    272 	}
    273 
    274 	/* per-part structure */
    275 	sc->sc_pt = malloc(sizeof(struct mlcd_pt) * sc->sc_npt, M_DEVBUF,
    276 	    M_WAITOK|M_ZERO);
    277 
    278 	for (i = 0; i < sc->sc_npt; i++) {
    279 		snprintf(sc->sc_pt[i].pt_name, sizeof(sc->sc_pt[i].pt_name),
    280 		    "%s.%d", device_xname(self), 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 = htobe32(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(device_t self, int flags)
    298 {
    299 	struct mlcd_softc *sc = device_private(self);
    300 	struct mlcd_buf *bp;
    301 	int minor_l, minor_h;
    302 
    303 	sc->sc_stat = MLCD_DETACH;	/* just in case */
    304 
    305 	/*
    306 	 * kill pending I/O
    307 	 */
    308 	if ((bp = sc->sc_bp) != NULL) {
    309 		bp->lb_error = EIO;
    310 		wakeup(bp);
    311 	}
    312 	while ((bp = SIMPLEQ_FIRST(&sc->sc_q)) != NULL) {
    313 		SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
    314 		bp->lb_error = EIO;
    315 		wakeup(bp);
    316 	}
    317 
    318 	/*
    319 	 * revoke vnodes
    320 	 */
    321 	minor_l = MLCD_MINOR(device_unit(self), 0);
    322 	minor_h = MLCD_MINOR(device_unit(self), sc->sc_npt - 1);
    323 	vdevgone(cdevsw_lookup_major(&mlcd_cdevsw), minor_l, minor_h, VCHR);
    324 
    325 	/*
    326 	 * free per-partition structure
    327 	 */
    328 	if (sc->sc_pt)
    329 		free(sc->sc_pt, M_DEVBUF);
    330 
    331 	return 0;
    332 }
    333 
    334 /*
    335  * called back from maple bus driver
    336  */
    337 /* ARGSUSED3 */
    338 static void
    339 mlcd_intr(void *arg, struct maple_response *response, int sz, int flags)
    340 {
    341 	struct mlcd_softc *sc = arg;
    342 	struct mlcd_response_media_info *rm = (void *) response->data;
    343 	struct mlcd_buf *bp;
    344 	int part;
    345 	struct mlcd_pt *pt;
    346 
    347 	switch (sc->sc_stat) {
    348 	case MLCD_INIT:
    349 		/* checking part geometry */
    350 		part = sc->sc_reqm.pt;
    351 		pt = &sc->sc_pt[part];
    352 		switch ((maple_response_t) response->response_code) {
    353 		case MAPLE_RESPONSE_DATATRF:
    354 			pt->pt_info = rm->info;
    355 			pt->pt_size = ((pt->pt_info.width + 1) *
    356 			    (pt->pt_info.height + 1) + 7) / 8;
    357 			pt->pt_nblk = pt->pt_size / sc->sc_bsize;
    358 			printf("%s: %dx%d display, %d bytes\n",
    359 			    pt->pt_name,
    360 			    pt->pt_info.width + 1, pt->pt_info.height + 1,
    361 			    pt->pt_size);
    362 
    363 			/* this partition is active */
    364 			pt->pt_flags = MLCD_PT_OK;
    365 
    366 			break;
    367 		default:
    368 			printf("%s: init: unexpected response %#x, sz %d\n",
    369 			    pt->pt_name, be32toh(response->response_code), sz);
    370 			break;
    371 		}
    372 		if (++part == sc->sc_npt) {
    373 			/* init done */
    374 
    375 			/* XXX initial image for Visual Memory */
    376 			if (sc->sc_pt[0].pt_size == sizeof initimg48x32 &&
    377 			    sc->sc_waccsz == sizeof initimg48x32 &&
    378 			    sc->sc_wacc == 1) {
    379 				sc->sc_stat = MLCD_INIT2;
    380 				sc->sc_reqw.func_code =
    381 				    htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
    382 				sc->sc_reqw.pt = 0;	/* part 0 */
    383 				sc->sc_reqw.block = 0;
    384 				sc->sc_reqw.phase = 0;
    385 				memcpy(sc->sc_reqw.data, initimg48x32,
    386 				    sizeof initimg48x32);
    387 				if (sc->sc_direction == MAPLE_CONN_TOP) {
    388 					/* the LCD is upside-down */
    389 					mlcd_rotate_bitmap(sc->sc_reqw.data,
    390 					    sizeof initimg48x32);
    391 				}
    392 				maple_command(sc->sc_parent, sc->sc_unit,
    393 				    MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
    394 				    MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
    395 			} else
    396 				sc->sc_stat = MLCD_IDLE;	/* init done */
    397 		} else {
    398 			sc->sc_reqm.pt = part;
    399 			maple_command(sc->sc_parent, sc->sc_unit,
    400 			    MAPLE_FN_LCD, MAPLE_COMMAND_GETMINFO,
    401 			    sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
    402 		}
    403 		break;
    404 
    405 	case MLCD_INIT2:
    406 		sc->sc_stat = MLCD_IDLE;	/* init done */
    407 		break;
    408 
    409 	case MLCD_WRITE:
    410 		bp = sc->sc_bp;
    411 
    412 		switch ((maple_response_t) response->response_code) {
    413 		case MAPLE_RESPONSE_OK:			/* write done */
    414 			if (++sc->sc_reqw.phase == sc->sc_wacc) {
    415 				/* all phase done */
    416 				mlcddone(sc);
    417 			} else {
    418 				/* go next phase */
    419 				memcpy(sc->sc_reqw.data,
    420 				    (char *)bp->lb_data +
    421 				    sc->sc_waccsz * sc->sc_reqw.phase,
    422 				    sc->sc_waccsz);
    423 				maple_command(sc->sc_parent, sc->sc_unit,
    424 				    MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
    425 				    MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
    426 			}
    427 			break;
    428 		case MAPLE_RESPONSE_LCDERR:
    429 			mlcd_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name,
    430 			    rm->func_code /* XXX */);
    431 			mlcdstart_bp(sc);		/* retry */
    432 			break;
    433 		default:
    434 			printf("%s: write: unexpected response %#x, %#x, sz %d\n",
    435 			    sc->sc_pt[sc->sc_reqw.pt].pt_name,
    436 			    be32toh(response->response_code),
    437 			    be32toh(rm->func_code), sz);
    438 			mlcdstart_bp(sc);		/* retry */
    439 			break;
    440 		}
    441 		break;
    442 
    443 	default:
    444 		break;
    445 	}
    446 }
    447 
    448 static void
    449 mlcd_printerror(const char *head, uint32_t code)
    450 {
    451 
    452 	printf("%s:", head);
    453 	NTOHL(code);
    454 	if (code & 1)
    455 		printf(" PT error");
    456 	if (code & 2)
    457 		printf(" Phase error");
    458 	if (code & 4)
    459 		printf(" Block error");
    460 	if (code & 010)
    461 		printf(" Write error");
    462 	if (code & 020)
    463 		printf(" Length error");
    464 	if (code & ~037)
    465 		printf(" Unknown error %#x", code & ~037);
    466 	printf("\n");
    467 }
    468 
    469 /* ARGSUSED */
    470 int
    471 mlcdopen(dev_t dev, int flags, int devtype, struct lwp *l)
    472 {
    473 	int unit, part;
    474 	struct mlcd_softc *sc;
    475 	struct mlcd_pt *pt;
    476 
    477 	unit = MLCD_UNIT(dev);
    478 	part = MLCD_PART(dev);
    479 	if ((sc = device_lookup_private(&mlcd_cd, unit)) == NULL
    480 	    || sc->sc_stat == MLCD_INIT
    481 	    || sc->sc_stat == MLCD_INIT2
    482 	    || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0)
    483 		return ENXIO;
    484 
    485 	if (pt->pt_flags & MLCD_PT_OPEN)
    486 		return EBUSY;
    487 
    488 	pt->pt_flags |= MLCD_PT_OPEN;
    489 
    490 	return 0;
    491 }
    492 
    493 /* ARGSUSED */
    494 int
    495 mlcdclose(dev_t dev, int flags, int devtype, struct lwp *l)
    496 {
    497 	int unit, part;
    498 	struct mlcd_softc *sc;
    499 	struct mlcd_pt *pt;
    500 
    501 	unit = MLCD_UNIT(dev);
    502 	part = MLCD_PART(dev);
    503 	sc = device_lookup_private(&mlcd_cd, unit);
    504 	pt = &sc->sc_pt[part];
    505 
    506 	pt->pt_flags &= ~MLCD_PT_OPEN;
    507 
    508 	return 0;
    509 }
    510 
    511 /*
    512  * start I/O operations
    513  */
    514 static void
    515 mlcdstart(struct mlcd_softc *sc)
    516 {
    517 	struct mlcd_buf *bp;
    518 
    519 	if ((bp = SIMPLEQ_FIRST(&sc->sc_q)) == NULL) {
    520 		sc->sc_stat = MLCD_IDLE;
    521 		maple_enable_unit_ping(sc->sc_parent, sc->sc_unit,
    522 		    MAPLE_FN_LCD, 1);
    523 		return;
    524 	}
    525 
    526 	SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
    527 
    528 	sc->sc_bp = bp;
    529 	sc->sc_retry = 0;
    530 	mlcdstart_bp(sc);
    531 }
    532 
    533 /*
    534  * start/retry a specified I/O operation
    535  */
    536 static void
    537 mlcdstart_bp(struct mlcd_softc *sc)
    538 {
    539 	struct mlcd_buf *bp;
    540 
    541 	bp = sc->sc_bp;
    542 
    543 	/* handle retry */
    544 	if (sc->sc_retry++ > MLCD_MAXRETRY) {
    545 		/* retry count exceeded */
    546 		bp->lb_error = EIO;
    547 		mlcddone(sc);
    548 		return;
    549 	}
    550 
    551 	/*
    552 	 * I/O access will fail if the removal detection (by maple driver)
    553 	 * occurs before finishing the I/O, so disable it.
    554 	 * We are sending commands, and the removal detection is still alive.
    555 	 */
    556 	maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD, 0);
    557 
    558 	/*
    559 	 * Start the first phase (phase# = 0).
    560 	 */
    561 	/* write */
    562 	sc->sc_stat = MLCD_WRITE;
    563 	sc->sc_reqw.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
    564 	sc->sc_reqw.pt = bp->lb_partno;
    565 	sc->sc_reqw.block = htobe16(bp->lb_blkno);
    566 	sc->sc_reqw.phase = 0;		/* first phase */
    567 	memcpy(sc->sc_reqw.data,
    568 	    (char *) bp->lb_data /* + sc->sc_waccsz * phase */, sc->sc_waccsz);
    569 	maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
    570 	    MAPLE_COMMAND_BWRITE, MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
    571 }
    572 
    573 static void
    574 mlcddone(struct mlcd_softc *sc)
    575 {
    576 	struct mlcd_buf *bp;
    577 
    578 	/* terminate current transfer */
    579 	bp = sc->sc_bp;
    580 	KASSERT(bp);
    581 	sc->sc_bp = NULL;
    582 	wakeup(bp);
    583 
    584 	/* go next transfer */
    585 	mlcdstart(sc);
    586 }
    587 
    588 /*
    589  * allocate a buffer for one block
    590  *
    591  * return NULL if
    592  *	[flags == M_NOWAIT] out of buffer space
    593  *	[flags == M_WAITOK] device detach detected
    594  */
    595 static struct mlcd_buf *
    596 mlcd_buf_alloc(int dev, int flags)
    597 {
    598 	struct mlcd_softc *sc;
    599 	struct mlcd_pt *pt;
    600 	int unit, part;
    601 	struct mlcd_buf *bp;
    602 
    603 	unit = MLCD_UNIT(dev);
    604 	part = MLCD_PART(dev);
    605 	sc = device_lookup_private(&mlcd_cd, unit);
    606 	KASSERT(sc);
    607 	pt = &sc->sc_pt[part];
    608 	KASSERT(pt);
    609 
    610 	if ((bp = malloc(MLCD_BUF_SZ(sc), M_DEVBUF, flags)) == NULL)
    611 		return bp;
    612 
    613 	/*
    614 	 * malloc() may sleep, and the device may be detached during sleep.
    615 	 * XXX this check is not complete.
    616 	 */
    617 	if (sc != device_lookup_private(&mlcd_cd, unit)
    618 	    || sc->sc_stat == MLCD_INIT
    619 	    || sc->sc_stat == MLCD_INIT2
    620 	    || part >= sc->sc_npt || pt != &sc->sc_pt[part]
    621 	    || pt->pt_flags == 0) {
    622 		free(bp, M_DEVBUF);
    623 		return NULL;
    624 	}
    625 
    626 	bp->lb_error = 0;
    627 
    628 	return bp;
    629 }
    630 
    631 static void
    632 mlcd_buf_free(struct mlcd_buf *bp)
    633 {
    634 
    635 	free(bp, M_DEVBUF);
    636 }
    637 
    638 /* invert order of bits */
    639 static inline uint32_t
    640 reverse_32(uint32_t b)
    641 {
    642 	uint32_t b1;
    643 
    644 	/* invert every 8bit */
    645 	b1 = (b & 0x55555555) << 1;  b = (b >> 1) & 0x55555555;  b |= b1;
    646 	b1 = (b & 0x33333333) << 2;  b = (b >> 2) & 0x33333333;  b |= b1;
    647 	b1 = (b & 0x0f0f0f0f) << 4;  b = (b >> 4) & 0x0f0f0f0f;  b |= b1;
    648 
    649 	/* invert byte order */
    650 	return bswap32(b);
    651 }
    652 
    653 static void
    654 mlcd_rotate_bitmap(void *ptr, size_t size)
    655 {
    656 	uint32_t *p, *q, tmp;
    657 
    658 	KDASSERT(size % sizeof(uint32_t) == 0);
    659 	for (p = ptr, q = (void *)((char *)ptr + size); p < q; ) {
    660 		tmp = reverse_32(*p);
    661 		*p++ = reverse_32(*--q);
    662 		*q = tmp;
    663 	}
    664 }
    665 
    666 /* ARGSUSED2 */
    667 int
    668 mlcdwrite(dev_t dev, struct uio *uio, int flags)
    669 {
    670 	struct mlcd_softc *sc;
    671 	struct mlcd_pt *pt;
    672 	struct mlcd_buf *bp;
    673 	int part;
    674 	off_t devsize;
    675 	int error = 0;
    676 
    677 	part = MLCD_PART(dev);
    678 	sc = device_lookup_private(&mlcd_cd, MLCD_UNIT(dev));
    679 	pt = &sc->sc_pt[part];
    680 
    681 #if 0
    682 	printf("%s: mlcdwrite: offset %ld, size %d\n",
    683 	    pt->pt_name, (long) uio->uio_offset, uio->uio_resid);
    684 #endif
    685 
    686 	devsize = pt->pt_nblk * sc->sc_bsize;
    687 	if (uio->uio_offset % sc->sc_bsize || uio->uio_offset > devsize)
    688 		return EINVAL;
    689 
    690 	if ((bp = mlcd_buf_alloc(dev, M_WAITOK)) == NULL)
    691 		return EIO;	/* device is detached during allocation */
    692 
    693 	bp->lb_partno = part;
    694 
    695 	while (uio->uio_offset < devsize
    696 	    && uio->uio_resid >= (size_t) sc->sc_bsize) {
    697 		/* invert block number if upside-down */
    698 		bp->lb_blkno = (sc->sc_direction == MAPLE_CONN_TOP) ?
    699 		    pt->pt_nblk - uio->uio_offset / sc->sc_bsize - 1 :
    700 		    uio->uio_offset / sc->sc_bsize;
    701 
    702 		if ((error = uiomove(bp->lb_data, sc->sc_bsize, uio)) != 0)
    703 			break;
    704 
    705 		if (sc->sc_direction == MAPLE_CONN_TOP) {
    706 			/* the LCD is upside-down */
    707 			mlcd_rotate_bitmap(bp->lb_data, sc->sc_bsize);
    708 		}
    709 
    710 		/* queue this transfer */
    711 		SIMPLEQ_INSERT_TAIL(&sc->sc_q, bp, lb_q);
    712 
    713 		if (sc->sc_stat == MLCD_IDLE)
    714 			mlcdstart(sc);
    715 
    716 		tsleep(bp, PRIBIO + 1, "mlcdbuf", 0);
    717 
    718 		if ((error = bp->lb_error) != 0) {
    719 			uio->uio_resid += sc->sc_bsize;
    720 			break;
    721 		}
    722 	}
    723 
    724 	mlcd_buf_free(bp);
    725 
    726 	return error;
    727 }
    728 
    729 int
    730 mlcdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
    731 {
    732 	int unit;
    733 	struct mlcd_softc *sc;
    734 
    735 	unit = MLCD_UNIT(dev);
    736 	sc = device_lookup_private(&mlcd_cd, unit);
    737 
    738 	switch (cmd) {
    739 
    740 	default:
    741 		/* generic maple ioctl */
    742 		return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data,
    743 		    flag, l);
    744 	}
    745 
    746 	return 0;
    747 }
    748