mlcd.c revision 1.13 1 /* $NetBSD: mlcd.c,v 1.13 2010/10/17 14:13:44 tsutsui 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.13 2010/10/17 14:13:44 tsutsui 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 #define MLCD_MAXACCSIZE 1012 /* (255*4) - 8 = 253*32 / 8 */
48
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 */
57 };
58
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];
65 };
66 #define MLCD_SIZE_REQW(sc) ((sc)->sc_waccsz + 8)
67
68 struct mlcd_request_get_media_info {
69 uint32_t func_code;
70 uint32_t pt; /* pt (1 byte) and unused 3 bytes */
71 };
72
73 struct mlcd_media_info {
74 uint8_t width; /* width - 1 */
75 uint8_t height; /* height - 1 */
76 uint8_t rsvd[2]; /* ? 0x10 0x02 */
77 };
78
79 struct mlcd_response_media_info {
80 uint32_t func_code; /* function code (big endian) */
81 struct mlcd_media_info info;
82 };
83
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 */
90 };
91 #define MLCD_BUF_SZ(sc) (offsetof(struct mlcd_buf, lb_data) + (sc)->sc_bsize)
92
93 struct mlcd_softc {
94 device_t sc_dev;
95
96 device_t 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;
106
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 */
111
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 */
119
120 char pt_name[16 /* see device.h */ + 4 /* ".255" */];
121 } *sc_pt;
122
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
130
131 /* pending buffers */
132 SIMPLEQ_HEAD(mlcd_bufq, mlcd_buf) sc_q;
133
134 /* current I/O access */
135 struct mlcd_buf *sc_bp;
136 int sc_retry;
137 #define MLCD_MAXRETRY 10
138 };
139
140 /*
141 * minor number layout (mlcddetach() depends on this layout):
142 *
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
146 */
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))
150
151 static int mlcdmatch(device_t, cfdata_t, void *);
152 static void mlcdattach(device_t, device_t, void *);
153 static int mlcddetach(device_t, 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 *);
163
164 dev_type_open(mlcdopen);
165 dev_type_close(mlcdclose);
166 dev_type_write(mlcdwrite);
167 dev_type_ioctl(mlcdioctl);
168
169 const struct cdevsw mlcd_cdevsw = {
170 mlcdopen, mlcdclose, noread, mlcdwrite, mlcdioctl,
171 nostop, notty, nopoll, nommap, nokqfilter
172 };
173
174 CFATTACH_DECL_NEW(mlcd, sizeof(struct mlcd_softc),
175 mlcdmatch, mlcdattach, mlcddetach, NULL);
176
177 extern struct cfdriver mlcd_cd;
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 struct mlcd_pt *pt;
530
531 bp = sc->sc_bp;
532 pt = &sc->sc_pt[bp->lb_partno];
533
534 /* handle retry */
535 if (sc->sc_retry++ > MLCD_MAXRETRY) {
536 /* retry count exceeded */
537 bp->lb_error = EIO;
538 mlcddone(sc);
539 return;
540 }
541
542 /*
543 * I/O access will fail if the removal detection (by maple driver)
544 * occurs before finishing the I/O, so disable it.
545 * We are sending commands, and the removal detection is still alive.
546 */
547 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD, 0);
548
549 /*
550 * Start the first phase (phase# = 0).
551 */
552 /* write */
553 sc->sc_stat = MLCD_WRITE;
554 sc->sc_reqw.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
555 sc->sc_reqw.pt = bp->lb_partno;
556 sc->sc_reqw.block = htobe16(bp->lb_blkno);
557 sc->sc_reqw.phase = 0; /* first phase */
558 memcpy(sc->sc_reqw.data,
559 (char *) bp->lb_data /* + sc->sc_waccsz * phase */, sc->sc_waccsz);
560 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
561 MAPLE_COMMAND_BWRITE, MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
562 }
563
564 static void
565 mlcddone(struct mlcd_softc *sc)
566 {
567 struct mlcd_buf *bp;
568
569 /* terminate current transfer */
570 bp = sc->sc_bp;
571 KASSERT(bp);
572 sc->sc_bp = NULL;
573 wakeup(bp);
574
575 /* go next transfer */
576 mlcdstart(sc);
577 }
578
579 /*
580 * allocate a buffer for one block
581 *
582 * return NULL if
583 * [flags == M_NOWAIT] out of buffer space
584 * [flags == M_WAITOK] device detach detected
585 */
586 static struct mlcd_buf *
587 mlcd_buf_alloc(int dev, int flags)
588 {
589 struct mlcd_softc *sc;
590 struct mlcd_pt *pt;
591 int unit, part;
592 struct mlcd_buf *bp;
593
594 unit = MLCD_UNIT(dev);
595 part = MLCD_PART(dev);
596 sc = device_lookup_private(&mlcd_cd, unit);
597 KASSERT(sc);
598 pt = &sc->sc_pt[part];
599 KASSERT(pt);
600
601 if ((bp = malloc(MLCD_BUF_SZ(sc), M_DEVBUF, flags)) == NULL)
602 return bp;
603
604 /*
605 * malloc() may sleep, and the device may be detached during sleep.
606 * XXX this check is not complete.
607 */
608 if (sc != device_lookup_private(&mlcd_cd, unit)
609 || sc->sc_stat == MLCD_INIT
610 || sc->sc_stat == MLCD_INIT2
611 || part >= sc->sc_npt || pt != &sc->sc_pt[part]
612 || pt->pt_flags == 0) {
613 free(bp, M_DEVBUF);
614 return NULL;
615 }
616
617 bp->lb_error = 0;
618
619 return bp;
620 }
621
622 static void
623 mlcd_buf_free(struct mlcd_buf *bp)
624 {
625
626 free(bp, M_DEVBUF);
627 }
628
629 /* invert order of bits */
630 static inline uint32_t
631 reverse_32(uint32_t b)
632 {
633 uint32_t b1;
634
635 /* invert every 8bit */
636 b1 = (b & 0x55555555) << 1; b = (b >> 1) & 0x55555555; b |= b1;
637 b1 = (b & 0x33333333) << 2; b = (b >> 2) & 0x33333333; b |= b1;
638 b1 = (b & 0x0f0f0f0f) << 4; b = (b >> 4) & 0x0f0f0f0f; b |= b1;
639
640 /* invert byte order */
641 return bswap32(b);
642 }
643
644 static void
645 mlcd_rotate_bitmap(void *ptr, size_t size)
646 {
647 uint32_t *p, *q, tmp;
648
649 KDASSERT(size % sizeof(uint32_t) == 0);
650 for (p = ptr, q = (void *)((char *)ptr + size); p < q; ) {
651 tmp = reverse_32(*p);
652 *p++ = reverse_32(*--q);
653 *q = tmp;
654 }
655 }
656
657 /* ARGSUSED2 */
658 int
659 mlcdwrite(dev_t dev, struct uio *uio, int flags)
660 {
661 struct mlcd_softc *sc;
662 struct mlcd_pt *pt;
663 struct mlcd_buf *bp;
664 int part;
665 off_t devsize;
666 int error = 0;
667
668 part = MLCD_PART(dev);
669 sc = device_lookup_private(&mlcd_cd, MLCD_UNIT(dev));
670 pt = &sc->sc_pt[part];
671
672 #if 0
673 printf("%s: mlcdwrite: offset %ld, size %d\n",
674 pt->pt_name, (long) uio->uio_offset, uio->uio_resid);
675 #endif
676
677 devsize = pt->pt_nblk * sc->sc_bsize;
678 if (uio->uio_offset % sc->sc_bsize || uio->uio_offset > devsize)
679 return EINVAL;
680
681 if ((bp = mlcd_buf_alloc(dev, M_WAITOK)) == NULL)
682 return EIO; /* device is detached during allocation */
683
684 bp->lb_partno = part;
685
686 while (uio->uio_offset < devsize
687 && uio->uio_resid >= (size_t) sc->sc_bsize) {
688 /* invert block number if upside-down */
689 bp->lb_blkno = (sc->sc_direction == MAPLE_CONN_TOP) ?
690 pt->pt_nblk - uio->uio_offset / sc->sc_bsize - 1 :
691 uio->uio_offset / sc->sc_bsize;
692
693 if ((error = uiomove(bp->lb_data, sc->sc_bsize, uio)) != 0)
694 break;
695
696 if (sc->sc_direction == MAPLE_CONN_TOP) {
697 /* the LCD is upside-down */
698 mlcd_rotate_bitmap(bp->lb_data, sc->sc_bsize);
699 }
700
701 /* queue this transfer */
702 SIMPLEQ_INSERT_TAIL(&sc->sc_q, bp, lb_q);
703
704 if (sc->sc_stat == MLCD_IDLE)
705 mlcdstart(sc);
706
707 tsleep(bp, PRIBIO + 1, "mlcdbuf", 0);
708
709 if ((error = bp->lb_error) != 0) {
710 uio->uio_resid += sc->sc_bsize;
711 break;
712 }
713 }
714
715 mlcd_buf_free(bp);
716
717 return error;
718 }
719
720 int
721 mlcdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
722 {
723 int unit, part;
724 struct mlcd_softc *sc;
725 struct mlcd_pt *pt;
726
727 unit = MLCD_UNIT(dev);
728 part = MLCD_PART(dev);
729 sc = device_lookup_private(&mlcd_cd, unit);
730 pt = &sc->sc_pt[part];
731
732 switch (cmd) {
733
734 default:
735 /* generic maple ioctl */
736 return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data,
737 flag, l);
738 }
739
740 return 0;
741 }
742