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