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