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