1 /* $NetBSD: gdrom.c,v 1.40 2014/07/25 08:10:32 dholland 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.40 2014/07/25 08:10:32 dholland 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 = { gdromstrategy }; 114 115 116 struct gd_toc { 117 unsigned int entry[99]; 118 unsigned int first, last; 119 unsigned int leadout; 120 }; 121 122 #ifdef GDROMDEBUG 123 #define DPRINTF(x) printf x 124 #else 125 #define DPRINTF(x) /**/ 126 #endif 127 128 #define TOC_LBA(n) ((n) & 0xffffff00) 129 #define TOC_ADR(n) ((n) & 0x0f) 130 #define TOC_CTRL(n) (((n) & 0xf0) >> 4) 131 #define TOC_TRACK(n) (((n) & 0x0000ff00) >> 8) 132 133 #define GDROM(o) (*(volatile uint8_t *)(0xa05f7000 + (o))) 134 135 #define GDSTATSTAT(n) ((n) & 0xf) 136 #define GDSTATDISK(n) (((n) >> 4) & 0xf) 137 138 #define GDROM_BUSY GDROM(0x18) 139 #define GDROM_DATA (*(volatile uint16_t *)(&GDROM(0x80))) 140 #define GDROM_REGX GDROM(0x84) 141 #define GDROM_STAT GDROM(0x8c) 142 #define GDROM_CNTLO GDROM(0x90) 143 #define GDROM_CNTHI GDROM(0x94) 144 #define GDROM_COND GDROM(0x9c) 145 146 #if 0 147 static int gdrom_getstat(void); 148 #endif 149 static int gdrom_do_command(struct gdrom_softc *, void *, void *, 150 unsigned int, int *); 151 static int gdrom_command_sense(struct gdrom_softc *, void *, void *, 152 unsigned int, int *); 153 static int gdrom_read_toc(struct gdrom_softc *, struct gd_toc *); 154 static int gdrom_read_sectors(struct gdrom_softc *, void *, int, int, 155 int *); 156 static int gdrom_mount_disk(struct gdrom_softc *); 157 static int gdrom_intr(void *); 158 static void gdrom_start(struct gdrom_softc *); 159 160 #if 0 161 int 162 gdrom_getstat(void) 163 { 164 uint8_t s1, s2, s3; 165 166 if (GDROM_BUSY & 0x80) 167 return -1; 168 s1 = GDROM_STAT; 169 s2 = GDROM_STAT; 170 s3 = GDROM_STAT; 171 if (GDROM_BUSY & 0x80) 172 return -1; 173 if (s1 == s2) 174 return s1; 175 else if (s2 == s3) 176 return s2; 177 else 178 return -1; 179 } 180 #endif 181 182 int 183 gdrom_intr(void *arg) 184 { 185 struct gdrom_softc *sc = arg; 186 int s; 187 uint8_t cond; 188 189 s = splbio(); 190 cond = GDROM_COND; 191 DPRINTF(("GDROM: cond = %x\n", cond)); 192 if (!sc->cmd_active) { 193 DPRINTF(("GDROM: inactive IRQ!?\n")); 194 splx(s); 195 return 0; 196 } 197 198 if ((cond & 0x08) != 0) { 199 int cnt = (GDROM_CNTHI << 8) | GDROM_CNTLO; 200 DPRINTF(("GDROM: cnt = %d\n", cnt)); 201 sc->cmd_actual += cnt; 202 if (cnt > 0 && sc->cmd_result_size > 0) { 203 int subcnt = (cnt > sc->cmd_result_size ? 204 sc->cmd_result_size : cnt); 205 uint16_t *ptr = sc->cmd_result_buf; 206 sc->cmd_result_buf = ((uint8_t *)sc->cmd_result_buf) + 207 subcnt; 208 sc->cmd_result_size -= subcnt; 209 cnt -= subcnt; 210 while (subcnt > 0) { 211 *ptr++ = GDROM_DATA; 212 subcnt -= 2; 213 } 214 } 215 while (cnt > 0) { 216 (void)GDROM_DATA; 217 cnt -= 2; 218 } 219 } 220 while ((GDROM_BUSY & 0x80) != 0); 221 222 if ((cond & 0x08) == 0) { 223 sc->cmd_cond = cond; 224 sc->cmd_active = 0; 225 wakeup(&sc->cmd_active); 226 } 227 228 splx(s); 229 return 1; 230 } 231 232 233 int 234 gdrom_do_command(struct gdrom_softc *sc, void *req, void *buf, 235 unsigned int nbyt, int *resid) 236 { 237 int i, s; 238 uint16_t *ptr = req; 239 240 while (GDROM_BUSY & 0x88) 241 ; 242 if (buf != NULL) { 243 GDROM_CNTLO = nbyt & 0xff; 244 GDROM_CNTHI = (nbyt >> 8) & 0xff; 245 GDROM_REGX = 0; 246 } 247 sc->cmd_result_buf = buf; 248 sc->cmd_result_size = nbyt; 249 250 if (GDSTATSTAT(GDROM_STAT) == 0x06) 251 return -1; 252 253 GDROM_COND = 0xa0; 254 DELAY(1); 255 while ((GDROM_BUSY & 0x88) != 0x08) 256 ; 257 258 s = splbio(); 259 260 sc->cmd_actual = 0; 261 sc->cmd_active = 1; 262 263 for (i = 0; i < 6; i++) 264 GDROM_DATA = ptr[i]; 265 266 while (sc->cmd_active) 267 tsleep(&sc->cmd_active, PRIBIO, "gdrom", 0); 268 269 splx(s); 270 271 if (resid != NULL) 272 *resid = sc->cmd_result_size; 273 274 return sc->cmd_cond; 275 } 276 277 278 int gdrom_command_sense(struct gdrom_softc *sc, void *req, void *buf, 279 unsigned int nbyt, int *resid) 280 { 281 /* 282 * 76543210 76543210 283 * 0 0x13 - 284 * 2 - bufsz(hi) 285 * 4 bufsz(lo) - 286 * 6 - - 287 * 8 - - 288 * 10 - - 289 */ 290 uint16_t sense_data[5]; 291 uint8_t cmd[12]; 292 int cond, sense_key, sense_specific; 293 294 cond = gdrom_do_command(sc, req, buf, nbyt, resid); 295 296 if (cond < 0) { 297 DPRINTF(("GDROM: not ready (2:58)\n")); 298 return EIO; 299 } 300 301 if ((cond & 1) == 0) { 302 DPRINTF(("GDROM: no sense. 0:0\n")); 303 return 0; 304 } 305 306 memset(cmd, 0, sizeof(cmd)); 307 308 cmd[0] = 0x13; 309 cmd[4] = sizeof(sense_data); 310 311 gdrom_do_command(sc, cmd, sense_data, sizeof(sense_data), NULL); 312 313 sense_key = sense_data[1] & 0xf; 314 sense_specific = sense_data[4]; 315 if (sense_key == 11 && sense_specific == 0) { 316 DPRINTF(("GDROM: aborted (ignored). 0:0\n")); 317 return 0; 318 } 319 320 DPRINTF(("GDROM: SENSE %d:", sense_key)); 321 DPRINTF(("GDROM: %d\n", sense_specific)); 322 323 return sense_key == 0 ? 0 : EIO; 324 } 325 326 int gdrom_read_toc(struct gdrom_softc *sc, struct gd_toc *toc) 327 { 328 /* 329 * 76543210 76543210 330 * 0 0x14 - 331 * 2 - bufsz(hi) 332 * 4 bufsz(lo) - 333 * 6 - - 334 * 8 - - 335 * 10 - - 336 */ 337 uint8_t cmd[12]; 338 339 memset(cmd, 0, sizeof(cmd)); 340 341 cmd[0] = 0x14; 342 cmd[3] = sizeof(struct gd_toc) >> 8; 343 cmd[4] = sizeof(struct gd_toc) & 0xff; 344 345 return gdrom_command_sense(sc, cmd, toc, sizeof(struct gd_toc), NULL); 346 } 347 348 int gdrom_read_sectors(struct gdrom_softc *sc, void *buf, int sector, int cnt, 349 int *resid) 350 { 351 /* 352 * 76543210 76543210 353 * 0 0x30 datafmt 354 * 2 sec(hi) sec(mid) 355 * 4 sec(lo) - 356 * 6 - - 357 * 8 cnt(hi) cnt(mid) 358 * 10 cnt(lo) - 359 */ 360 uint8_t cmd[12]; 361 362 memset(cmd, 0, sizeof(cmd)); 363 364 cmd[0] = 0x30; 365 cmd[1] = 0x20; 366 cmd[2] = sector >> 16; 367 cmd[3] = sector >> 8; 368 cmd[4] = sector; 369 cmd[8] = cnt >> 16; 370 cmd[9] = cnt >> 8; 371 cmd[10] = cnt; 372 373 return gdrom_command_sense(sc, cmd, buf, cnt << 11, resid); 374 } 375 376 int gdrom_mount_disk(struct gdrom_softc *sc) 377 { 378 /* 379 * 76543210 76543210 380 * 0 0x70 - 381 * 2 0x1f - 382 * 4 - - 383 * 6 - - 384 * 8 - - 385 * 10 - - 386 */ 387 uint8_t cmd[12]; 388 389 memset(cmd, 0, sizeof(cmd)); 390 391 cmd[0] = 0x70; 392 cmd[1] = 0x1f; 393 394 return gdrom_command_sense(sc, cmd, NULL, 0, NULL); 395 } 396 397 int 398 gdrommatch(device_t parent, cfdata_t cf, void *aux) 399 { 400 static int gdrom_matched = 0; 401 402 /* Allow only once instance. */ 403 if (gdrom_matched) 404 return 0; 405 gdrom_matched = 1; 406 407 return 1; 408 } 409 410 void 411 gdromattach(device_t parent, device_t self, void *aux) 412 { 413 struct gdrom_softc *sc; 414 uint32_t p; 415 416 sc = device_private(self); 417 sc->sc_dev = self; 418 419 bufq_alloc(&sc->sc_bufq, "disksort", BUFQ_SORT_RAWBLOCK); 420 421 /* 422 * Initialize and attach the disk structure. 423 */ 424 disk_init(&sc->sc_dk, device_xname(self), &gdromdkdriver); 425 disk_attach(&sc->sc_dk); 426 427 /* 428 * reenable disabled drive 429 */ 430 *((volatile uint32_t *)0xa05f74e4) = 0x1fffff; 431 for (p = 0; p < 0x200000 / 4; p++) 432 (void)((volatile uint32_t *)0xa0000000)[p]; 433 434 printf(": %s\n", sysasic_intr_string(SYSASIC_IRL9)); 435 sysasic_intr_establish(SYSASIC_EVENT_GDROM, IPL_BIO, SYSASIC_IRL9, 436 gdrom_intr, sc); 437 } 438 439 int 440 gdromopen(dev_t dev, int flags, int devtype, struct lwp *l) 441 { 442 struct gdrom_softc *sc; 443 int s, error, unit, cnt; 444 struct gd_toc toc; 445 446 DPRINTF(("GDROM: open\n")); 447 448 unit = DISKUNIT(dev); 449 450 sc = device_lookup_private(&gdrom_cd, unit); 451 if (sc == NULL) 452 return ENXIO; 453 454 if (sc->is_open) 455 return EBUSY; 456 457 s = splbio(); 458 while (sc->is_busy) 459 tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0); 460 sc->is_busy = true; 461 splx(s); 462 463 for (cnt = 0; cnt < 5; cnt++) 464 if ((error = gdrom_mount_disk(sc)) == 0) 465 break; 466 467 if (error == 0) 468 error = gdrom_read_toc(sc, &toc); 469 470 sc->is_busy = false; 471 wakeup(&sc->is_busy); 472 473 if (error != 0) 474 return error; 475 476 sc->is_open = true; 477 sc->openpart_start = 150; 478 479 DPRINTF(("GDROM: open OK\n")); 480 return 0; 481 } 482 483 int 484 gdromclose(dev_t dev, int flags, int devtype, struct lwp *l) 485 { 486 struct gdrom_softc *sc; 487 int unit; 488 489 DPRINTF(("GDROM: close\n")); 490 491 unit = DISKUNIT(dev); 492 sc = device_lookup_private(&gdrom_cd, unit); 493 494 sc->is_open = false; 495 496 return 0; 497 } 498 499 void 500 gdromstrategy(struct buf *bp) 501 { 502 struct gdrom_softc *sc; 503 int s, unit; 504 505 DPRINTF(("GDROM: strategy\n")); 506 507 unit = DISKUNIT(bp->b_dev); 508 sc = device_lookup_private(&gdrom_cd, unit); 509 510 if (bp->b_bcount == 0) 511 goto done; 512 513 bp->b_rawblkno = bp->b_blkno / (2048 / DEV_BSIZE) + sc->openpart_start; 514 515 DPRINTF(("GDROM: read_sectors(%p, %lld, %d) [%d bytes]\n", 516 bp->b_data, bp->b_rawblkno, 517 bp->b_bcount >> 11, bp->b_bcount)); 518 519 s = splbio(); 520 bufq_put(sc->sc_bufq, bp); 521 splx(s); 522 if (!sc->is_active) 523 gdrom_start(sc); 524 return; 525 526 done: 527 bp->b_resid = bp->b_bcount; 528 biodone(bp); 529 } 530 531 void 532 gdrom_start(struct gdrom_softc *sc) 533 { 534 struct buf *bp; 535 int error, resid, s; 536 537 sc->is_active = true; 538 539 for (;;) { 540 s = splbio(); 541 bp = bufq_get(sc->sc_bufq); 542 if (bp == NULL) { 543 splx(s); 544 break; 545 } 546 547 while (sc->is_busy) 548 tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0); 549 sc->is_busy = true; 550 disk_busy(&sc->sc_dk); 551 splx(s); 552 553 error = gdrom_read_sectors(sc, bp->b_data, bp->b_rawblkno, 554 bp->b_bcount >> 11, &resid); 555 bp->b_error = error; 556 bp->b_resid = resid; 557 if (error != 0) 558 bp->b_resid = bp->b_bcount; 559 560 sc->is_busy = false; 561 wakeup(&sc->is_busy); 562 563 s = splbio(); 564 disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid, 565 (bp->b_flags & B_READ) != 0); 566 splx(s); 567 biodone(bp); 568 } 569 570 sc->is_active = false; 571 } 572 573 int 574 gdromioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) 575 { 576 struct gdrom_softc *sc; 577 int unit, error; 578 579 DPRINTF(("GDROM: ioctl %lx\n", cmd)); 580 581 unit = DISKUNIT(dev); 582 sc = device_lookup_private(&gdrom_cd, unit); 583 584 switch (cmd) { 585 case CDIOREADMSADDR: { 586 int s, track, sessno = *(int *)addr; 587 struct gd_toc toc; 588 589 if (sessno != 0) 590 return EINVAL; 591 592 s = splbio(); 593 while (sc->is_busy) 594 tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0); 595 sc->is_busy = true; 596 splx(s); 597 598 error = gdrom_read_toc(sc, &toc); 599 600 sc->is_busy = false; 601 wakeup(&sc->is_busy); 602 603 if (error != 0) 604 return error; 605 #ifdef GDROMDEBUGTOC 606 { /* Dump the GDROM TOC */ 607 unsigned char *ptr = (unsigned char *)&toc; 608 int i; 609 610 printf("gdrom: TOC\n"); 611 for(i = 0; i < sizeof(toc); ++i) { 612 printf("%02x", *ptr++); 613 if( i%32 == 31) 614 printf("\n"); 615 else if( i%4 == 3) 616 printf(","); 617 } 618 printf("\n"); 619 } 620 #endif 621 for (track = TOC_TRACK(toc.last); 622 track >= TOC_TRACK(toc.first); 623 --track) { 624 if (track < 1 || track > 100) 625 return ENXIO; 626 if (TOC_CTRL(toc.entry[track - 1])) 627 break; 628 } 629 630 #ifdef GDROMDEBUGTOC 631 printf("gdrom: Using track %d, LBA %u\n", track, 632 TOC_LBA(toc.entry[track - 1])); 633 #endif 634 635 *(int *)addr = htonl(TOC_LBA(toc.entry[track - 1])) - 636 sc->openpart_start; 637 638 return 0; 639 } 640 default: 641 return ENOTTY; 642 } 643 644 #ifdef DIAGNOSTIC 645 panic("gdromioctl: impossible"); 646 #endif 647 } 648 649 650 int 651 gdromread(dev_t dev, struct uio *uio, int flags) 652 { 653 654 DPRINTF(("GDROM: read\n")); 655 return physio(gdromstrategy, NULL, dev, B_READ, minphys, uio); 656 } 657 658 int 659 gdromwrite(dev_t dev, struct uio *uio, int flags) 660 { 661 662 return EROFS; 663 } 664