di.c revision 1.1 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