No empty .Rs/.Re
[netbsd-mini2440.git] / sys / arch / dreamcast / dev / maple / mlcd.c
blobe6c94d29886a52b8b12a14e27f8198e9c73e432b
1 /* $NetBSD: mlcd.c,v 1.10 2008/06/08 16:39:43 tsutsui Exp $ */
3 /*-
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by ITOH Yasufumi.
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.
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.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: mlcd.c,v 1.10 2008/06/08 16:39:43 tsutsui Exp $");
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>
44 #include <dreamcast/dev/maple/maple.h>
45 #include <dreamcast/dev/maple/mapleconf.h>
47 #define MLCD_MAXACCSIZE 1012 /* (255*4) - 8 = 253*32 / 8 */
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 */
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];
66 #define MLCD_SIZE_REQW(sc) ((sc)->sc_waccsz + 8)
68 struct mlcd_request_get_media_info {
69 uint32_t func_code;
70 uint32_t pt; /* pt (1 byte) and unused 3 bytes */
73 struct mlcd_media_info {
74 uint8_t width; /* width - 1 */
75 uint8_t height; /* height - 1 */
76 uint8_t rsvd[2]; /* ? 0x10 0x02 */
79 struct mlcd_response_media_info {
80 uint32_t func_code; /* function code (big endian) */
81 struct mlcd_media_info info;
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 */
91 #define MLCD_BUF_SZ(sc) (offsetof(struct mlcd_buf, lb_data) + (sc)->sc_bsize)
93 struct mlcd_softc {
94 struct device sc_dev;
96 struct device *sc_parent;
97 struct maple_unit *sc_unit;
98 int sc_direction;
99 enum mlcd_stat {
100 MLCD_INIT, /* during initialization */
101 MLCD_INIT2, /* during initialization */
102 MLCD_IDLE, /* init done, not in I/O */
103 MLCD_WRITE, /* in write operation */
104 MLCD_DETACH /* detaching */
105 } sc_stat;
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 */
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 */
120 char pt_name[16 /* see device.h */ + 4 /* ".255" */];
121 } *sc_pt;
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
131 /* pending buffers */
132 SIMPLEQ_HEAD(mlcd_bufq, mlcd_buf) sc_q;
134 /* current I/O access */
135 struct mlcd_buf *sc_bp;
136 int sc_retry;
137 #define MLCD_MAXRETRY 10
141 * minor number layout (mlcddetach() depends on this layout):
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
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))
151 static int mlcdmatch(struct device *, struct cfdata *, void *);
152 static void mlcdattach(struct device *, struct device *, void *);
153 static int mlcddetach(struct device *, int);
154 static void mlcd_intr(void *, struct maple_response *, int, int);
155 static void mlcd_printerror(const char *, uint32_t);
156 static struct mlcd_buf *mlcd_buf_alloc(int /*dev*/, int /*flags*/);
157 static void mlcd_buf_free(struct mlcd_buf *);
158 static inline uint32_t reverse_32(uint32_t);
159 static void mlcd_rotate_bitmap(void *, size_t);
160 static void mlcdstart(struct mlcd_softc *);
161 static void mlcdstart_bp(struct mlcd_softc *);
162 static void mlcddone(struct mlcd_softc *);
164 dev_type_open(mlcdopen);
165 dev_type_close(mlcdclose);
166 dev_type_write(mlcdwrite);
167 dev_type_ioctl(mlcdioctl);
169 const struct cdevsw mlcd_cdevsw = {
170 mlcdopen, mlcdclose, noread, mlcdwrite, mlcdioctl,
171 nostop, notty, nopoll, nommap, nokqfilter
174 CFATTACH_DECL(mlcd, sizeof(struct mlcd_softc),
175 mlcdmatch, mlcdattach, mlcddetach, NULL);
177 extern struct cfdriver mlcd_cd;
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
199 /* ARGSUSED */
200 static int
201 mlcdmatch(struct device *parent, struct cfdata *cf, void *aux)
203 struct maple_attach_args *ma = aux;
205 return (ma->ma_function == MAPLE_FN_LCD ? MAPLE_MATCH_FUNC : 0);
208 static void
209 mlcdattach(struct device *parent, struct device *self, void *aux)
211 struct mlcd_softc *sc = (void *) self;
212 struct maple_attach_args *ma = aux;
213 int i;
214 union {
215 uint32_t v;
216 struct mlcd_funcdef s;
217 } funcdef;
219 sc->sc_parent = parent;
220 sc->sc_unit = ma->ma_unit;
221 sc->sc_direction = ma->ma_basedevinfo->di_connector_direction;
223 funcdef.v = maple_get_function_data(ma->ma_devinfo, MAPLE_FN_LCD);
224 printf(": LCD display\n");
225 printf("%s: %d LCD, %d bytes/block, ",
226 sc->sc_dev.dv_xname,
227 sc->sc_npt = funcdef.s.pt + 1,
228 sc->sc_bsize = (funcdef.s.bb + 1) << 5);
229 if ((sc->sc_wacc = funcdef.s.wa) == 0)
230 printf("no ");
231 else
232 printf("%d acc/", sc->sc_wacc);
233 printf("write, %s, norm %s%s\n",
234 funcdef.s.hv ? "vert" : "horiz",
235 funcdef.s.bw ? "black" : "white",
236 sc->sc_direction == MAPLE_CONN_TOP ? ", upside-down" : "");
239 * start init sequence
241 sc->sc_stat = MLCD_INIT;
242 SIMPLEQ_INIT(&sc->sc_q);
244 /* check consistency */
245 if (sc->sc_wacc != 0) {
246 sc->sc_waccsz = sc->sc_bsize / sc->sc_wacc;
247 if (sc->sc_bsize != sc->sc_waccsz * sc->sc_wacc) {
248 printf("%s: write access isn't equally divided\n",
249 sc->sc_dev.dv_xname);
250 sc->sc_wacc = 0; /* no write */
251 } else if (sc->sc_waccsz > MLCD_MAXACCSIZE) {
252 printf("%s: write access size is too large\n",
253 sc->sc_dev.dv_xname);
254 sc->sc_wacc = 0; /* no write */
257 if (sc->sc_wacc == 0) {
258 printf("%s: device doesn't support write\n",
259 sc->sc_dev.dv_xname);
260 return;
263 /* per-part structure */
264 sc->sc_pt = malloc(sizeof(struct mlcd_pt) * sc->sc_npt, M_DEVBUF,
265 M_WAITOK|M_ZERO);
267 for (i = 0; i < sc->sc_npt; i++) {
268 sprintf(sc->sc_pt[i].pt_name, "%s.%d", sc->sc_dev.dv_xname, i);
271 maple_set_callback(parent, sc->sc_unit, MAPLE_FN_LCD,
272 mlcd_intr, sc);
275 * get size (start from partition 0)
277 sc->sc_reqm.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
278 sc->sc_reqm.pt = 0;
279 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
280 MAPLE_COMMAND_GETMINFO, sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
283 /* ARGSUSED1 */
284 static int
285 mlcddetach(struct device *self, int flags)
287 struct mlcd_softc *sc = (struct mlcd_softc *) self;
288 struct mlcd_buf *bp;
289 int minor_l, minor_h;
291 sc->sc_stat = MLCD_DETACH; /* just in case */
294 * kill pending I/O
296 if ((bp = sc->sc_bp) != NULL) {
297 bp->lb_error = EIO;
298 wakeup(bp);
300 while ((bp = SIMPLEQ_FIRST(&sc->sc_q)) != NULL) {
301 SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
302 bp->lb_error = EIO;
303 wakeup(bp);
307 * revoke vnodes
309 minor_l = MLCD_MINOR(device_unit(self), 0);
310 minor_h = MLCD_MINOR(device_unit(self), sc->sc_npt - 1);
311 vdevgone(cdevsw_lookup_major(&mlcd_cdevsw), minor_l, minor_h, VCHR);
314 * free per-partition structure
316 if (sc->sc_pt)
317 free(sc->sc_pt, M_DEVBUF);
319 return 0;
323 * called back from maple bus driver
325 /* ARGSUSED3 */
326 static void
327 mlcd_intr(void *dev, struct maple_response *response, int sz, int flags)
329 struct mlcd_softc *sc = dev;
330 struct mlcd_response_media_info *rm = (void *) response->data;
331 struct mlcd_buf *bp;
332 int part;
333 struct mlcd_pt *pt;
335 switch (sc->sc_stat) {
336 case MLCD_INIT:
337 /* checking part geometry */
338 part = sc->sc_reqm.pt;
339 pt = &sc->sc_pt[part];
340 switch ((maple_response_t) response->response_code) {
341 case MAPLE_RESPONSE_DATATRF:
342 pt->pt_info = rm->info;
343 pt->pt_size = ((pt->pt_info.width + 1) *
344 (pt->pt_info.height + 1) + 7) / 8;
345 pt->pt_nblk = pt->pt_size / sc->sc_bsize;
346 printf("%s: %dx%d display, %d bytes\n",
347 pt->pt_name,
348 pt->pt_info.width + 1, pt->pt_info.height + 1,
349 pt->pt_size);
351 /* this partition is active */
352 pt->pt_flags = MLCD_PT_OK;
354 break;
355 default:
356 printf("%s: init: unexpected response %#x, sz %d\n",
357 pt->pt_name, be32toh(response->response_code), sz);
358 break;
360 if (++part == sc->sc_npt) {
361 /* init done */
363 /* XXX initial image for Visual Memory */
364 if (sc->sc_pt[0].pt_size == sizeof initimg48x32 &&
365 sc->sc_waccsz == sizeof initimg48x32 &&
366 sc->sc_wacc == 1) {
367 sc->sc_stat = MLCD_INIT2;
368 sc->sc_reqw.func_code =
369 htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
370 sc->sc_reqw.pt = 0; /* part 0 */
371 sc->sc_reqw.block = 0;
372 sc->sc_reqw.phase = 0;
373 memcpy(sc->sc_reqw.data, initimg48x32,
374 sizeof initimg48x32);
375 if (sc->sc_direction == MAPLE_CONN_TOP) {
376 /* the LCD is upside-down */
377 mlcd_rotate_bitmap(sc->sc_reqw.data,
378 sizeof initimg48x32);
380 maple_command(sc->sc_parent, sc->sc_unit,
381 MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
382 MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
383 } else
384 sc->sc_stat = MLCD_IDLE; /* init done */
385 } else {
386 sc->sc_reqm.pt = part;
387 maple_command(sc->sc_parent, sc->sc_unit,
388 MAPLE_FN_LCD, MAPLE_COMMAND_GETMINFO,
389 sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
391 break;
393 case MLCD_INIT2:
394 sc->sc_stat = MLCD_IDLE; /* init done */
395 break;
397 case MLCD_WRITE:
398 bp = sc->sc_bp;
400 switch ((maple_response_t) response->response_code) {
401 case MAPLE_RESPONSE_OK: /* write done */
402 if (++sc->sc_reqw.phase == sc->sc_wacc) {
403 /* all phase done */
404 mlcddone(sc);
405 } else {
406 /* go next phase */
407 memcpy(sc->sc_reqw.data,
408 (char *)bp->lb_data +
409 sc->sc_waccsz * sc->sc_reqw.phase,
410 sc->sc_waccsz);
411 maple_command(sc->sc_parent, sc->sc_unit,
412 MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
413 MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
415 break;
416 case MAPLE_RESPONSE_LCDERR:
417 mlcd_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name,
418 rm->func_code /* XXX */);
419 mlcdstart_bp(sc); /* retry */
420 break;
421 default:
422 printf("%s: write: unexpected response %#x, %#x, sz %d\n",
423 sc->sc_pt[sc->sc_reqw.pt].pt_name,
424 be32toh(response->response_code),
425 be32toh(rm->func_code), sz);
426 mlcdstart_bp(sc); /* retry */
427 break;
429 break;
431 default:
432 break;
436 static void
437 mlcd_printerror(const char *head, uint32_t code)
440 printf("%s:", head);
441 NTOHL(code);
442 if (code & 1)
443 printf(" PT error");
444 if (code & 2)
445 printf(" Phase error");
446 if (code & 4)
447 printf(" Block error");
448 if (code & 010)
449 printf(" Write error");
450 if (code & 020)
451 printf(" Length error");
452 if (code & ~037)
453 printf(" Unknown error %#x", code & ~037);
454 printf("\n");
457 /* ARGSUSED */
459 mlcdopen(dev_t dev, int flags, int devtype, struct lwp *l)
461 int unit, part;
462 struct mlcd_softc *sc;
463 struct mlcd_pt *pt;
465 unit = MLCD_UNIT(dev);
466 part = MLCD_PART(dev);
467 if ((sc = device_lookup_private(&mlcd_cd, unit)) == NULL
468 || sc->sc_stat == MLCD_INIT
469 || sc->sc_stat == MLCD_INIT2
470 || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0)
471 return ENXIO;
473 if (pt->pt_flags & MLCD_PT_OPEN)
474 return EBUSY;
476 pt->pt_flags |= MLCD_PT_OPEN;
478 return 0;
481 /* ARGSUSED */
483 mlcdclose(dev_t dev, int flags, int devtype, struct lwp *l)
485 int unit, part;
486 struct mlcd_softc *sc;
487 struct mlcd_pt *pt;
489 unit = MLCD_UNIT(dev);
490 part = MLCD_PART(dev);
491 sc = device_lookup_private(&mlcd_cd, unit);
492 pt = &sc->sc_pt[part];
494 pt->pt_flags &= ~MLCD_PT_OPEN;
496 return 0;
500 * start I/O operations
502 static void
503 mlcdstart(struct mlcd_softc *sc)
505 struct mlcd_buf *bp;
507 if ((bp = SIMPLEQ_FIRST(&sc->sc_q)) == NULL) {
508 sc->sc_stat = MLCD_IDLE;
509 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit,
510 MAPLE_FN_LCD, 1);
511 return;
514 SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
516 sc->sc_bp = bp;
517 sc->sc_retry = 0;
518 mlcdstart_bp(sc);
522 * start/retry a specified I/O operation
524 static void
525 mlcdstart_bp(struct mlcd_softc *sc)
527 struct mlcd_buf *bp;
528 struct mlcd_pt *pt;
530 bp = sc->sc_bp;
531 pt = &sc->sc_pt[bp->lb_partno];
533 /* handle retry */
534 if (sc->sc_retry++ > MLCD_MAXRETRY) {
535 /* retry count exceeded */
536 bp->lb_error = EIO;
537 mlcddone(sc);
538 return;
542 * I/O access will fail if the removal detection (by maple driver)
543 * occurs before finishing the I/O, so disable it.
544 * We are sending commands, and the removal detection is still alive.
546 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD, 0);
549 * Start the first phase (phase# = 0).
551 /* write */
552 sc->sc_stat = MLCD_WRITE;
553 sc->sc_reqw.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
554 sc->sc_reqw.pt = bp->lb_partno;
555 sc->sc_reqw.block = htobe16(bp->lb_blkno);
556 sc->sc_reqw.phase = 0; /* first phase */
557 memcpy(sc->sc_reqw.data,
558 (char *) bp->lb_data /* + sc->sc_waccsz * phase */, sc->sc_waccsz);
559 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
560 MAPLE_COMMAND_BWRITE, MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
563 static void
564 mlcddone(struct mlcd_softc *sc)
566 struct mlcd_buf *bp;
568 /* terminate current transfer */
569 bp = sc->sc_bp;
570 KASSERT(bp);
571 sc->sc_bp = NULL;
572 wakeup(bp);
574 /* go next transfer */
575 mlcdstart(sc);
579 * allocate a buffer for one block
581 * return NULL if
582 * [flags == M_NOWAIT] out of buffer space
583 * [flags == M_WAITOK] device detach detected
585 static struct mlcd_buf *
586 mlcd_buf_alloc(int dev, int flags)
588 struct mlcd_softc *sc;
589 struct mlcd_pt *pt;
590 int unit, part;
591 struct mlcd_buf *bp;
593 unit = MLCD_UNIT(dev);
594 part = MLCD_PART(dev);
595 sc = device_lookup_private(&mlcd_cd, unit);
596 KASSERT(sc);
597 pt = &sc->sc_pt[part];
598 KASSERT(pt);
600 if ((bp = malloc(MLCD_BUF_SZ(sc), M_DEVBUF, flags)) == NULL)
601 return bp;
604 * malloc() may sleep, and the device may be detached during sleep.
605 * XXX this check is not complete.
607 if (sc != device_lookup_private(&mlcd_cd, unit)
608 || sc->sc_stat == MLCD_INIT
609 || sc->sc_stat == MLCD_INIT2
610 || part >= sc->sc_npt || pt != &sc->sc_pt[part]
611 || pt->pt_flags == 0) {
612 free(bp, M_DEVBUF);
613 return NULL;
616 bp->lb_error = 0;
618 return bp;
621 static void
622 mlcd_buf_free(struct mlcd_buf *bp)
625 free(bp, M_DEVBUF);
628 /* invert order of bits */
629 static inline uint32_t
630 reverse_32(uint32_t b)
632 uint32_t b1;
634 /* invert every 8bit */
635 b1 = (b & 0x55555555) << 1; b = (b >> 1) & 0x55555555; b |= b1;
636 b1 = (b & 0x33333333) << 2; b = (b >> 2) & 0x33333333; b |= b1;
637 b1 = (b & 0x0f0f0f0f) << 4; b = (b >> 4) & 0x0f0f0f0f; b |= b1;
639 /* invert byte order */
640 return bswap32(b);
643 static void
644 mlcd_rotate_bitmap(void *ptr, size_t size)
646 uint32_t *p, *q, tmp;
648 KDASSERT(size % sizeof(uint32_t) == 0);
649 for (p = ptr, q = (void *)((char *)ptr + size); p < q; ) {
650 tmp = reverse_32(*p);
651 *p++ = reverse_32(*--q);
652 *q = tmp;
656 /* ARGSUSED2 */
658 mlcdwrite(dev_t dev, struct uio *uio, int flags)
660 struct mlcd_softc *sc;
661 struct mlcd_pt *pt;
662 struct mlcd_buf *bp;
663 int part;
664 off_t devsize;
665 int error = 0;
667 part = MLCD_PART(dev);
668 sc = device_lookup_private(&mlcd_cd, MLCD_UNIT(dev));
669 pt = &sc->sc_pt[part];
671 #if 0
672 printf("%s: mlcdwrite: offset %ld, size %d\n",
673 pt->pt_name, (long) uio->uio_offset, uio->uio_resid);
674 #endif
676 devsize = pt->pt_nblk * sc->sc_bsize;
677 if (uio->uio_offset % sc->sc_bsize || uio->uio_offset > devsize)
678 return EINVAL;
680 if ((bp = mlcd_buf_alloc(dev, M_WAITOK)) == NULL)
681 return EIO; /* device is detached during allocation */
683 bp->lb_partno = part;
685 while (uio->uio_offset < devsize
686 && uio->uio_resid >= (size_t) sc->sc_bsize) {
687 /* invert block number if upside-down */
688 bp->lb_blkno = (sc->sc_direction == MAPLE_CONN_TOP) ?
689 pt->pt_nblk - uio->uio_offset / sc->sc_bsize - 1 :
690 uio->uio_offset / sc->sc_bsize;
692 if ((error = uiomove(bp->lb_data, sc->sc_bsize, uio)) != 0)
693 break;
695 if (sc->sc_direction == MAPLE_CONN_TOP) {
696 /* the LCD is upside-down */
697 mlcd_rotate_bitmap(bp->lb_data, sc->sc_bsize);
700 /* queue this transfer */
701 SIMPLEQ_INSERT_TAIL(&sc->sc_q, bp, lb_q);
703 if (sc->sc_stat == MLCD_IDLE)
704 mlcdstart(sc);
706 tsleep(bp, PRIBIO + 1, "mlcdbuf", 0);
708 if ((error = bp->lb_error) != 0) {
709 uio->uio_resid += sc->sc_bsize;
710 break;
714 mlcd_buf_free(bp);
716 return error;
720 mlcdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
722 int unit, part;
723 struct mlcd_softc *sc;
724 struct mlcd_pt *pt;
726 unit = MLCD_UNIT(dev);
727 part = MLCD_PART(dev);
728 sc = device_lookup_private(&mlcd_cd, unit);
729 pt = &sc->sc_pt[part];
731 switch (cmd) {
733 default:
734 /* generic maple ioctl */
735 return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data,
736 flag, l);
739 return 0;