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