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