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