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