di.c revision 1.1
1/* $NetBSD: di.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2025 Jared McNeill <jmcneill@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 96struct di_softc; 97 98static int di_match(device_t, cfdata_t, void *); 99static void di_attach(device_t, device_t, void *); 100 101static bool di_shutdown(device_t, int); 102 103static int di_intr(void *); 104static void di_timeout(void *); 105static void di_idle(void *); 106 107static void di_request(struct scsipi_channel *, scsipi_adapter_req_t, 108 void *); 109static void di_init_regs(struct di_softc *); 110static void di_reset(struct di_softc *, bool); 111 112struct 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); 118CTASSERT(sizeof(struct di_response_inquiry) == 0x20); 119 120struct 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 145CFATTACH_DECL_NEW(di, sizeof(struct di_softc), 146 di_match, di_attach, NULL, NULL); 147 148static int 149di_match(device_t parent, cfdata_t cf, void *aux) 150{ 151 return !wiiu_native; 152} 153 154static void 155di_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 234static bool 235di_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 244static void 245di_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 256static void 257di_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 278static int 279di_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 295static int 296di_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 389static int 390di_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 417static void 418di_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 437static void 438di_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 450static void 451di_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 463static void 464di_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 472static void 473di_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 501static void 502di_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 521static void 522di_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