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