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