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