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