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