1 /* $NetBSD: di.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2025 Jared McNeill <jmcneill (at) invisible.ca> 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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: di.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/device.h> 35 #include <sys/systm.h> 36 #include <sys/callout.h> 37 #include <sys/buf.h> 38 #include <sys/dvdio.h> 39 40 #include <uvm/uvm_extern.h> 41 42 #include <dev/scsipi/scsi_all.h> 43 #include <dev/scsipi/scsi_disk.h> 44 #include <dev/scsipi/scsipi_all.h> 45 #include <dev/scsipi/scsipi_cd.h> 46 #include <dev/scsipi/scsipi_disk.h> 47 #include <dev/scsipi/scsiconf.h> 48 49 #include <machine/wii.h> 50 #include <machine/wiiu.h> 51 #include <machine/pio.h> 52 #include "ahb.h" 53 54 #ifdef DI_DEBUG 55 #define DPRINTF(dv, fmt, ...) device_printf(dv, fmt, ## __VA_ARGS__) 56 #else 57 #define DPRINTF(dv, fmt, ...) 58 #endif 59 60 #define DI_REG_SIZE 0x40 61 62 #define DISR 0x00 63 #define DISR_BRKINT __BIT(6) 64 #define DISR_BRKINTMASK __BIT(5) 65 #define DISR_TCINT __BIT(4) 66 #define DISR_TCINTMASK __BIT(3) 67 #define DISR_DEINT __BIT(2) 68 #define DISR_DEINTMASK __BIT(1) 69 #define DISR_BRK __BIT(0) 70 #define DICVR 0x04 71 #define DICVR_CVRINT __BIT(2) 72 #define DICVR_CVRINTMASK __BIT(1) 73 #define DICVR_CVR __BIT(0) 74 #define DICMDBUF0 0x08 75 #define DICMDBUF1 0x0c 76 #define DICMDBUF2 0x10 77 #define DIMAR 0x14 78 #define DILENGTH 0x18 79 #define DICR 0x1c 80 #define DICR_DMA __BIT(1) 81 #define DICR_TSTART __BIT(0) 82 #define DIMMBUF 0x20 83 #define DICFG 0x24 84 85 #define DI_CMD_INQUIRY 0x12000000 86 #define DI_CMD_REPORT_KEY(x) (0xa4000000 | ((uint32_t)(x) << 16)) 87 #define DI_CMD_READ_DVD_STRUCT(x) (0xad000000 | ((uint32_t)(x) << 24)) 88 #define DI_CMD_READ_DVD 0xd0000000 89 #define DI_CMD_REQUEST_ERROR 0xe0000000 90 #define DI_CMD_STOP_MOTOR 0xe3000000 91 92 #define DVDBLOCKSIZE 2048 93 94 #define DI_IDLE_TIMEOUT_MS 30000 95 96 struct di_softc; 97 98 static int di_match(device_t, cfdata_t, void *); 99 static void di_attach(device_t, device_t, void *); 100 101 static bool di_shutdown(device_t, int); 102 103 static int di_intr(void *); 104 static void di_timeout(void *); 105 static void di_idle(void *); 106 107 static void di_request(struct scsipi_channel *, scsipi_adapter_req_t, 108 void *); 109 static void di_init_regs(struct di_softc *); 110 static void di_reset(struct di_softc *, bool); 111 112 struct di_response_inquiry { 113 uint16_t revision_level; 114 uint16_t device_code; 115 uint32_t release_date; 116 uint8_t padding[24]; 117 } __aligned(4); 118 CTASSERT(sizeof(struct di_response_inquiry) == 0x20); 119 120 struct di_softc { 121 device_t sc_dev; 122 bus_space_tag_t sc_bst; 123 bus_space_handle_t sc_bsh; 124 bus_dma_tag_t sc_dmat; 125 126 struct scsipi_adapter sc_adapter; 127 struct scsipi_channel sc_channel; 128 129 struct scsipi_xfer *sc_cur_xs; 130 callout_t sc_timeout; 131 callout_t sc_idle; 132 int sc_pamr; 133 134 bus_dmamap_t sc_dma_map; 135 void *sc_dma_addr; 136 size_t sc_dma_size; 137 bus_dma_segment_t sc_dma_segs[1]; 138 }; 139 140 #define WR4(sc, reg, val) \ 141 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 142 #define RD4(sc, reg) \ 143 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 144 145 CFATTACH_DECL_NEW(di, sizeof(struct di_softc), 146 di_match, di_attach, NULL, NULL); 147 148 static int 149 di_match(device_t parent, cfdata_t cf, void *aux) 150 { 151 return !wiiu_native; 152 } 153 154 static void 155 di_attach(device_t parent, device_t self, void *aux) 156 { 157 struct ahb_attach_args *aaa = aux; 158 struct di_softc *sc = device_private(self); 159 struct scsipi_adapter *adapt = &sc->sc_adapter; 160 struct scsipi_channel *chan = &sc->sc_channel; 161 int error, nsegs; 162 163 sc->sc_dev = self; 164 sc->sc_dmat = aaa->aaa_dmat; 165 sc->sc_bst = aaa->aaa_bst; 166 error = bus_space_map(sc->sc_bst, aaa->aaa_addr, DI_REG_SIZE, 167 0, &sc->sc_bsh); 168 if (error != 0) { 169 aprint_error(": couldn't map registers (%d)\n", error); 170 return; 171 } 172 173 aprint_naive("\n"); 174 aprint_normal(": Drive Interface\n"); 175 176 callout_init(&sc->sc_timeout, 0); 177 callout_setfunc(&sc->sc_timeout, di_timeout, sc); 178 callout_init(&sc->sc_idle, 0); 179 callout_setfunc(&sc->sc_idle, di_idle, sc); 180 181 sc->sc_dma_size = MAXPHYS; 182 error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dma_size, PAGE_SIZE, 0, 183 sc->sc_dma_segs, 1, &nsegs, BUS_DMA_WAITOK); 184 if (error != 0) { 185 aprint_error_dev(self, "bus_dmamem_alloc failed: %d\n", error); 186 return; 187 } 188 error = bus_dmamem_map(sc->sc_dmat, sc->sc_dma_segs, nsegs, 189 sc->sc_dma_size, &sc->sc_dma_addr, BUS_DMA_WAITOK); 190 if (error != 0) { 191 aprint_error_dev(self, "bus_dmamem_map failed: %d\n", error); 192 return; 193 } 194 error = bus_dmamap_create(sc->sc_dmat, sc->sc_dma_size, nsegs, 195 sc->sc_dma_size, 0, BUS_DMA_WAITOK, &sc->sc_dma_map); 196 if (error != 0) { 197 aprint_error_dev(self, "bus_dmamap_create failed: %d\n", error); 198 return; 199 } 200 error = bus_dmamap_load(sc->sc_dmat, sc->sc_dma_map, sc->sc_dma_addr, 201 sc->sc_dma_size, NULL, BUS_DMA_WAITOK); 202 if (error != 0) { 203 aprint_error_dev(self, "bus_dmamap_load failed: %d\n", error); 204 return; 205 } 206 207 memset(adapt, 0, sizeof(*adapt)); 208 adapt->adapt_nchannels = 1; 209 adapt->adapt_request = di_request; 210 adapt->adapt_minphys = minphys; 211 adapt->adapt_dev = self; 212 adapt->adapt_max_periph = 1; 213 adapt->adapt_openings = 1; 214 215 memset(chan, 0, sizeof(*chan)); 216 chan->chan_bustype = &scsi_bustype; 217 chan->chan_ntargets = 2; 218 chan->chan_nluns = 1; 219 chan->chan_id = 0; 220 chan->chan_flags = SCSIPI_CHAN_NOSETTLE; 221 chan->chan_adapter = adapt; 222 223 config_found(self, chan, scsiprint, CFARGS(.iattr = "scsi")); 224 225 ahb_intr_establish(aaa->aaa_irq, IPL_BIO, di_intr, sc, 226 device_xname(self)); 227 228 di_init_regs(sc); 229 callout_schedule(&sc->sc_idle, mstohz(DI_IDLE_TIMEOUT_MS)); 230 231 pmf_device_register1(self, NULL, NULL, di_shutdown); 232 } 233 234 static bool 235 di_shutdown(device_t dev, int how) 236 { 237 struct di_softc *sc = device_private(dev); 238 239 di_reset(sc, false); 240 241 return true; 242 } 243 244 static void 245 di_sense(struct scsipi_xfer *xs, uint8_t skey, uint8_t asc, uint8_t ascq) 246 { 247 struct scsi_sense_data *sense = &xs->sense.scsi_sense; 248 249 xs->error = XS_SENSE; 250 sense->response_code = SSD_RCODE_CURRENT | SSD_RCODE_VALID; 251 sense->flags = skey; 252 sense->asc = asc; 253 sense->ascq = ascq; 254 } 255 256 static void 257 di_request_error_sync(struct di_softc *sc, struct scsipi_xfer *xs) 258 { 259 uint32_t imm; 260 int s; 261 262 s = splbio(); 263 WR4(sc, DICMDBUF0, DI_CMD_REQUEST_ERROR); 264 WR4(sc, DICMDBUF1, 0); 265 WR4(sc, DICMDBUF2, 0); 266 WR4(sc, DILENGTH, 4); 267 WR4(sc, DICR, DICR_TSTART); 268 while (((RD4(sc, DISR) & DISR_TCINT)) == 0) { 269 delay(1); 270 } 271 imm = RD4(sc, DIMMBUF); 272 splx(s); 273 274 DPRINTF(sc->sc_dev, "ERR IMMBUF = 0x%08x\n", imm); 275 di_sense(xs, (imm >> 16) & 0xff, (imm >> 8) & 0xff, imm & 0xff); 276 } 277 278 static int 279 di_transfer_error(struct di_softc *sc, struct scsipi_xfer *xs) 280 { 281 if (xs == NULL) { 282 return 0; 283 } 284 285 DPRINTF(sc->sc_dev, "transfer error\n"); 286 287 callout_stop(&sc->sc_timeout); 288 di_request_error_sync(sc, xs); 289 sc->sc_cur_xs = NULL; 290 scsipi_done(xs); 291 292 return 1; 293 } 294 295 static int 296 di_transfer_complete(struct di_softc *sc, struct scsipi_xfer *xs) 297 { 298 struct scsipi_generic *cmd; 299 struct scsipi_inquiry_data *inqbuf; 300 struct scsipi_read_cd_cap_data *cdcap; 301 struct di_response_inquiry *rinq; 302 uint32_t imm; 303 uint8_t *data; 304 char buf[5]; 305 306 if (xs == NULL) { 307 DPRINTF(sc->sc_dev, "no active transfer\n"); 308 return 0; 309 } 310 311 KASSERT(sc->sc_cur_xs == xs); 312 313 cmd = xs->cmd; 314 315 switch (cmd->opcode) { 316 case INQUIRY: 317 inqbuf = (struct scsipi_inquiry_data *)xs->data; 318 rinq = sc->sc_dma_addr; 319 320 bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, sizeof(*rinq), 321 BUS_DMASYNC_POSTREAD); 322 323 DPRINTF(sc->sc_dev, "revision_level %#x " 324 "device_code %#x " 325 "release_date %#x\n", 326 rinq->revision_level, 327 rinq->device_code, 328 rinq->release_date); 329 330 memset(inqbuf, 0, sizeof(*inqbuf)); 331 inqbuf->device = T_CDROM; 332 inqbuf->dev_qual2 = SID_REMOVABLE; 333 strncpy(inqbuf->vendor, "NINTENDO", sizeof(inqbuf->vendor)); 334 snprintf(inqbuf->product, sizeof(inqbuf->product), "%08x", 335 rinq->release_date); 336 snprintf(buf, sizeof(buf), "%04x", rinq->revision_level); 337 memcpy(inqbuf->revision, buf, sizeof(inqbuf->revision)); 338 xs->resid = 0; 339 break; 340 341 case SCSI_TEST_UNIT_READY: 342 case SCSI_REQUEST_SENSE: 343 imm = RD4(sc, DIMMBUF); 344 DPRINTF(sc->sc_dev, "TUR IMMBUF = 0x%08x\n", imm); 345 switch ((imm >> 24) & 0xff) { 346 case 0: 347 di_sense(xs, (imm >> 16) & 0xff, (imm >> 8) & 0xff, 348 imm & 0xff); 349 break; 350 default: 351 di_sense(xs, SKEY_MEDIUM_ERROR, 0, 0); 352 break; 353 } 354 break; 355 356 case SCSI_READ_6_COMMAND: 357 case READ_10: 358 case GPCMD_REPORT_KEY: 359 bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, xs->datalen, 360 BUS_DMASYNC_POSTREAD); 361 memcpy(xs->data, sc->sc_dma_addr, xs->datalen); 362 xs->resid = 0; 363 break; 364 365 case GPCMD_READ_DVD_STRUCTURE: 366 bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, DVDBLOCKSIZE, 367 BUS_DMASYNC_POSTREAD); 368 memcpy(xs->data + 4, sc->sc_dma_addr, xs->datalen - 4); 369 xs->resid = 0; 370 break; 371 372 case READ_CD_CAPACITY: 373 cdcap = (struct scsipi_read_cd_cap_data *)xs->data; 374 375 bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 0, DVDBLOCKSIZE, 376 BUS_DMASYNC_POSTREAD); 377 data = sc->sc_dma_addr; 378 _lto4b(DVDBLOCKSIZE, cdcap->length); 379 memcpy(cdcap->addr, &data[8], sizeof(cdcap->addr)); 380 break; 381 } 382 383 sc->sc_cur_xs = NULL; 384 scsipi_done(xs); 385 386 return 1; 387 } 388 389 static int 390 di_intr(void *priv) 391 { 392 struct di_softc *sc = priv; 393 uint32_t sr, cvr; 394 int ret = 0; 395 396 sr = RD4(sc, DISR); 397 cvr = RD4(sc, DICVR); 398 399 if ((sr & DISR_DEINT) != 0) { 400 ret |= di_transfer_error(sc, sc->sc_cur_xs); 401 } else if ((sr & DISR_TCINT) != 0) { 402 ret |= di_transfer_complete(sc, sc->sc_cur_xs); 403 } 404 405 if ((cvr & DICVR_CVRINT) != 0) { 406 DPRINTF(sc->sc_dev, "drive %s\n", 407 (cvr & DICVR_CVR) == 0 ? "closed" : "opened"); 408 ret |= 1; 409 } 410 411 WR4(sc, DISR, sr); 412 WR4(sc, DICVR, cvr); 413 414 return ret; 415 } 416 417 static void 418 di_timeout(void *priv) 419 { 420 struct di_softc *sc = priv; 421 int s; 422 423 s = splbio(); 424 if (sc->sc_cur_xs != NULL) { 425 struct scsipi_xfer *xs = sc->sc_cur_xs; 426 427 DPRINTF(sc->sc_dev, "command %#x timeout, DISR = %#x\n", 428 xs->cmd->opcode, RD4(sc, DISR)); 429 xs->error = XS_TIMEOUT; 430 scsipi_done(xs); 431 432 sc->sc_cur_xs = NULL; 433 } 434 splx(s); 435 } 436 437 static void 438 di_idle(void *priv) 439 { 440 struct di_softc *sc = priv; 441 442 if ((RD4(sc, DICVR) & DICVR_CVR) != 0) { 443 /* Cover is opened, nothing to do. */ 444 return; 445 } 446 447 di_reset(sc, false); 448 } 449 450 static void 451 di_start_request(struct di_softc *sc, struct scsipi_xfer *xs) 452 { 453 KASSERT(sc->sc_cur_xs == NULL); 454 sc->sc_cur_xs = xs; 455 if (xs->timeout != 0) { 456 callout_schedule(&sc->sc_timeout, mstohz(xs->timeout) + 1); 457 } else { 458 DPRINTF(sc->sc_dev, "WARNING: xfer with no timeout!\n"); 459 callout_schedule(&sc->sc_timeout, mstohz(15000)); 460 } 461 } 462 463 static void 464 di_init_regs(struct di_softc *sc) 465 { 466 WR4(sc, DISR, DISR_BRKINT | 467 DISR_TCINT | DISR_TCINTMASK | 468 DISR_DEINT | DISR_DEINTMASK); 469 WR4(sc, DICVR, DICVR_CVRINT | DICVR_CVRINTMASK); 470 } 471 472 static void 473 di_reset(struct di_softc *sc, bool spinup) 474 { 475 uint32_t val; 476 int s; 477 478 DPRINTF(sc->sc_dev, "reset spinup=%d\n", spinup); 479 480 s = splhigh(); 481 482 if (spinup) { 483 out32(HW_GPIOB_OUT, in32(HW_GPIOB_OUT) & ~__BIT(GPIO_DI_SPIN)); 484 } else { 485 out32(HW_GPIOB_OUT, in32(HW_GPIOB_OUT) | __BIT(GPIO_DI_SPIN)); 486 } 487 488 val = in32(HW_RESETS); 489 out32(HW_RESETS, val & ~RSTB_IODI); 490 delay(12); 491 out32(HW_RESETS, val | RSTB_IODI); 492 493 WR4(sc, DISR, DISR_BRKINT | 494 DISR_TCINT | DISR_TCINTMASK | 495 DISR_DEINT | DISR_DEINTMASK); 496 WR4(sc, DICVR, DICVR_CVRINT | DICVR_CVRINTMASK); 497 498 splx(s); 499 } 500 501 static void 502 di_stop_motor(struct di_softc *sc, struct scsipi_xfer *xs, bool eject) 503 { 504 uint32_t cmdflags = 0; 505 int s; 506 507 if (eject) { 508 cmdflags |= 1 << 17; 509 } 510 511 s = splbio(); 512 WR4(sc, DICMDBUF0, DI_CMD_STOP_MOTOR | cmdflags); 513 WR4(sc, DICMDBUF1, 0); 514 WR4(sc, DICMDBUF2, 0); 515 WR4(sc, DILENGTH, 4); 516 WR4(sc, DICR, DICR_TSTART); 517 di_start_request(sc, xs); 518 splx(s); 519 } 520 521 static void 522 di_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *arg) 523 { 524 struct di_softc *sc = device_private(chan->chan_adapter->adapt_dev); 525 struct scsipi_xfer *xs; 526 struct scsipi_generic *cmd; 527 struct scsipi_start_stop *ss; 528 struct scsi_prevent_allow_medium_removal *pamr; 529 uint32_t blkno; 530 int s; 531 532 if (req != ADAPTER_REQ_RUN_XFER) { 533 return; 534 } 535 536 callout_stop(&sc->sc_idle); 537 538 KASSERT(sc->sc_cur_xs == NULL); 539 540 xs = arg; 541 cmd = xs->cmd; 542 543 switch (cmd->opcode) { 544 case INQUIRY: 545 bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 546 0, sizeof(struct di_response_inquiry), 547 BUS_DMASYNC_PREREAD); 548 549 s = splbio(); 550 WR4(sc, DICMDBUF0, DI_CMD_INQUIRY); 551 WR4(sc, DICMDBUF1, 0); 552 WR4(sc, DILENGTH, sizeof(struct di_response_inquiry)); 553 WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 554 WR4(sc, DICR, DICR_TSTART | DICR_DMA); 555 di_start_request(sc, xs); 556 splx(s); 557 break; 558 559 case SCSI_TEST_UNIT_READY: 560 case SCSI_REQUEST_SENSE: 561 s = splbio(); 562 WR4(sc, DICMDBUF0, DI_CMD_REQUEST_ERROR); 563 WR4(sc, DICMDBUF1, 0); 564 WR4(sc, DICMDBUF2, 0); 565 WR4(sc, DILENGTH, 4); 566 WR4(sc, DICR, DICR_TSTART); 567 di_start_request(sc, xs); 568 splx(s); 569 break; 570 571 case SCSI_READ_6_COMMAND: 572 case READ_10: 573 if (cmd->opcode == SCSI_READ_6_COMMAND) { 574 blkno = _3btol(((struct scsi_rw_6 *)cmd)->addr); 575 } else { 576 KASSERT(cmd->opcode == READ_10); 577 blkno = _4btol(((struct scsipi_rw_10 *)cmd)->addr); 578 } 579 580 if (xs->datalen == 0) { 581 xs->error = XS_DRIVER_STUFFUP; 582 scsipi_done(xs); 583 break; 584 } 585 586 bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 587 0, xs->datalen, BUS_DMASYNC_PREREAD); 588 589 s = splbio(); 590 WR4(sc, DICMDBUF0, DI_CMD_READ_DVD); 591 WR4(sc, DICMDBUF1, blkno); 592 WR4(sc, DICMDBUF2, howmany(xs->datalen, DVDBLOCKSIZE)); 593 WR4(sc, DILENGTH, roundup(xs->datalen, DVDBLOCKSIZE)); 594 WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 595 WR4(sc, DICR, DICR_TSTART | DICR_DMA); 596 di_start_request(sc, xs); 597 splx(s); 598 break; 599 600 case GPCMD_READ_DVD_STRUCTURE: 601 if (xs->datalen == 0) { 602 DPRINTF(sc->sc_dev, "zero datalen\n"); 603 xs->error = XS_DRIVER_STUFFUP; 604 scsipi_done(xs); 605 break; 606 } 607 608 bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 609 0, xs->datalen, BUS_DMASYNC_PREREAD); 610 611 s = splbio(); 612 WR4(sc, DICMDBUF0, DI_CMD_READ_DVD_STRUCT(cmd->bytes[6])); 613 WR4(sc, DICMDBUF1, 0); 614 WR4(sc, DICMDBUF2, 0); 615 WR4(sc, DILENGTH, roundup(xs->datalen, DVDBLOCKSIZE)); 616 WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 617 WR4(sc, DICR, DICR_TSTART | DICR_DMA); 618 di_start_request(sc, xs); 619 splx(s); 620 break; 621 622 case GPCMD_REPORT_KEY: 623 if (xs->datalen == 0) { 624 DPRINTF(sc->sc_dev, "zero datalen\n"); 625 xs->error = XS_DRIVER_STUFFUP; 626 scsipi_done(xs); 627 break; 628 } 629 630 bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 631 0, xs->datalen, BUS_DMASYNC_PREREAD); 632 633 s = splbio(); 634 WR4(sc, DICMDBUF0, DI_CMD_REPORT_KEY(cmd->bytes[9] >> 2)); 635 WR4(sc, DICMDBUF1, _4btol(&cmd->bytes[1])); 636 WR4(sc, DICMDBUF2, 0); 637 WR4(sc, DILENGTH, roundup(xs->datalen, 0x20)); 638 WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 639 WR4(sc, DICR, DICR_TSTART | DICR_DMA); 640 di_start_request(sc, xs); 641 splx(s); 642 break; 643 644 case READ_CD_CAPACITY: 645 bus_dmamap_sync(sc->sc_dmat, sc->sc_dma_map, 646 0, DVDBLOCKSIZE, BUS_DMASYNC_PREREAD); 647 648 s = splbio(); 649 WR4(sc, DICMDBUF0, DI_CMD_READ_DVD_STRUCT(DVD_STRUCT_PHYSICAL)); 650 WR4(sc, DICMDBUF1, 0); 651 WR4(sc, DICMDBUF2, 0); 652 WR4(sc, DILENGTH, DVDBLOCKSIZE); 653 WR4(sc, DIMAR, sc->sc_dma_segs[0].ds_addr); 654 WR4(sc, DICR, DICR_TSTART | DICR_DMA); 655 di_start_request(sc, xs); 656 splx(s); 657 break; 658 659 case GET_CONFIGURATION: 660 memset(xs->data, 0, sizeof(struct scsipi_get_conf_data)); 661 xs->resid = 0; 662 scsipi_done(xs); 663 break; 664 665 case READ_TOC: 666 memset(xs->data, 0, sizeof(struct scsipi_toc_header)); 667 xs->resid = 0; 668 scsipi_done(xs); 669 break; 670 671 case READ_TRACKINFO: 672 case READ_DISCINFO: 673 di_sense(xs, SKEY_ILLEGAL_REQUEST, 0, 0); 674 scsipi_done(xs); 675 break; 676 677 case START_STOP: 678 ss = (struct scsipi_start_stop *)cmd; 679 if (ss->how == SSS_START) { 680 di_reset(sc, true); 681 scsipi_done(xs); 682 } else { 683 di_stop_motor(sc, xs, (ss->how & SSS_LOEJ) != 0); 684 } 685 break; 686 687 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL: 688 pamr = (struct scsi_prevent_allow_medium_removal *)cmd; 689 sc->sc_pamr = pamr->how; 690 scsipi_done(xs); 691 break; 692 693 default: 694 DPRINTF(sc->sc_dev, "unsupported opcode %#x\n", cmd->opcode); 695 scsipi_done(xs); 696 } 697 698 if (!sc->sc_pamr) { 699 callout_schedule(&sc->sc_idle, mstohz(DI_IDLE_TIMEOUT_MS)); 700 } 701 } 702