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