1 /* $NetBSD: mlcd.c,v 1.17 2014/03/26 16:08:45 christos 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.17 2014/03/26 16:08:45 christos 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