1 /* $NetBSD: gdrom.c,v 1.42 2016/03/13 17:59:20 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 Marcus Comstedt 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Marcus Comstedt. 18 * 4. Neither the name of The NetBSD Foundation nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 36 __KERNEL_RCSID(0, "$NetBSD: gdrom.c,v 1.42 2016/03/13 17:59:20 christos Exp $"); 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/device.h> 41 42 #include <sys/buf.h> 43 #include <sys/bufq.h> 44 #include <sys/ioctl.h> 45 #include <sys/fcntl.h> 46 #include <sys/disklabel.h> 47 #include <sys/disk.h> 48 #include <sys/cdio.h> 49 #include <sys/proc.h> 50 #include <sys/conf.h> 51 52 #include <machine/sysasicvar.h> 53 54 #include "ioconf.h" 55 56 static int gdrommatch(device_t, cfdata_t, void *); 57 static void gdromattach(device_t, device_t, void *); 58 59 dev_type_open(gdromopen); 60 dev_type_close(gdromclose); 61 dev_type_read(gdromread); 62 dev_type_write(gdromwrite); 63 dev_type_ioctl(gdromioctl); 64 dev_type_strategy(gdromstrategy); 65 66 const struct bdevsw gdrom_bdevsw = { 67 .d_open = gdromopen, 68 .d_close = gdromclose, 69 .d_strategy = gdromstrategy, 70 .d_ioctl = gdromioctl, 71 .d_dump = nodump, 72 .d_psize = nosize, 73 .d_discard = nodiscard, 74 .d_flag = D_DISK 75 }; 76 77 const struct cdevsw gdrom_cdevsw = { 78 .d_open = gdromopen, 79 .d_close = gdromclose, 80 .d_read = gdromread, 81 .d_write = gdromwrite, 82 .d_ioctl = gdromioctl, 83 .d_stop = nostop, 84 .d_tty = notty, 85 .d_poll = nopoll, 86 .d_mmap = nommap, 87 .d_kqfilter = nokqfilter, 88 .d_discard = nodiscard, 89 .d_flag = D_DISK 90 }; 91 92 struct gdrom_softc { 93 device_t sc_dev; /* generic device info */ 94 struct disk sc_dk; /* generic disk info */ 95 struct bufq_state *sc_bufq; /* device buffer queue */ 96 struct buf curbuf; /* state of current I/O operation */ 97 98 bool is_open; 99 bool is_busy; 100 bool is_active; 101 int openpart_start; /* start sector of currently open partition */ 102 103 int cmd_active; 104 void *cmd_result_buf; /* where to store result data (16 bit aligned) */ 105 int cmd_result_size; /* number of bytes allocated for buf */ 106 int cmd_actual; /* number of bytes actually read */ 107 int cmd_cond; /* resulting condition of command */ 108 }; 109 110 CFATTACH_DECL_NEW(gdrom, sizeof(struct gdrom_softc), 111 gdrommatch, gdromattach, NULL, NULL); 112 113 struct dkdriver gdromdkdriver = { 114 .d_strategy = gdromstrategy 115 }; 116 117 118 struct gd_toc { 119 unsigned int entry[99]; 120 unsigned int first, last; 121 unsigned int leadout; 122 }; 123 124 #ifdef GDROMDEBUG 125 #define DPRINTF(x) printf x 126 #else 127 #define DPRINTF(x) /**/ 128 #endif 129 130 #define TOC_LBA(n) ((n) & 0xffffff00) 131 #define TOC_ADR(n) ((n) & 0x0f) 132 #define TOC_CTRL(n) (((n) & 0xf0) >> 4) 133 #define TOC_TRACK(n) (((n) & 0x0000ff00) >> 8) 134 135 #define GDROM(o) (*(volatile uint8_t *)(0xa05f7000U + (o))) 136 137 #define GDSTATSTAT(n) ((n) & 0xf) 138 #define GDSTATDISK(n) (((n) >> 4) & 0xf) 139 140 #define GDROM_BUSY GDROM(0x18) 141 #define GDROM_DATA (*(volatile uint16_t *)(&GDROM(0x80))) 142 #define GDROM_REGX GDROM(0x84) 143 #define GDROM_STAT GDROM(0x8c) 144 #define GDROM_CNTLO GDROM(0x90) 145 #define GDROM_CNTHI GDROM(0x94) 146 #define GDROM_COND GDROM(0x9c) 147 148 #if 0 149 static int gdrom_getstat(void); 150 #endif 151 static int gdrom_do_command(struct gdrom_softc *, void *, void *, 152 unsigned int, int *); 153 static int gdrom_command_sense(struct gdrom_softc *, void *, void *, 154 unsigned int, int *); 155 static int gdrom_read_toc(struct gdrom_softc *, struct gd_toc *); 156 static int gdrom_read_sectors(struct gdrom_softc *, void *, int, int, 157 int *); 158 static int gdrom_mount_disk(struct gdrom_softc *); 159 static int gdrom_intr(void *); 160 static void gdrom_start(struct gdrom_softc *); 161 162 #if 0 163 int 164 gdrom_getstat(void) 165 { 166 uint8_t s1, s2, s3; 167 168 if (GDROM_BUSY & 0x80) 169 return -1; 170 s1 = GDROM_STAT; 171 s2 = GDROM_STAT; 172 s3 = GDROM_STAT; 173 if (GDROM_BUSY & 0x80) 174 return -1; 175 if (s1 == s2) 176 return s1; 177 else if (s2 == s3) 178 return s2; 179 else 180 return -1; 181 } 182 #endif 183 184 int 185 gdrom_intr(void *arg) 186 { 187 struct gdrom_softc *sc = arg; 188 int s; 189 uint8_t cond; 190 191 s = splbio(); 192 cond = GDROM_COND; 193 DPRINTF(("GDROM: cond = %x\n", cond)); 194 if (!sc->cmd_active) { 195 DPRINTF(("GDROM: inactive IRQ!?\n")); 196 splx(s); 197 return 0; 198 } 199 200 if ((cond & 0x08) != 0) { 201 int cnt = (GDROM_CNTHI << 8) | GDROM_CNTLO; 202 DPRINTF(("GDROM: cnt = %d\n", cnt)); 203 sc->cmd_actual += cnt; 204 if (cnt > 0 && sc->cmd_result_size > 0) { 205 int subcnt = (cnt > sc->cmd_result_size ? 206 sc->cmd_result_size : cnt); 207 uint16_t *ptr = sc->cmd_result_buf; 208 sc->cmd_result_buf = ((uint8_t *)sc->cmd_result_buf) + 209 subcnt; 210 sc->cmd_result_size -= subcnt; 211 cnt -= subcnt; 212 while (subcnt > 0) { 213 *ptr++ = GDROM_DATA; 214 subcnt -= 2; 215 } 216 } 217 while (cnt > 0) { 218 (void)GDROM_DATA; 219 cnt -= 2; 220 } 221 } 222 while ((GDROM_BUSY & 0x80) != 0); 223 224 if ((cond & 0x08) == 0) { 225 sc->cmd_cond = cond; 226 sc->cmd_active = 0; 227 wakeup(&sc->cmd_active); 228 } 229 230 splx(s); 231 return 1; 232 } 233 234 235 int 236 gdrom_do_command(struct gdrom_softc *sc, void *req, void *buf, 237 unsigned int nbyt, int *resid) 238 { 239 int i, s; 240 uint16_t *ptr = req; 241 242 while (GDROM_BUSY & 0x88) 243 ; 244 if (buf != NULL) { 245 GDROM_CNTLO = nbyt & 0xff; 246 GDROM_CNTHI = (nbyt >> 8) & 0xff; 247 GDROM_REGX = 0; 248 } 249 sc->cmd_result_buf = buf; 250 sc->cmd_result_size = nbyt; 251 252 if (GDSTATSTAT(GDROM_STAT) == 0x06) 253 return -1; 254 255 GDROM_COND = 0xa0; 256 DELAY(1); 257 while ((GDROM_BUSY & 0x88) != 0x08) 258 ; 259 260 s = splbio(); 261 262 sc->cmd_actual = 0; 263 sc->cmd_active = 1; 264 265 for (i = 0; i < 6; i++) 266 GDROM_DATA = ptr[i]; 267 268 while (sc->cmd_active) 269 tsleep(&sc->cmd_active, PRIBIO, "gdrom", 0); 270 271 splx(s); 272 273 if (resid != NULL) 274 *resid = sc->cmd_result_size; 275 276 return sc->cmd_cond; 277 } 278 279 280 int gdrom_command_sense(struct gdrom_softc *sc, void *req, void *buf, 281 unsigned int nbyt, int *resid) 282 { 283 /* 284 * 76543210 76543210 285 * 0 0x13 - 286 * 2 - bufsz(hi) 287 * 4 bufsz(lo) - 288 * 6 - - 289 * 8 - - 290 * 10 - - 291 */ 292 uint16_t sense_data[5]; 293 uint8_t cmd[12]; 294 int cond, sense_key, sense_specific; 295 296 cond = gdrom_do_command(sc, req, buf, nbyt, resid); 297 298 if (cond < 0) { 299 DPRINTF(("GDROM: not ready (2:58)\n")); 300 return EIO; 301 } 302 303 if ((cond & 1) == 0) { 304 DPRINTF(("GDROM: no sense. 0:0\n")); 305 return 0; 306 } 307 308 memset(cmd, 0, sizeof(cmd)); 309 310 cmd[0] = 0x13; 311 cmd[4] = sizeof(sense_data); 312 313 gdrom_do_command(sc, cmd, sense_data, sizeof(sense_data), NULL); 314 315 sense_key = sense_data[1] & 0xf; 316 sense_specific = sense_data[4]; 317 if (sense_key == 11 && sense_specific == 0) { 318 DPRINTF(("GDROM: aborted (ignored). 0:0\n")); 319 return 0; 320 } 321 322 DPRINTF(("GDROM: SENSE %d:", sense_key)); 323 DPRINTF(("GDROM: %d\n", sense_specific)); 324 325 return sense_key == 0 ? 0 : EIO; 326 } 327 328 int gdrom_read_toc(struct gdrom_softc *sc, struct gd_toc *toc) 329 { 330 /* 331 * 76543210 76543210 332 * 0 0x14 - 333 * 2 - bufsz(hi) 334 * 4 bufsz(lo) - 335 * 6 - - 336 * 8 - - 337 * 10 - - 338 */ 339 uint8_t cmd[12]; 340 341 memset(cmd, 0, sizeof(cmd)); 342 343 cmd[0] = 0x14; 344 cmd[3] = sizeof(struct gd_toc) >> 8; 345 cmd[4] = sizeof(struct gd_toc) & 0xff; 346 347 return gdrom_command_sense(sc, cmd, toc, sizeof(struct gd_toc), NULL); 348 } 349 350 int gdrom_read_sectors(struct gdrom_softc *sc, void *buf, int sector, int cnt, 351 int *resid) 352 { 353 /* 354 * 76543210 76543210 355 * 0 0x30 datafmt 356 * 2 sec(hi) sec(mid) 357 * 4 sec(lo) - 358 * 6 - - 359 * 8 cnt(hi) cnt(mid) 360 * 10 cnt(lo) - 361 */ 362 uint8_t cmd[12]; 363 364 memset(cmd, 0, sizeof(cmd)); 365 366 cmd[0] = 0x30; 367 cmd[1] = 0x20; 368 cmd[2] = sector >> 16; 369 cmd[3] = sector >> 8; 370 cmd[4] = sector; 371 cmd[8] = cnt >> 16; 372 cmd[9] = cnt >> 8; 373 cmd[10] = cnt; 374 375 return gdrom_command_sense(sc, cmd, buf, cnt << 11, resid); 376 } 377 378 int gdrom_mount_disk(struct gdrom_softc *sc) 379 { 380 /* 381 * 76543210 76543210 382 * 0 0x70 - 383 * 2 0x1f - 384 * 4 - - 385 * 6 - - 386 * 8 - - 387 * 10 - - 388 */ 389 uint8_t cmd[12]; 390 391 memset(cmd, 0, sizeof(cmd)); 392 393 cmd[0] = 0x70; 394 cmd[1] = 0x1f; 395 396 return gdrom_command_sense(sc, cmd, NULL, 0, NULL); 397 } 398 399 int 400 gdrommatch(device_t parent, cfdata_t cf, void *aux) 401 { 402 static int gdrom_matched = 0; 403 404 /* Allow only once instance. */ 405 if (gdrom_matched) 406 return 0; 407 gdrom_matched = 1; 408 409 return 1; 410 } 411 412 void 413 gdromattach(device_t parent, device_t self, void *aux) 414 { 415 struct gdrom_softc *sc; 416 uint32_t p; 417 418 sc = device_private(self); 419 sc->sc_dev = self; 420 421 bufq_alloc(&sc->sc_bufq, "disksort", BUFQ_SORT_RAWBLOCK); 422 423 /* 424 * Initialize and attach the disk structure. 425 */ 426 disk_init(&sc->sc_dk, device_xname(self), &gdromdkdriver); 427 disk_attach(&sc->sc_dk); 428 429 /* 430 * reenable disabled drive 431 */ 432 *((volatile uint32_t *)0xa05f74e4) = 0x1fffff; 433 for (p = 0; p < 0x200000 / 4; p++) 434 (void)((volatile uint32_t *)0xa0000000)[p]; 435 436 printf(": %s\n", sysasic_intr_string(SYSASIC_IRL9)); 437 sysasic_intr_establish(SYSASIC_EVENT_GDROM, IPL_BIO, SYSASIC_IRL9, 438 gdrom_intr, sc); 439 } 440 441 int 442 gdromopen(dev_t dev, int flags, int devtype, struct lwp *l) 443 { 444 struct gdrom_softc *sc; 445 int s, error, unit, cnt; 446 struct gd_toc toc; 447 448 DPRINTF(("GDROM: open\n")); 449 450 unit = DISKUNIT(dev); 451 452 sc = device_lookup_private(&gdrom_cd, unit); 453 if (sc == NULL) 454 return ENXIO; 455 456 if (sc->is_open) 457 return EBUSY; 458 459 s = splbio(); 460 while (sc->is_busy) 461 tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0); 462 sc->is_busy = true; 463 splx(s); 464 465 for (cnt = 0; cnt < 5; cnt++) 466 if ((error = gdrom_mount_disk(sc)) == 0) 467 break; 468 469 if (error == 0) 470 error = gdrom_read_toc(sc, &toc); 471 472 sc->is_busy = false; 473 wakeup(&sc->is_busy); 474 475 if (error != 0) 476 return error; 477 478 sc->is_open = true; 479 sc->openpart_start = 150; 480 481 DPRINTF(("GDROM: open OK\n")); 482 return 0; 483 } 484 485 int 486 gdromclose(dev_t dev, int flags, int devtype, struct lwp *l) 487 { 488 struct gdrom_softc *sc; 489 int unit; 490 491 DPRINTF(("GDROM: close\n")); 492 493 unit = DISKUNIT(dev); 494 sc = device_lookup_private(&gdrom_cd, unit); 495 496 sc->is_open = false; 497 498 return 0; 499 } 500 501 void 502 gdromstrategy(struct buf *bp) 503 { 504 struct gdrom_softc *sc; 505 int s, unit; 506 507 DPRINTF(("GDROM: strategy\n")); 508 509 unit = DISKUNIT(bp->b_dev); 510 sc = device_lookup_private(&gdrom_cd, unit); 511 512 if (bp->b_bcount == 0) 513 goto done; 514 515 bp->b_rawblkno = bp->b_blkno / (2048 / DEV_BSIZE) + sc->openpart_start; 516 517 DPRINTF(("GDROM: read_sectors(%p, %lld, %d) [%d bytes]\n", 518 bp->b_data, bp->b_rawblkno, 519 bp->b_bcount >> 11, bp->b_bcount)); 520 521 s = splbio(); 522 bufq_put(sc->sc_bufq, bp); 523 splx(s); 524 if (!sc->is_active) 525 gdrom_start(sc); 526 return; 527 528 done: 529 bp->b_resid = bp->b_bcount; 530 biodone(bp); 531 } 532 533 void 534 gdrom_start(struct gdrom_softc *sc) 535 { 536 struct buf *bp; 537 int error, resid, s; 538 539 sc->is_active = true; 540 541 for (;;) { 542 s = splbio(); 543 bp = bufq_get(sc->sc_bufq); 544 if (bp == NULL) { 545 splx(s); 546 break; 547 } 548 549 while (sc->is_busy) 550 tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0); 551 sc->is_busy = true; 552 disk_busy(&sc->sc_dk); 553 splx(s); 554 555 error = gdrom_read_sectors(sc, bp->b_data, bp->b_rawblkno, 556 bp->b_bcount >> 11, &resid); 557 bp->b_error = error; 558 bp->b_resid = resid; 559 if (error != 0) 560 bp->b_resid = bp->b_bcount; 561 562 sc->is_busy = false; 563 wakeup(&sc->is_busy); 564 565 s = splbio(); 566 disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid, 567 (bp->b_flags & B_READ) != 0); 568 splx(s); 569 biodone(bp); 570 } 571 572 sc->is_active = false; 573 } 574 575 int 576 gdromioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) 577 { 578 struct gdrom_softc *sc; 579 int unit, error; 580 581 DPRINTF(("GDROM: ioctl %lx\n", cmd)); 582 583 unit = DISKUNIT(dev); 584 sc = device_lookup_private(&gdrom_cd, unit); 585 586 switch (cmd) { 587 case CDIOREADMSADDR: { 588 int s, track, sessno = *(int *)addr; 589 struct gd_toc toc; 590 591 if (sessno != 0) 592 return EINVAL; 593 594 s = splbio(); 595 while (sc->is_busy) 596 tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0); 597 sc->is_busy = true; 598 splx(s); 599 600 error = gdrom_read_toc(sc, &toc); 601 602 sc->is_busy = false; 603 wakeup(&sc->is_busy); 604 605 if (error != 0) 606 return error; 607 #ifdef GDROMDEBUGTOC 608 { /* Dump the GDROM TOC */ 609 unsigned char *ptr = (unsigned char *)&toc; 610 int i; 611 612 printf("gdrom: TOC\n"); 613 for(i = 0; i < sizeof(toc); ++i) { 614 printf("%02x", *ptr++); 615 if( i%32 == 31) 616 printf("\n"); 617 else if( i%4 == 3) 618 printf(","); 619 } 620 printf("\n"); 621 } 622 #endif 623 for (track = TOC_TRACK(toc.last); 624 track >= TOC_TRACK(toc.first); 625 --track) { 626 if (track < 1 || track > 100) 627 return ENXIO; 628 if (TOC_CTRL(toc.entry[track - 1])) 629 break; 630 } 631 632 #ifdef GDROMDEBUGTOC 633 printf("gdrom: Using track %d, LBA %u\n", track, 634 TOC_LBA(toc.entry[track - 1])); 635 #endif 636 637 *(int *)addr = htonl(TOC_LBA(toc.entry[track - 1])) - 638 sc->openpart_start; 639 640 return 0; 641 } 642 default: 643 return ENOTTY; 644 } 645 646 #ifdef DIAGNOSTIC 647 panic("gdromioctl: impossible"); 648 #endif 649 } 650 651 652 int 653 gdromread(dev_t dev, struct uio *uio, int flags) 654 { 655 656 DPRINTF(("GDROM: read\n")); 657 return physio(gdromstrategy, NULL, dev, B_READ, minphys, uio); 658 } 659 660 int 661 gdromwrite(dev_t dev, struct uio *uio, int flags) 662 { 663 664 return EROFS; 665 } 666