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