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