/* $NetBSD: gdrom.c,v 1.1 2016/12/29 11:49:05 tsutsui Exp $ */ /*- * Copyright (c) 2001 Marcus Comstedt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Marcus Comstedt. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * WIP gdrom driver using MI ATA/ATAPI drivers. * * XXX: Still not functional because GD-ROM driver does not generate * XXX: interrupts after ATAPI command packet xfers and such quirks * XXX: need to be handled in MI scsipi layer. */ #include /* RCS ID & Copyright macro defns */ __KERNEL_RCSID(0, "$NetBSD: gdrom.c,v 1.1 2016/12/29 11:49:05 tsutsui Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ioconf.h" struct gdrom_softc { device_t sc_dev; /* generic device info */ struct disk sc_dk; /* generic disk info */ struct bufq_state *sc_bufq; /* device buffer queue */ struct buf curbuf; /* state of current I/O operation */ kmutex_t sc_lock; struct scsipi_periph *sc_periph; bool is_open; bool is_busy; bool is_active; int openpart_start; /* start sector of currently open partition */ int cmd_active; void *cmd_result_buf; /* where to store result data (16 bit aligned) */ int cmd_result_size; /* number of bytes allocated for buf */ int cmd_actual; /* number of bytes actually read */ int cmd_cond; /* resulting condition of command */ }; struct gd_toc { unsigned int entry[99]; unsigned int first, last; unsigned int leadout; }; static int gdrommatch(device_t, cfdata_t, void *); static void gdromattach(device_t, device_t, void *); #if 0 static int gdrom_command_sense(struct gdrom_softc *, void *, void *, unsigned int, int *); #endif static int gdrom_read_toc(struct gdrom_softc *, struct gd_toc *); static int gdrom_read_sectors(struct gdrom_softc *, struct buf *); static int gdrom_mount_disk(struct gdrom_softc *); static void gdrom_start(struct scsipi_periph *); static void gdrom_done(struct scsipi_xfer *, int); static const struct scsipi_inquiry_pattern gdrom_patterns[] = { {T_DIRECT, T_FIXED, "", "DCR-MOD", ""}, }; static dev_type_open(gdromopen); static dev_type_close(gdromclose); static dev_type_read(gdromread); static dev_type_write(gdromwrite); static dev_type_ioctl(gdromioctl); static dev_type_strategy(gdromstrategy); const struct bdevsw gdrom_bdevsw = { .d_open = gdromopen, .d_close = gdromclose, .d_strategy = gdromstrategy, .d_ioctl = gdromioctl, .d_dump = nodump, .d_psize = nosize, .d_discard = nodiscard, .d_flag = D_DISK }; const struct cdevsw gdrom_cdevsw = { .d_open = gdromopen, .d_close = gdromclose, .d_read = gdromread, .d_write = gdromwrite, .d_ioctl = gdromioctl, .d_stop = nostop, .d_tty = notty, .d_poll = nopoll, .d_mmap = nommap, .d_kqfilter = nokqfilter, .d_discard = nodiscard, .d_flag = D_DISK }; CFATTACH_DECL_NEW(gdrom, sizeof(struct gdrom_softc), gdrommatch, gdromattach, NULL, NULL); struct dkdriver gdromdkdriver = { .d_strategy = gdromstrategy }; static const struct scsipi_periphsw gdrom_switch = { NULL/*gdrom_interpret_sense*/, /* use our error handler first */ gdrom_start, /* we have a queue, which is started by this */ NULL, /* we do not have an async handler */ gdrom_done, /* deal with stats at interrupt time */ }; #define GDROMDEBUG #ifdef GDROMDEBUG int gdrom_debug = 0; /* patchable */ #define GDROM_DPRINTF(x) if (gdrom_debug) printf x #else #define GDROM_DPRINTF(x) /**/ #endif #define TOC_LBA(n) ((n) & 0xffffff00) #define TOC_ADR(n) ((n) & 0x0f) #define TOC_CTRL(n) (((n) & 0xf0) >> 4) #define TOC_TRACK(n) (((n) & 0x0000ff00) >> 8) #if 0 int gdrom_command_sense(struct gdrom_softc *sc, void *req, void *buf, unsigned int nbyt, int *resid) { /* * 76543210 76543210 * 0 0x13 - * 2 - bufsz(hi) * 4 bufsz(lo) - * 6 - - * 8 - - * 10 - - */ uint16_t sense_data[5]; uint8_t cmd[12]; int cond, sense_key, sense_specific; cond = scsipi_command(sc->sc_periph, req, 12, buf, nbyt, 4, 3000, NULL, XS_CTL_DATA_IN); if (resid != NULL) *resid = nbyt; if (cond < 0) { GDROM_DPRINTF(("GDROM: not ready (2:58)\n")); return EIO; } if ((cond & 1) == 0) { GDROM_DPRINTF(("GDROM: no sense. 0:0\n")); return 0; } memset(cmd, 0, sizeof(cmd)); cmd[0] = 0x13; cmd[4] = sizeof(sense_data); scsipi_command(sc->sc_periph, (void *)cmd, sizeof(cmd), (void *)sense_data, sizeof(sense_data), 4, 3000, NULL, XS_CTL_DATA_IN); sense_key = sense_data[1] & 0xf; sense_specific = sense_data[4]; if (sense_key == 11 && sense_specific == 0) { GDROM_DPRINTF(("GDROM: aborted (ignored). 0:0\n")); return 0; } GDROM_DPRINTF(("GDROM: SENSE %d:", sense_key)); GDROM_DPRINTF(("GDROM: %d\n", sense_specific)); return sense_key == 0 ? 0 : EIO; } #endif int gdrom_read_toc(struct gdrom_softc *sc, struct gd_toc *toc) { /* * 76543210 76543210 * 0 0x14 - * 2 - bufsz(hi) * 4 bufsz(lo) - * 6 - - * 8 - - * 10 - - */ uint8_t cmd[12]; GDROM_DPRINTF(("%s: called\n", __func__)); memset(cmd, 0, sizeof(cmd)); cmd[0] = 0x14; cmd[3] = sizeof(struct gd_toc) >> 8; cmd[4] = sizeof(struct gd_toc) & 0xff; return scsipi_command(sc->sc_periph, (void *)cmd, 12, (void *)toc, sizeof(struct gd_toc), 4, 3000, NULL, XS_CTL_DATA_IN); } int gdrom_read_sectors(struct gdrom_softc *sc, struct buf *bp) { /* * 76543210 76543210 * 0 0x30 datafmt * 2 sec(hi) sec(mid) * 4 sec(lo) - * 6 - - * 8 cnt(hi) cnt(mid) * 10 cnt(lo) - */ uint8_t cmd[12]; void *buf; int sector, cnt; int cond; GDROM_DPRINTF(("%s: called\n", __func__)); buf = bp->b_data; sector = bp->b_rawblkno; cnt = bp->b_bcount >> 11; memset(cmd, 0, sizeof(cmd)); cmd[0] = 0x30; cmd[1] = 0x20; cmd[2] = sector >> 16; cmd[3] = sector >> 8; cmd[4] = sector; cmd[8] = cnt >> 16; cmd[9] = cnt >> 8; cmd[10] = cnt; cond = scsipi_command(sc->sc_periph, (void *)cmd, 12, (void *)buf, bp->b_bcount, 4, 3000, bp, XS_CTL_DATA_IN); GDROM_DPRINTF(("%s: cond = %d\n", __func__, cond)); return cond; } int gdrom_mount_disk(struct gdrom_softc *sc) { /* * 76543210 76543210 * 0 0x70 - * 2 0x1f - * 4 - - * 6 - - * 8 - - * 10 - - */ uint8_t cmd[12]; int cond; GDROM_DPRINTF(("%s: called\n", __func__)); memset(cmd, 0, sizeof(cmd)); cmd[0] = 0x70; cmd[2] = 0x1f; cond = scsipi_command(sc->sc_periph, (void *)cmd, 12, NULL, 0, 4, 3000, NULL, 0); GDROM_DPRINTF(("%s: cond = %d\n", __func__, cond)); return cond; } int gdrommatch(device_t parent, cfdata_t cf, void *aux) { struct scsipibus_attach_args *sa = aux; int priority; (void)scsipi_inqmatch(&sa->sa_inqbuf, gdrom_patterns, __arraycount(gdrom_patterns), sizeof(gdrom_patterns[0]), &priority); if (priority > 0) { /* beat generic direct fixed device */ priority = 255; } return priority; } void gdromattach(device_t parent, device_t self, void *aux) { struct gdrom_softc *sc; struct scsipibus_attach_args *sa; struct scsipi_periph *periph; sc = device_private(self); sa = aux; periph = sa->sa_periph; sc->sc_dev = self; sc->sc_periph = periph; periph->periph_dev = sc->sc_dev; periph->periph_switch = &gdrom_switch; mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); bufq_alloc(&sc->sc_bufq, "disksort", BUFQ_SORT_RAWBLOCK); /* * Initialize and attach the disk structure. */ disk_init(&sc->sc_dk, device_xname(self), &gdromdkdriver); disk_attach(&sc->sc_dk); aprint_normal("\n"); aprint_naive("\n"); } int gdromopen(dev_t dev, int flags, int devtype, struct lwp *l) { struct gdrom_softc *sc; int s, error, unit, cnt; struct gd_toc toc; GDROM_DPRINTF(("%s: called\n", __func__)); unit = DISKUNIT(dev); sc = device_lookup_private(&gdrom_cd, unit); if (sc == NULL) return ENXIO; if (sc->is_open) return EBUSY; s = splbio(); while (sc->is_busy) tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0); sc->is_busy = true; splx(s); for (cnt = 0; cnt < 1; cnt++) if ((error = gdrom_mount_disk(sc)) == 0) break; if (error == 0) error = gdrom_read_toc(sc, &toc); sc->is_busy = false; wakeup(&sc->is_busy); if (error != 0) return error; sc->is_open = true; sc->openpart_start = 150; GDROM_DPRINTF(("%s: open OK\n", __func__)); return 0; } int gdromclose(dev_t dev, int flags, int devtype, struct lwp *l) { struct gdrom_softc *sc; int unit; GDROM_DPRINTF(("%s: called\n", __func__)); unit = DISKUNIT(dev); sc = device_lookup_private(&gdrom_cd, unit); sc->is_open = false; return 0; } void gdromstrategy(struct buf *bp) { struct gdrom_softc *sc; struct scsipi_periph *periph; int s, unit; GDROM_DPRINTF(("%s: called\n", __func__)); unit = DISKUNIT(bp->b_dev); sc = device_lookup_private(&gdrom_cd, unit); periph = sc->sc_periph; if (bp->b_bcount == 0) goto done; bp->b_rawblkno = bp->b_blkno / (2048 / DEV_BSIZE) + sc->openpart_start; GDROM_DPRINTF(("%s: read_sectors(%p, %lld, %d) [%d bytes]\n", __func__, bp->b_data, bp->b_rawblkno, bp->b_bcount >> 11, bp->b_bcount)); s = splbio(); bufq_put(sc->sc_bufq, bp); splx(s); if (!sc->is_active) gdrom_start(periph); return; done: bp->b_resid = bp->b_bcount; biodone(bp); } void gdrom_start(struct scsipi_periph *periph) { struct gdrom_softc *sc = device_private(periph->periph_dev); struct buf *bp; int error, s; sc->is_active = true; for (;;) { s = splbio(); bp = bufq_get(sc->sc_bufq); if (bp == NULL) { splx(s); break; } while (sc->is_busy) tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0); sc->is_busy = true; disk_busy(&sc->sc_dk); splx(s); error = gdrom_read_sectors(sc, bp); bp->b_error = error; if (error != 0) bp->b_resid = bp->b_bcount; sc->is_busy = false; wakeup(&sc->is_busy); s = splbio(); disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid, (bp->b_flags & B_READ) != 0); splx(s); biodone(bp); } sc->is_active = false; } void gdrom_done(struct scsipi_xfer *xs, int error) { GDROM_DPRINTF(("%s: called\n", __func__)); } int gdromioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) { struct gdrom_softc *sc; int unit, error; GDROM_DPRINTF(("%s: cmd %lx\n", __func__, cmd)); unit = DISKUNIT(dev); sc = device_lookup_private(&gdrom_cd, unit); switch (cmd) { case CDIOREADMSADDR: { int s, track, sessno = *(int *)addr; struct gd_toc toc; if (sessno != 0) return EINVAL; s = splbio(); while (sc->is_busy) tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0); sc->is_busy = true; splx(s); error = gdrom_read_toc(sc, &toc); sc->is_busy = false; wakeup(&sc->is_busy); if (error != 0) return error; #ifdef GDROMDEBUGTOC { /* Dump the GDROM TOC */ unsigned char *ptr = (unsigned char *)&toc; int i; printf("gdrom: TOC\n"); for(i = 0; i < sizeof(toc); ++i) { printf("%02x", *ptr++); if( i%32 == 31) printf("\n"); else if( i%4 == 3) printf(","); } printf("\n"); } #endif for (track = TOC_TRACK(toc.last); track >= TOC_TRACK(toc.first); --track) { if (track < 1 || track > 100) return ENXIO; if (TOC_CTRL(toc.entry[track - 1])) break; } #ifdef GDROMDEBUGTOC printf("gdrom: Using track %d, LBA %u\n", track, TOC_LBA(toc.entry[track - 1])); #endif *(int *)addr = htonl(TOC_LBA(toc.entry[track - 1])) - sc->openpart_start; return 0; } default: return ENOTTY; } #ifdef DIAGNOSTIC panic("gdromioctl: impossible"); #endif } int gdromread(dev_t dev, struct uio *uio, int flags) { GDROM_DPRINTF(("%s: called\n", __func__)); return physio(gdromstrategy, NULL, dev, B_READ, minphys, uio); } int gdromwrite(dev_t dev, struct uio *uio, int flags) { return EROFS; }