mlcd.c revision 1.15 1 /* $NetBSD: mlcd.c,v 1.15 2014/03/14 08:55:40 martin 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.15 2014/03/14 08:55:40 martin 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 mlcdopen, mlcdclose, noread, mlcdwrite, mlcdioctl,
173 nostop, notty, nopoll, nommap, nokqfilter
174 };
175
176 CFATTACH_DECL_NEW(mlcd, sizeof(struct mlcd_softc),
177 mlcdmatch, mlcdattach, mlcddetach, NULL);
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
530 bp = sc->sc_bp;
531
532 /* handle retry */
533 if (sc->sc_retry++ > MLCD_MAXRETRY) {
534 /* retry count exceeded */
535 bp->lb_error = EIO;
536 mlcddone(sc);
537 return;
538 }
539
540 /*
541 * I/O access will fail if the removal detection (by maple driver)
542 * occurs before finishing the I/O, so disable it.
543 * We are sending commands, and the removal detection is still alive.
544 */
545 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD, 0);
546
547 /*
548 * Start the first phase (phase# = 0).
549 */
550 /* write */
551 sc->sc_stat = MLCD_WRITE;
552 sc->sc_reqw.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
553 sc->sc_reqw.pt = bp->lb_partno;
554 sc->sc_reqw.block = htobe16(bp->lb_blkno);
555 sc->sc_reqw.phase = 0; /* first phase */
556 memcpy(sc->sc_reqw.data,
557 (char *) bp->lb_data /* + sc->sc_waccsz * phase */, sc->sc_waccsz);
558 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
559 MAPLE_COMMAND_BWRITE, MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
560 }
561
562 static void
563 mlcddone(struct mlcd_softc *sc)
564 {
565 struct mlcd_buf *bp;
566
567 /* terminate current transfer */
568 bp = sc->sc_bp;
569 KASSERT(bp);
570 sc->sc_bp = NULL;
571 wakeup(bp);
572
573 /* go next transfer */
574 mlcdstart(sc);
575 }
576
577 /*
578 * allocate a buffer for one block
579 *
580 * return NULL if
581 * [flags == M_NOWAIT] out of buffer space
582 * [flags == M_WAITOK] device detach detected
583 */
584 static struct mlcd_buf *
585 mlcd_buf_alloc(int dev, int flags)
586 {
587 struct mlcd_softc *sc;
588 struct mlcd_pt *pt;
589 int unit, part;
590 struct mlcd_buf *bp;
591
592 unit = MLCD_UNIT(dev);
593 part = MLCD_PART(dev);
594 sc = device_lookup_private(&mlcd_cd, unit);
595 KASSERT(sc);
596 pt = &sc->sc_pt[part];
597 KASSERT(pt);
598
599 if ((bp = malloc(MLCD_BUF_SZ(sc), M_DEVBUF, flags)) == NULL)
600 return bp;
601
602 /*
603 * malloc() may sleep, and the device may be detached during sleep.
604 * XXX this check is not complete.
605 */
606 if (sc != device_lookup_private(&mlcd_cd, unit)
607 || sc->sc_stat == MLCD_INIT
608 || sc->sc_stat == MLCD_INIT2
609 || part >= sc->sc_npt || pt != &sc->sc_pt[part]
610 || pt->pt_flags == 0) {
611 free(bp, M_DEVBUF);
612 return NULL;
613 }
614
615 bp->lb_error = 0;
616
617 return bp;
618 }
619
620 static void
621 mlcd_buf_free(struct mlcd_buf *bp)
622 {
623
624 free(bp, M_DEVBUF);
625 }
626
627 /* invert order of bits */
628 static inline uint32_t
629 reverse_32(uint32_t b)
630 {
631 uint32_t b1;
632
633 /* invert every 8bit */
634 b1 = (b & 0x55555555) << 1; b = (b >> 1) & 0x55555555; b |= b1;
635 b1 = (b & 0x33333333) << 2; b = (b >> 2) & 0x33333333; b |= b1;
636 b1 = (b & 0x0f0f0f0f) << 4; b = (b >> 4) & 0x0f0f0f0f; b |= b1;
637
638 /* invert byte order */
639 return bswap32(b);
640 }
641
642 static void
643 mlcd_rotate_bitmap(void *ptr, size_t size)
644 {
645 uint32_t *p, *q, tmp;
646
647 KDASSERT(size % sizeof(uint32_t) == 0);
648 for (p = ptr, q = (void *)((char *)ptr + size); p < q; ) {
649 tmp = reverse_32(*p);
650 *p++ = reverse_32(*--q);
651 *q = tmp;
652 }
653 }
654
655 /* ARGSUSED2 */
656 int
657 mlcdwrite(dev_t dev, struct uio *uio, int flags)
658 {
659 struct mlcd_softc *sc;
660 struct mlcd_pt *pt;
661 struct mlcd_buf *bp;
662 int part;
663 off_t devsize;
664 int error = 0;
665
666 part = MLCD_PART(dev);
667 sc = device_lookup_private(&mlcd_cd, MLCD_UNIT(dev));
668 pt = &sc->sc_pt[part];
669
670 #if 0
671 printf("%s: mlcdwrite: offset %ld, size %d\n",
672 pt->pt_name, (long) uio->uio_offset, uio->uio_resid);
673 #endif
674
675 devsize = pt->pt_nblk * sc->sc_bsize;
676 if (uio->uio_offset % sc->sc_bsize || uio->uio_offset > devsize)
677 return EINVAL;
678
679 if ((bp = mlcd_buf_alloc(dev, M_WAITOK)) == NULL)
680 return EIO; /* device is detached during allocation */
681
682 bp->lb_partno = part;
683
684 while (uio->uio_offset < devsize
685 && uio->uio_resid >= (size_t) sc->sc_bsize) {
686 /* invert block number if upside-down */
687 bp->lb_blkno = (sc->sc_direction == MAPLE_CONN_TOP) ?
688 pt->pt_nblk - uio->uio_offset / sc->sc_bsize - 1 :
689 uio->uio_offset / sc->sc_bsize;
690
691 if ((error = uiomove(bp->lb_data, sc->sc_bsize, uio)) != 0)
692 break;
693
694 if (sc->sc_direction == MAPLE_CONN_TOP) {
695 /* the LCD is upside-down */
696 mlcd_rotate_bitmap(bp->lb_data, sc->sc_bsize);
697 }
698
699 /* queue this transfer */
700 SIMPLEQ_INSERT_TAIL(&sc->sc_q, bp, lb_q);
701
702 if (sc->sc_stat == MLCD_IDLE)
703 mlcdstart(sc);
704
705 tsleep(bp, PRIBIO + 1, "mlcdbuf", 0);
706
707 if ((error = bp->lb_error) != 0) {
708 uio->uio_resid += sc->sc_bsize;
709 break;
710 }
711 }
712
713 mlcd_buf_free(bp);
714
715 return error;
716 }
717
718 int
719 mlcdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
720 {
721 int unit;
722 struct mlcd_softc *sc;
723
724 unit = MLCD_UNIT(dev);
725 sc = device_lookup_private(&mlcd_cd, unit);
726
727 switch (cmd) {
728
729 default:
730 /* generic maple ioctl */
731 return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data,
732 flag, l);
733 }
734
735 return 0;
736 }
737