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