mlcd.c revision 1.14.22.1 1 /* $NetBSD: mlcd.c,v 1.14.22.1 2014/05/18 17:45:02 rmind 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.14.22.1 2014/05/18 17:45:02 rmind 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 snprintf(sc->sc_pt[i].pt_name, sizeof(sc->sc_pt[i].pt_name),
279 "%s.%d", device_xname(self), i);
280 }
281
282 maple_set_callback(parent, sc->sc_unit, MAPLE_FN_LCD,
283 mlcd_intr, sc);
284
285 /*
286 * get size (start from partition 0)
287 */
288 sc->sc_reqm.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
289 sc->sc_reqm.pt = 0;
290 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
291 MAPLE_COMMAND_GETMINFO, sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
292 }
293
294 /* ARGSUSED1 */
295 static int
296 mlcddetach(device_t self, int flags)
297 {
298 struct mlcd_softc *sc = device_private(self);
299 struct mlcd_buf *bp;
300 int minor_l, minor_h;
301
302 sc->sc_stat = MLCD_DETACH; /* just in case */
303
304 /*
305 * kill pending I/O
306 */
307 if ((bp = sc->sc_bp) != NULL) {
308 bp->lb_error = EIO;
309 wakeup(bp);
310 }
311 while ((bp = SIMPLEQ_FIRST(&sc->sc_q)) != NULL) {
312 SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
313 bp->lb_error = EIO;
314 wakeup(bp);
315 }
316
317 /*
318 * revoke vnodes
319 */
320 minor_l = MLCD_MINOR(device_unit(self), 0);
321 minor_h = MLCD_MINOR(device_unit(self), sc->sc_npt - 1);
322 vdevgone(cdevsw_lookup_major(&mlcd_cdevsw), minor_l, minor_h, VCHR);
323
324 /*
325 * free per-partition structure
326 */
327 if (sc->sc_pt)
328 free(sc->sc_pt, M_DEVBUF);
329
330 return 0;
331 }
332
333 /*
334 * called back from maple bus driver
335 */
336 /* ARGSUSED3 */
337 static void
338 mlcd_intr(void *arg, struct maple_response *response, int sz, int flags)
339 {
340 struct mlcd_softc *sc = arg;
341 struct mlcd_response_media_info *rm = (void *) response->data;
342 struct mlcd_buf *bp;
343 int part;
344 struct mlcd_pt *pt;
345
346 switch (sc->sc_stat) {
347 case MLCD_INIT:
348 /* checking part geometry */
349 part = sc->sc_reqm.pt;
350 pt = &sc->sc_pt[part];
351 switch ((maple_response_t) response->response_code) {
352 case MAPLE_RESPONSE_DATATRF:
353 pt->pt_info = rm->info;
354 pt->pt_size = ((pt->pt_info.width + 1) *
355 (pt->pt_info.height + 1) + 7) / 8;
356 pt->pt_nblk = pt->pt_size / sc->sc_bsize;
357 printf("%s: %dx%d display, %d bytes\n",
358 pt->pt_name,
359 pt->pt_info.width + 1, pt->pt_info.height + 1,
360 pt->pt_size);
361
362 /* this partition is active */
363 pt->pt_flags = MLCD_PT_OK;
364
365 break;
366 default:
367 printf("%s: init: unexpected response %#x, sz %d\n",
368 pt->pt_name, be32toh(response->response_code), sz);
369 break;
370 }
371 if (++part == sc->sc_npt) {
372 /* init done */
373
374 /* XXX initial image for Visual Memory */
375 if (sc->sc_pt[0].pt_size == sizeof initimg48x32 &&
376 sc->sc_waccsz == sizeof initimg48x32 &&
377 sc->sc_wacc == 1) {
378 sc->sc_stat = MLCD_INIT2;
379 sc->sc_reqw.func_code =
380 htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
381 sc->sc_reqw.pt = 0; /* part 0 */
382 sc->sc_reqw.block = 0;
383 sc->sc_reqw.phase = 0;
384 memcpy(sc->sc_reqw.data, initimg48x32,
385 sizeof initimg48x32);
386 if (sc->sc_direction == MAPLE_CONN_TOP) {
387 /* the LCD is upside-down */
388 mlcd_rotate_bitmap(sc->sc_reqw.data,
389 sizeof initimg48x32);
390 }
391 maple_command(sc->sc_parent, sc->sc_unit,
392 MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
393 MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
394 } else
395 sc->sc_stat = MLCD_IDLE; /* init done */
396 } else {
397 sc->sc_reqm.pt = part;
398 maple_command(sc->sc_parent, sc->sc_unit,
399 MAPLE_FN_LCD, MAPLE_COMMAND_GETMINFO,
400 sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
401 }
402 break;
403
404 case MLCD_INIT2:
405 sc->sc_stat = MLCD_IDLE; /* init done */
406 break;
407
408 case MLCD_WRITE:
409 bp = sc->sc_bp;
410
411 switch ((maple_response_t) response->response_code) {
412 case MAPLE_RESPONSE_OK: /* write done */
413 if (++sc->sc_reqw.phase == sc->sc_wacc) {
414 /* all phase done */
415 mlcddone(sc);
416 } else {
417 /* go next phase */
418 memcpy(sc->sc_reqw.data,
419 (char *)bp->lb_data +
420 sc->sc_waccsz * sc->sc_reqw.phase,
421 sc->sc_waccsz);
422 maple_command(sc->sc_parent, sc->sc_unit,
423 MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
424 MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
425 }
426 break;
427 case MAPLE_RESPONSE_LCDERR:
428 mlcd_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name,
429 rm->func_code /* XXX */);
430 mlcdstart_bp(sc); /* retry */
431 break;
432 default:
433 printf("%s: write: unexpected response %#x, %#x, sz %d\n",
434 sc->sc_pt[sc->sc_reqw.pt].pt_name,
435 be32toh(response->response_code),
436 be32toh(rm->func_code), sz);
437 mlcdstart_bp(sc); /* retry */
438 break;
439 }
440 break;
441
442 default:
443 break;
444 }
445 }
446
447 static void
448 mlcd_printerror(const char *head, uint32_t code)
449 {
450
451 printf("%s:", head);
452 NTOHL(code);
453 if (code & 1)
454 printf(" PT error");
455 if (code & 2)
456 printf(" Phase error");
457 if (code & 4)
458 printf(" Block error");
459 if (code & 010)
460 printf(" Write error");
461 if (code & 020)
462 printf(" Length error");
463 if (code & ~037)
464 printf(" Unknown error %#x", code & ~037);
465 printf("\n");
466 }
467
468 /* ARGSUSED */
469 int
470 mlcdopen(dev_t dev, int flags, int devtype, struct lwp *l)
471 {
472 int unit, part;
473 struct mlcd_softc *sc;
474 struct mlcd_pt *pt;
475
476 unit = MLCD_UNIT(dev);
477 part = MLCD_PART(dev);
478 if ((sc = device_lookup_private(&mlcd_cd, unit)) == NULL
479 || sc->sc_stat == MLCD_INIT
480 || sc->sc_stat == MLCD_INIT2
481 || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0)
482 return ENXIO;
483
484 if (pt->pt_flags & MLCD_PT_OPEN)
485 return EBUSY;
486
487 pt->pt_flags |= MLCD_PT_OPEN;
488
489 return 0;
490 }
491
492 /* ARGSUSED */
493 int
494 mlcdclose(dev_t dev, int flags, int devtype, struct lwp *l)
495 {
496 int unit, part;
497 struct mlcd_softc *sc;
498 struct mlcd_pt *pt;
499
500 unit = MLCD_UNIT(dev);
501 part = MLCD_PART(dev);
502 sc = device_lookup_private(&mlcd_cd, unit);
503 pt = &sc->sc_pt[part];
504
505 pt->pt_flags &= ~MLCD_PT_OPEN;
506
507 return 0;
508 }
509
510 /*
511 * start I/O operations
512 */
513 static void
514 mlcdstart(struct mlcd_softc *sc)
515 {
516 struct mlcd_buf *bp;
517
518 if ((bp = SIMPLEQ_FIRST(&sc->sc_q)) == NULL) {
519 sc->sc_stat = MLCD_IDLE;
520 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit,
521 MAPLE_FN_LCD, 1);
522 return;
523 }
524
525 SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
526
527 sc->sc_bp = bp;
528 sc->sc_retry = 0;
529 mlcdstart_bp(sc);
530 }
531
532 /*
533 * start/retry a specified I/O operation
534 */
535 static void
536 mlcdstart_bp(struct mlcd_softc *sc)
537 {
538 struct mlcd_buf *bp;
539
540 bp = sc->sc_bp;
541
542 /* handle retry */
543 if (sc->sc_retry++ > MLCD_MAXRETRY) {
544 /* retry count exceeded */
545 bp->lb_error = EIO;
546 mlcddone(sc);
547 return;
548 }
549
550 /*
551 * I/O access will fail if the removal detection (by maple driver)
552 * occurs before finishing the I/O, so disable it.
553 * We are sending commands, and the removal detection is still alive.
554 */
555 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD, 0);
556
557 /*
558 * Start the first phase (phase# = 0).
559 */
560 /* write */
561 sc->sc_stat = MLCD_WRITE;
562 sc->sc_reqw.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
563 sc->sc_reqw.pt = bp->lb_partno;
564 sc->sc_reqw.block = htobe16(bp->lb_blkno);
565 sc->sc_reqw.phase = 0; /* first phase */
566 memcpy(sc->sc_reqw.data,
567 (char *) bp->lb_data /* + sc->sc_waccsz * phase */, sc->sc_waccsz);
568 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
569 MAPLE_COMMAND_BWRITE, MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
570 }
571
572 static void
573 mlcddone(struct mlcd_softc *sc)
574 {
575 struct mlcd_buf *bp;
576
577 /* terminate current transfer */
578 bp = sc->sc_bp;
579 KASSERT(bp);
580 sc->sc_bp = NULL;
581 wakeup(bp);
582
583 /* go next transfer */
584 mlcdstart(sc);
585 }
586
587 /*
588 * allocate a buffer for one block
589 *
590 * return NULL if
591 * [flags == M_NOWAIT] out of buffer space
592 * [flags == M_WAITOK] device detach detected
593 */
594 static struct mlcd_buf *
595 mlcd_buf_alloc(int dev, int flags)
596 {
597 struct mlcd_softc *sc;
598 struct mlcd_pt *pt;
599 int unit, part;
600 struct mlcd_buf *bp;
601
602 unit = MLCD_UNIT(dev);
603 part = MLCD_PART(dev);
604 sc = device_lookup_private(&mlcd_cd, unit);
605 KASSERT(sc);
606 pt = &sc->sc_pt[part];
607 KASSERT(pt);
608
609 if ((bp = malloc(MLCD_BUF_SZ(sc), M_DEVBUF, flags)) == NULL)
610 return bp;
611
612 /*
613 * malloc() may sleep, and the device may be detached during sleep.
614 * XXX this check is not complete.
615 */
616 if (sc != device_lookup_private(&mlcd_cd, unit)
617 || sc->sc_stat == MLCD_INIT
618 || sc->sc_stat == MLCD_INIT2
619 || part >= sc->sc_npt || pt != &sc->sc_pt[part]
620 || pt->pt_flags == 0) {
621 free(bp, M_DEVBUF);
622 return NULL;
623 }
624
625 bp->lb_error = 0;
626
627 return bp;
628 }
629
630 static void
631 mlcd_buf_free(struct mlcd_buf *bp)
632 {
633
634 free(bp, M_DEVBUF);
635 }
636
637 /* invert order of bits */
638 static inline uint32_t
639 reverse_32(uint32_t b)
640 {
641 uint32_t b1;
642
643 /* invert every 8bit */
644 b1 = (b & 0x55555555) << 1; b = (b >> 1) & 0x55555555; b |= b1;
645 b1 = (b & 0x33333333) << 2; b = (b >> 2) & 0x33333333; b |= b1;
646 b1 = (b & 0x0f0f0f0f) << 4; b = (b >> 4) & 0x0f0f0f0f; b |= b1;
647
648 /* invert byte order */
649 return bswap32(b);
650 }
651
652 static void
653 mlcd_rotate_bitmap(void *ptr, size_t size)
654 {
655 uint32_t *p, *q, tmp;
656
657 KDASSERT(size % sizeof(uint32_t) == 0);
658 for (p = ptr, q = (void *)((char *)ptr + size); p < q; ) {
659 tmp = reverse_32(*p);
660 *p++ = reverse_32(*--q);
661 *q = tmp;
662 }
663 }
664
665 /* ARGSUSED2 */
666 int
667 mlcdwrite(dev_t dev, struct uio *uio, int flags)
668 {
669 struct mlcd_softc *sc;
670 struct mlcd_pt *pt;
671 struct mlcd_buf *bp;
672 int part;
673 off_t devsize;
674 int error = 0;
675
676 part = MLCD_PART(dev);
677 sc = device_lookup_private(&mlcd_cd, MLCD_UNIT(dev));
678 pt = &sc->sc_pt[part];
679
680 #if 0
681 printf("%s: mlcdwrite: offset %ld, size %d\n",
682 pt->pt_name, (long) uio->uio_offset, uio->uio_resid);
683 #endif
684
685 devsize = pt->pt_nblk * sc->sc_bsize;
686 if (uio->uio_offset % sc->sc_bsize || uio->uio_offset > devsize)
687 return EINVAL;
688
689 if ((bp = mlcd_buf_alloc(dev, M_WAITOK)) == NULL)
690 return EIO; /* device is detached during allocation */
691
692 bp->lb_partno = part;
693
694 while (uio->uio_offset < devsize
695 && uio->uio_resid >= (size_t) sc->sc_bsize) {
696 /* invert block number if upside-down */
697 bp->lb_blkno = (sc->sc_direction == MAPLE_CONN_TOP) ?
698 pt->pt_nblk - uio->uio_offset / sc->sc_bsize - 1 :
699 uio->uio_offset / sc->sc_bsize;
700
701 if ((error = uiomove(bp->lb_data, sc->sc_bsize, uio)) != 0)
702 break;
703
704 if (sc->sc_direction == MAPLE_CONN_TOP) {
705 /* the LCD is upside-down */
706 mlcd_rotate_bitmap(bp->lb_data, sc->sc_bsize);
707 }
708
709 /* queue this transfer */
710 SIMPLEQ_INSERT_TAIL(&sc->sc_q, bp, lb_q);
711
712 if (sc->sc_stat == MLCD_IDLE)
713 mlcdstart(sc);
714
715 tsleep(bp, PRIBIO + 1, "mlcdbuf", 0);
716
717 if ((error = bp->lb_error) != 0) {
718 uio->uio_resid += sc->sc_bsize;
719 break;
720 }
721 }
722
723 mlcd_buf_free(bp);
724
725 return error;
726 }
727
728 int
729 mlcdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
730 {
731 int unit;
732 struct mlcd_softc *sc;
733
734 unit = MLCD_UNIT(dev);
735 sc = device_lookup_private(&mlcd_cd, unit);
736
737 switch (cmd) {
738
739 default:
740 /* generic maple ioctl */
741 return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data,
742 flag, l);
743 }
744
745 return 0;
746 }
747