mlcd.c revision 1.3 1 /* $NetBSD: mlcd.c,v 1.3 2003/07/15 01:31:40 lukem 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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: mlcd.c,v 1.3 2003/07/15 01:31:40 lukem Exp $");
41
42 #include <sys/param.h>
43 #include <sys/device.h>
44 #include <sys/kernel.h>
45 #include <sys/malloc.h>
46 #include <sys/proc.h>
47 #include <sys/systm.h>
48 #include <sys/vnode.h>
49 #include <sys/conf.h>
50
51 #include <dreamcast/dev/maple/maple.h>
52 #include <dreamcast/dev/maple/mapleconf.h>
53
54 #define MLCD_MAXACCSIZE 1012 /* (255*4) - 8 = 253*32 / 8 */
55
56 struct mlcd_funcdef { /* XXX assuming little-endian structure packing */
57 unsigned unused : 6,
58 bw : 1, /* 0: normally white, 1: normally black */
59 hv : 1, /* 0: horizontal, 1: vertical */
60 ra : 4, /* 0 */
61 wa : 4, /* number of access / write */
62 bb : 8, /* block size / 32 - 1 */
63 pt : 8; /* number of partition - 1 */
64 };
65
66 struct mlcd_request_write_data {
67 u_int32_t func_code;
68 u_int8_t pt;
69 u_int8_t phase; /* 0, 1, 2, 3: for each 128 byte */
70 u_int16_t block;
71 u_int8_t data[MLCD_MAXACCSIZE];
72 };
73 #define MLCD_SIZE_REQW(sc) ((sc)->sc_waccsz + 8)
74
75 struct mlcd_request_get_media_info {
76 u_int32_t func_code;
77 u_int32_t pt; /* pt (1 byte) and unused 3 bytes */
78 };
79
80 struct mlcd_media_info {
81 u_int8_t width; /* width - 1 */
82 u_int8_t height; /* height - 1 */
83 u_int8_t rsvd[2]; /* ? 0x10 0x02 */
84 };
85
86 struct mlcd_response_media_info {
87 u_int32_t func_code; /* function code (big endian) */
88 struct mlcd_media_info info;
89 };
90
91 struct mlcd_buf {
92 SIMPLEQ_ENTRY(mlcd_buf) lb_q;
93 int lb_error;
94 int lb_partno;
95 int lb_blkno;
96 u_int32_t lb_data[1]; /* variable length */
97 };
98 #define MLCD_BUF_SZ(sc) (offsetof(struct mlcd_buf, lb_data) + (sc)->sc_bsize)
99
100 struct mlcd_softc {
101 struct device sc_dev;
102
103 struct device *sc_parent;
104 struct maple_unit *sc_unit;
105 int sc_direction;
106 enum mlcd_stat {
107 MLCD_INIT, /* during initialization */
108 MLCD_INIT2, /* during initialization */
109 MLCD_IDLE, /* init done, not in I/O */
110 MLCD_WRITE, /* in write operation */
111 MLCD_DETACH /* detaching */
112 } sc_stat;
113
114 int sc_npt; /* number of partitions */
115 int sc_bsize; /* block size */
116 int sc_wacc; /* number of write access per block */
117 int sc_waccsz; /* size of a write access */
118
119 struct mlcd_pt {
120 int pt_flags;
121 #define MLCD_PT_OK 1 /* partition is alive */
122 #define MLCD_PT_OPEN 2
123 struct mlcd_media_info pt_info; /* geometry per part */
124 int pt_size; /* partition size in byte */
125 int pt_nblk; /* partition size in block */
126
127 char pt_name[16 /* see device.h */ + 4 /* ".255" */];
128 } *sc_pt;
129
130 /* write request buffer (only one is used at a time) */
131 union {
132 struct mlcd_request_write_data req_write;
133 struct mlcd_request_get_media_info req_minfo;
134 } sc_req;
135 #define sc_reqw sc_req.req_write
136 #define sc_reqm sc_req.req_minfo
137
138 /* pending buffers */
139 SIMPLEQ_HEAD(mlcd_bufq, mlcd_buf) sc_q;
140
141 /* current I/O access */
142 struct mlcd_buf *sc_bp;
143 int sc_retry;
144 #define MLCD_MAXRETRY 10
145 };
146
147 /*
148 * minor number layout (mlcddetach() depends on this layout):
149 *
150 * 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
151 * |---------------------------------| |---------------------|
152 * unit part
153 */
154 #define MLCD_PART(dev) (minor(dev) & 0xff)
155 #define MLCD_UNIT(dev) (minor(dev) >> 8)
156 #define MLCD_MINOR(unit, part) (((unit) << 8) | (part))
157
158 static int mlcdmatch __P((struct device *, struct cfdata *, void *));
159 static void mlcdattach __P((struct device *, struct device *, void *));
160 static int mlcddetach __P((struct device *, int));
161 static void mlcd_intr __P((void *, struct maple_response *, int, int));
162 static void mlcd_printerror __P((const char *, u_int32_t));
163 static struct mlcd_buf *mlcd_buf_alloc __P((int /*dev*/, int /*flags*/));
164 static void mlcd_buf_free __P((struct mlcd_buf *));
165 static __inline u_int32_t reverse_32 __P((u_int32_t));
166 static void mlcd_rotate_bitmap __P((void *, size_t));
167 static void mlcdstart __P((struct mlcd_softc *));
168 static void mlcdstart_bp __P((struct mlcd_softc *));
169 static void mlcddone __P((struct mlcd_softc *));
170
171 dev_type_open(mlcdopen);
172 dev_type_close(mlcdclose);
173 dev_type_write(mlcdwrite);
174 dev_type_ioctl(mlcdioctl);
175
176 const struct cdevsw mlcd_cdevsw = {
177 mlcdopen, mlcdclose, noread, mlcdwrite, mlcdioctl,
178 nostop, notty, nopoll, nommap, nokqfilter
179 };
180
181 CFATTACH_DECL(mlcd, sizeof(struct mlcd_softc),
182 mlcdmatch, mlcdattach, mlcddetach, NULL);
183
184 extern struct cfdriver mlcd_cd;
185
186 /* initial image "NetBSD dreamcast" */
187 static const char initimg48x32[192] = {
188 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
189 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
190 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
191 0x1c, 0x70, 0x00, 0x7e, 0x1c, 0xf0, 0x0c, 0x60, 0x00, 0x33, 0x26, 0x6c,
192 0x0c, 0x60, 0x0c, 0x33, 0x66, 0x66, 0x1e, 0xc7, 0x0c, 0x62, 0x60, 0xc6,
193 0x1a, 0xc9, 0xbe, 0x7c, 0x30, 0xc6, 0x1a, 0xdb, 0x98, 0x66, 0x18, 0xc6,
194 0x1a, 0xdc, 0x18, 0x66, 0x0d, 0x8c, 0x31, 0xb0, 0x32, 0xc6, 0x8d, 0x8c,
195 0x31, 0xb1, 0x36, 0xcd, 0x99, 0x98, 0x71, 0x9e, 0x1d, 0xf9, 0xf3, 0xe0,
196 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
197 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08,
198 0x1d, 0x6c, 0x63, 0xc7, 0x30, 0xde, 0x25, 0x92, 0x12, 0xa8, 0x09, 0x08,
199 0x25, 0x1e, 0x72, 0xa8, 0x38, 0xc8, 0x25, 0x10, 0x92, 0xa8, 0x48, 0x28,
200 0x1d, 0x0e, 0x6a, 0xa7, 0x35, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
201 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
202 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
203 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
204 };
205
206 /* ARGSUSED */
207 static int
208 mlcdmatch(parent, cf, aux)
209 struct device *parent;
210 struct cfdata *cf;
211 void *aux;
212 {
213 struct maple_attach_args *ma = aux;
214
215 return (ma->ma_function == MAPLE_FN_LCD ? MAPLE_MATCH_FUNC : 0);
216 }
217
218 static void
219 mlcdattach(parent, self, aux)
220 struct device *parent, *self;
221 void *aux;
222 {
223 struct mlcd_softc *sc = (void *) self;
224 struct maple_attach_args *ma = aux;
225 int i;
226 union {
227 u_int32_t v;
228 struct mlcd_funcdef s;
229 } funcdef;
230
231 sc->sc_parent = parent;
232 sc->sc_unit = ma->ma_unit;
233 sc->sc_direction = ma->ma_basedevinfo->di_connector_direction;
234
235 funcdef.v = maple_get_function_data(ma->ma_devinfo, MAPLE_FN_LCD);
236 printf(": LCD display\n");
237 printf("%s: %d LCD, %d bytes/block, ",
238 sc->sc_dev.dv_xname,
239 sc->sc_npt = funcdef.s.pt + 1,
240 sc->sc_bsize = (funcdef.s.bb + 1) << 5);
241 if ((sc->sc_wacc = funcdef.s.wa) == 0)
242 printf("no ");
243 else
244 printf("%d acc/", sc->sc_wacc);
245 printf("write, %s, norm %s%s\n",
246 funcdef.s.hv ? "vert" : "horiz",
247 funcdef.s.bw ? "black" : "white",
248 sc->sc_direction == MAPLE_CONN_TOP ? ", upside-down" : "");
249
250 /*
251 * start init sequence
252 */
253 sc->sc_stat = MLCD_INIT;
254 SIMPLEQ_INIT(&sc->sc_q);
255
256 /* check consistency */
257 if (sc->sc_wacc != 0) {
258 sc->sc_waccsz = sc->sc_bsize / sc->sc_wacc;
259 if (sc->sc_bsize != sc->sc_waccsz * sc->sc_wacc) {
260 printf("%s: write access isn't equally divided\n",
261 sc->sc_dev.dv_xname);
262 sc->sc_wacc = 0; /* no write */
263 } else if (sc->sc_waccsz > MLCD_MAXACCSIZE) {
264 printf("%s: write access size is too large\n",
265 sc->sc_dev.dv_xname);
266 sc->sc_wacc = 0; /* no write */
267 }
268 }
269 if (sc->sc_wacc == 0) {
270 printf("%s: device doesn't support write\n",
271 sc->sc_dev.dv_xname);
272 return;
273 }
274
275 /* per-part structure */
276 sc->sc_pt = malloc(sizeof(struct mlcd_pt) * sc->sc_npt, M_DEVBUF,
277 M_WAITOK|M_ZERO);
278
279 for (i = 0; i < sc->sc_npt; i++) {
280 sprintf(sc->sc_pt[i].pt_name, "%s.%d", sc->sc_dev.dv_xname, i);
281 }
282
283 maple_set_callback(parent, sc->sc_unit, MAPLE_FN_LCD,
284 mlcd_intr, sc);
285
286 /*
287 * get size (start from partition 0)
288 */
289 sc->sc_reqm.func_code = htonl(MAPLE_FUNC(MAPLE_FN_LCD));
290 sc->sc_reqm.pt = 0;
291 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
292 MAPLE_COMMAND_GETMINFO, sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
293 }
294
295 /* ARGSUSED1 */
296 static int
297 mlcddetach(self, flags)
298 struct device *self;
299 int flags;
300 {
301 struct mlcd_softc *sc = (struct mlcd_softc *) self;
302 struct mlcd_buf *bp;
303 int minor_l, minor_h;
304
305 sc->sc_stat = MLCD_DETACH; /* just in case */
306
307 /*
308 * kill pending I/O
309 */
310 if ((bp = sc->sc_bp) != NULL) {
311 bp->lb_error = EIO;
312 wakeup(bp);
313 }
314 while ((bp = SIMPLEQ_FIRST(&sc->sc_q)) != NULL) {
315 SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
316 bp->lb_error = EIO;
317 wakeup(bp);
318 }
319
320 /*
321 * revoke vnodes
322 */
323 minor_l = MLCD_MINOR(self->dv_unit, 0);
324 minor_h = MLCD_MINOR(self->dv_unit, sc->sc_npt - 1);
325 vdevgone(cdevsw_lookup_major(&mlcd_cdevsw), minor_l, minor_h, VCHR);
326
327 /*
328 * free per-partition structure
329 */
330 if (sc->sc_pt)
331 free(sc->sc_pt, M_DEVBUF);
332
333 return 0;
334 }
335
336 /*
337 * called back from maple bus driver
338 */
339 /* ARGSUSED3 */
340 static void
341 mlcd_intr(dev, response, sz, flags)
342 void *dev;
343 struct maple_response *response;
344 int sz, flags;
345 {
346 struct mlcd_softc *sc = dev;
347 struct mlcd_response_media_info *rm = (void *) response->data;
348 struct mlcd_buf *bp;
349 int part;
350 struct mlcd_pt *pt;
351
352 switch (sc->sc_stat) {
353 case MLCD_INIT:
354 /* checking part geometry */
355 part = sc->sc_reqm.pt;
356 pt = &sc->sc_pt[part];
357 switch ((maple_response_t) response->response_code) {
358 case MAPLE_RESPONSE_DATATRF:
359 pt->pt_info = rm->info;
360 pt->pt_size = ((pt->pt_info.width + 1) *
361 (pt->pt_info.height + 1) + 7) / 8;
362 pt->pt_nblk = pt->pt_size / sc->sc_bsize;
363 printf("%s: %dx%d display, %d bytes\n",
364 pt->pt_name,
365 pt->pt_info.width + 1, pt->pt_info.height + 1,
366 pt->pt_size);
367
368 /* this partition is active */
369 pt->pt_flags = MLCD_PT_OK;
370
371 break;
372 default:
373 printf("%s: init: unexpected response %#x, sz %d\n",
374 pt->pt_name, ntohl(response->response_code), sz);
375 break;
376 }
377 if (++part == sc->sc_npt) {
378 /* init done */
379
380 /* XXX initial image for Visual Memory */
381 if (sc->sc_pt[0].pt_size == sizeof initimg48x32 &&
382 sc->sc_waccsz == sizeof initimg48x32 &&
383 sc->sc_wacc == 1) {
384 sc->sc_stat = MLCD_INIT2;
385 sc->sc_reqw.func_code =
386 htonl(MAPLE_FUNC(MAPLE_FN_LCD));
387 sc->sc_reqw.pt = 0; /* part 0 */
388 sc->sc_reqw.block = 0;
389 sc->sc_reqw.phase = 0;
390 bcopy(initimg48x32, sc->sc_reqw.data,
391 sizeof initimg48x32);
392 if (sc->sc_direction == MAPLE_CONN_TOP) {
393 /* the LCD is upside-down */
394 mlcd_rotate_bitmap(sc->sc_reqw.data,
395 sizeof initimg48x32);
396 }
397 maple_command(sc->sc_parent, sc->sc_unit,
398 MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
399 MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
400 } else
401 sc->sc_stat = MLCD_IDLE; /* init done */
402 } else {
403 sc->sc_reqm.pt = part;
404 maple_command(sc->sc_parent, sc->sc_unit,
405 MAPLE_FN_LCD, MAPLE_COMMAND_GETMINFO,
406 sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
407 }
408 break;
409
410 case MLCD_INIT2:
411 sc->sc_stat = MLCD_IDLE; /* init done */
412 break;
413
414 case MLCD_WRITE:
415 bp = sc->sc_bp;
416
417 switch ((maple_response_t) response->response_code) {
418 case MAPLE_RESPONSE_OK: /* write done */
419 if (++sc->sc_reqw.phase == sc->sc_wacc) {
420 /* all phase done */
421 mlcddone(sc);
422 } else {
423 /* go next phase */
424 bcopy((char *)bp->lb_data
425 + sc->sc_waccsz * sc->sc_reqw.phase,
426 sc->sc_reqw.data, sc->sc_waccsz);
427 maple_command(sc->sc_parent, sc->sc_unit,
428 MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
429 MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
430 }
431 break;
432 case MAPLE_RESPONSE_LCDERR:
433 mlcd_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name,
434 rm->func_code /* XXX */);
435 mlcdstart_bp(sc); /* retry */
436 break;
437 default:
438 printf("%s: write: unexpected response %#x, %#x, sz %d\n",
439 sc->sc_pt[sc->sc_reqw.pt].pt_name,
440 ntohl(response->response_code),
441 ntohl(rm->func_code), sz);
442 mlcdstart_bp(sc); /* retry */
443 break;
444 }
445 break;
446
447 default:
448 break;
449 }
450 }
451
452 static void
453 mlcd_printerror(head, code)
454 const char *head;
455 u_int32_t code;
456 {
457
458 printf("%s:", head);
459 NTOHL(code);
460 if (code & 1)
461 printf(" PT error");
462 if (code & 2)
463 printf(" Phase error");
464 if (code & 4)
465 printf(" Block error");
466 if (code & 010)
467 printf(" Write error");
468 if (code & 020)
469 printf(" Length error");
470 if (code & ~037)
471 printf(" Unknown error %#x", code & ~037);
472 printf("\n");
473 }
474
475 /* ARGSUSED */
476 int
477 mlcdopen(dev, flags, devtype, p)
478 dev_t dev;
479 int flags, devtype;
480 struct proc *p;
481 {
482 int unit, part;
483 struct mlcd_softc *sc;
484 struct mlcd_pt *pt;
485
486 unit = MLCD_UNIT(dev);
487 part = MLCD_PART(dev);
488 if ((sc = device_lookup(&mlcd_cd, unit)) == NULL
489 || sc->sc_stat == MLCD_INIT
490 || sc->sc_stat == MLCD_INIT2
491 || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0)
492 return ENXIO;
493
494 if (pt->pt_flags & MLCD_PT_OPEN)
495 return EBUSY;
496
497 pt->pt_flags |= MLCD_PT_OPEN;
498
499 return 0;
500 }
501
502 /* ARGSUSED */
503 int
504 mlcdclose(dev, flags, devtype, p)
505 dev_t dev;
506 int flags, devtype;
507 struct proc *p;
508 {
509 int unit, part;
510 struct mlcd_softc *sc;
511 struct mlcd_pt *pt;
512
513 unit = MLCD_UNIT(dev);
514 part = MLCD_PART(dev);
515 sc = mlcd_cd.cd_devs[unit];
516 pt = &sc->sc_pt[part];
517
518 pt->pt_flags &= ~MLCD_PT_OPEN;
519
520 return 0;
521 }
522
523 /*
524 * start I/O operations
525 */
526 static void
527 mlcdstart(sc)
528 struct mlcd_softc *sc;
529 {
530 struct mlcd_buf *bp;
531
532 if ((bp = SIMPLEQ_FIRST(&sc->sc_q)) == NULL) {
533 sc->sc_stat = MLCD_IDLE;
534 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit,
535 MAPLE_FN_LCD, 1);
536 return;
537 }
538
539 SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
540
541 sc->sc_bp = bp;
542 sc->sc_retry = 0;
543 mlcdstart_bp(sc);
544 }
545
546 /*
547 * start/retry a specified I/O operation
548 */
549 static void
550 mlcdstart_bp(sc)
551 struct mlcd_softc *sc;
552 {
553 struct mlcd_buf *bp;
554 struct mlcd_pt *pt;
555
556 bp = sc->sc_bp;
557 pt = &sc->sc_pt[bp->lb_partno];
558
559 /* handle retry */
560 if (sc->sc_retry++ > MLCD_MAXRETRY) {
561 /* retry count exceeded */
562 bp->lb_error = EIO;
563 mlcddone(sc);
564 return;
565 }
566
567 /*
568 * I/O access will fail if the removal detection (by maple driver)
569 * occurs before finishing the I/O, so disable it.
570 * We are sending commands, and the removal detection is still alive.
571 */
572 maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD, 0);
573
574 /*
575 * Start the first phase (phase# = 0).
576 */
577 /* write */
578 sc->sc_stat = MLCD_WRITE;
579 sc->sc_reqw.func_code = htonl(MAPLE_FUNC(MAPLE_FN_LCD));
580 sc->sc_reqw.pt = bp->lb_partno;
581 sc->sc_reqw.block = htons(bp->lb_blkno);
582 sc->sc_reqw.phase = 0; /* first phase */
583 bcopy((char *) bp->lb_data /* + sc->sc_waccsz * phase */,
584 sc->sc_reqw.data, sc->sc_waccsz);
585 maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
586 MAPLE_COMMAND_BWRITE, MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
587 }
588
589 static void
590 mlcddone(sc)
591 struct mlcd_softc *sc;
592 {
593 struct mlcd_buf *bp;
594
595 /* terminate current transfer */
596 bp = sc->sc_bp;
597 KASSERT(bp);
598 sc->sc_bp = NULL;
599 wakeup(bp);
600
601 /* go next transfer */
602 mlcdstart(sc);
603 }
604
605 /*
606 * allocate a buffer for one block
607 *
608 * return NULL if
609 * [flags == M_NOWAIT] out of buffer space
610 * [flags == M_WAITOK] device detach detected
611 */
612 static struct mlcd_buf *
613 mlcd_buf_alloc(dev, flags)
614 int dev;
615 int flags; /* flags for malloc() */
616 {
617 struct mlcd_softc *sc;
618 struct mlcd_pt *pt;
619 int unit, part;
620 struct mlcd_buf *bp;
621
622 unit = MLCD_UNIT(dev);
623 part = MLCD_PART(dev);
624 sc = mlcd_cd.cd_devs[unit];
625 KASSERT(sc);
626 pt = &sc->sc_pt[part];
627 KASSERT(pt);
628
629 if ((bp = malloc(MLCD_BUF_SZ(sc), M_DEVBUF, flags)) == NULL)
630 return bp;
631
632 /*
633 * malloc() may sleep, and the device may be detached during sleep.
634 * XXX this check is not complete.
635 */
636 if (sc != device_lookup(&mlcd_cd, unit)
637 || sc->sc_stat == MLCD_INIT
638 || sc->sc_stat == MLCD_INIT2
639 || part >= sc->sc_npt || pt != &sc->sc_pt[part]
640 || pt->pt_flags == 0) {
641 free(bp, M_DEVBUF);
642 return NULL;
643 }
644
645 bp->lb_error = 0;
646
647 return bp;
648 }
649
650 static void
651 mlcd_buf_free(bp)
652 struct mlcd_buf *bp;
653 {
654
655 free(bp, M_DEVBUF);
656 }
657
658 /* invert order of bits */
659 static __inline u_int32_t
660 reverse_32(b)
661 u_int32_t b;
662 {
663 u_int32_t b1;
664
665 /* invert every 8bit */
666 b1 = (b & 0x55555555) << 1; b = (b >> 1) & 0x55555555; b |= b1;
667 b1 = (b & 0x33333333) << 2; b = (b >> 2) & 0x33333333; b |= b1;
668 b1 = (b & 0x0f0f0f0f) << 4; b = (b >> 4) & 0x0f0f0f0f; b |= b1;
669
670 /* invert byte order */
671 return bswap32(b);
672 }
673
674 static void
675 mlcd_rotate_bitmap(ptr, size)
676 void *ptr;
677 size_t size;
678 {
679 u_int32_t *p, *q, tmp;
680
681 KDASSERT(size % sizeof(u_int32_t) == 0);
682 for (p = ptr, q = (void *)((char *)ptr + size); p < q; ) {
683 tmp = reverse_32(*p);
684 *p++ = reverse_32(*--q);
685 *q = tmp;
686 }
687 }
688
689 /* ARGSUSED2 */
690 int
691 mlcdwrite(dev, uio, flags)
692 dev_t dev;
693 struct uio *uio;
694 int flags;
695 {
696 struct mlcd_softc *sc;
697 struct mlcd_pt *pt;
698 struct mlcd_buf *bp;
699 int part;
700 off_t devsize;
701 int error = 0;
702
703 part = MLCD_PART(dev);
704 sc = mlcd_cd.cd_devs[MLCD_UNIT(dev)];
705 pt = &sc->sc_pt[part];
706
707 #if 0
708 printf("%s: mlcdwrite: offset %ld, size %d\n",
709 pt->pt_name, (long) uio->uio_offset, uio->uio_resid);
710 #endif
711
712 devsize = pt->pt_nblk * sc->sc_bsize;
713 if (uio->uio_offset % sc->sc_bsize || uio->uio_offset > devsize)
714 return EINVAL;
715
716 if ((bp = mlcd_buf_alloc(dev, M_WAITOK)) == NULL)
717 return EIO; /* device is detached during allocation */
718
719 bp->lb_partno = part;
720
721 while (uio->uio_offset < devsize
722 && uio->uio_resid >= (size_t) sc->sc_bsize) {
723 /* invert block number if upside-down */
724 bp->lb_blkno = (sc->sc_direction == MAPLE_CONN_TOP) ?
725 pt->pt_nblk - uio->uio_offset / sc->sc_bsize - 1 :
726 uio->uio_offset / sc->sc_bsize;
727
728 if ((error = uiomove(bp->lb_data, sc->sc_bsize, uio)) != 0)
729 break;
730
731 if (sc->sc_direction == MAPLE_CONN_TOP) {
732 /* the LCD is upside-down */
733 mlcd_rotate_bitmap(bp->lb_data, sc->sc_bsize);
734 }
735
736 /* queue this transfer */
737 SIMPLEQ_INSERT_TAIL(&sc->sc_q, bp, lb_q);
738
739 if (sc->sc_stat == MLCD_IDLE)
740 mlcdstart(sc);
741
742 tsleep(bp, PRIBIO + 1, "mlcdbuf", 0);
743
744 if ((error = bp->lb_error) != 0) {
745 uio->uio_resid += sc->sc_bsize;
746 break;
747 }
748 }
749
750 mlcd_buf_free(bp);
751
752 return error;
753 }
754
755 int
756 mlcdioctl(dev, cmd, data, flag, p)
757 dev_t dev;
758 u_long cmd;
759 caddr_t data;
760 int flag;
761 struct proc *p;
762 {
763 int unit, part;
764 struct mlcd_softc *sc;
765 struct mlcd_pt *pt;
766
767 unit = MLCD_UNIT(dev);
768 part = MLCD_PART(dev);
769 sc = mlcd_cd.cd_devs[unit];
770 pt = &sc->sc_pt[part];
771
772 switch (cmd) {
773
774 default:
775 /* generic maple ioctl */
776 return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data,
777 flag, p);
778 }
779
780 return 0;
781 }
782