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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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