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