static int mcdopen(dev_t dev, int flags, int fmt, struct thread *td) { struct mcd_softc *sc; int r,retry; sc = (struct mcd_softc *)dev->si_drv1; /* not initialized*/ if (!(sc->data.flags & MCDINIT)) return (ENXIO); /* invalidated in the meantime? mark all open part's invalid */ if (!(sc->data.flags & MCDVALID) && sc->data.openflags) return (ENXIO); if (mcd_getstat(sc, 1) == -1) return (EIO); if ( (sc->data.status & (MCDDSKCHNG|MCDDOOROPEN)) || !(sc->data.status & MCDDSKIN)) for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) { (void) tsleep((caddr_t)sc, PSOCK | PCATCH, "mcdsn1", hz/WAIT_FRAC); if ((r = mcd_getstat(sc, 1)) == -1) return (EIO); if (r != -2) break; } if (sc->data.status & MCDDOOROPEN) { device_printf(sc->dev, "door is open\n"); return (ENXIO); } if (!(sc->data.status & MCDDSKIN)) { device_printf(sc->dev, "no CD inside\n"); return (ENXIO); } if (sc->data.status & MCDDSKCHNG) { device_printf(sc->dev, "CD not sensed\n"); return (ENXIO); } if (mcdsize(dev) < 0) { device_printf(sc->dev, "failed to get disk size\n"); return (ENXIO); } dev->si_bsize_phys = sc->data.blksize; sc->data.openflags = 1; sc->data.partflags |= MCDREADRAW; sc->data.flags |= MCDVALID; (void) mcd_lock_door(sc, MCD_LK_LOCK); if (!(sc->data.flags & MCDVALID)) return (ENXIO); return mcd_read_toc(sc); }
static int mcd_setmode(struct mcd_softc *sc, int mode) { int retry, st; if (sc->data.curr_mode == mode) return (0); if (sc->data.debug) device_printf(sc->dev, "setting mode to %d\n", mode); for(retry=0; retry<MCD_RETRYS; retry++) { sc->data.curr_mode = MCD_MD_UNKNOWN; MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDSETMODE); MCD_WRITE(sc, MCD_REG_COMMAND, mode); if ((st = mcd_getstat(sc, 0)) >= 0) { sc->data.curr_mode = mode; return (0); } if (st == -2) { device_printf(sc->dev, "media changed\n"); break; } } return (-1); }
static int mcd_eject(struct mcd_softc *sc) { int r; if (mcd_getstat(sc, 1) == -1) /* detect disk change too */ return (EIO); if (sc->data.status & MCDDOOROPEN) return (0); if ((r = mcd_stop(sc)) == EIO) return (r); MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDEJECTDISK); if (mcd_getstat(sc, 0) == -1) return (EIO); return (0); }
static int mcd_inject(struct mcd_softc *sc) { if (mcd_getstat(sc, 1) == -1) /* detect disk change too */ return (EIO); if (sc->data.status & MCDDOOROPEN) return mcd_close_tray(sc); return (0); }
static int mcd_lock_door(struct mcd_softc *sc, int lock) { MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDLOCKDRV); MCD_WRITE(sc, MCD_REG_COMMAND, lock); if (mcd_getstat(sc, 0) == -1) return (EIO); return (0); }
static int mcd_close_tray(struct mcd_softc *sc) { int retry, r; if (mcd_getstat(sc, 1) == -1) return (EIO); if (sc->data.status & MCDDOOROPEN) { MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDCLOSETRAY); for (retry = 0; retry < CLOSE_TRAY_SECS * WAIT_FRAC; retry++) { if (MCD_READ(sc, MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) (void) tsleep((caddr_t)sc, PSOCK | PCATCH, "mcdcls", hz/WAIT_FRAC); else { if ((r = mcd_getstat(sc, 0)) == -1) return (EIO); return (0); } } return (ENXIO); } return (0); }
static int mcd_send(struct mcd_softc *sc, int cmd,int nretrys) { int i,k=0; /*MCD_TRACE("mcd_send: command = 0x%02x\n",cmd,0,0,0);*/ for (i=0; i<nretrys; i++) { MCD_WRITE(sc, MCD_REG_COMMAND, cmd); if ((k=mcd_getstat(sc, 0)) != -1) break; } if (k == -2) { device_printf(sc->dev, "media changed\n"); return (-1); } if (i == nretrys) { device_printf(sc->dev, "mcd_send retry cnt exceeded\n"); return (-1); } /*MCD_TRACE("mcd_send: done\n",0,0,0,0);*/ return (0); }
static int mcd_play(struct mcd_softc *sc, struct mcd_read2 *pb) { int retry, st = -1, status; sc->data.lastpb = *pb; for(retry=0; retry<MCD_RETRYS; retry++) { critical_enter(); MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDSINGLESPEEDREAD); MCD_WRITE(sc, MCD_REG_COMMAND, pb->start_msf[0]); MCD_WRITE(sc, MCD_REG_COMMAND, pb->start_msf[1]); MCD_WRITE(sc, MCD_REG_COMMAND, pb->start_msf[2]); MCD_WRITE(sc, MCD_REG_COMMAND, pb->end_msf[0]); MCD_WRITE(sc, MCD_REG_COMMAND, pb->end_msf[1]); MCD_WRITE(sc, MCD_REG_COMMAND, pb->end_msf[2]); critical_exit(); status=mcd_getstat(sc, 0); if (status == -1) continue; else if (status != -2) st = 0; break; } if (status == -2) { device_printf(sc->dev, "media changed\n"); return (ENXIO); } if (sc->data.debug) device_printf(sc->dev, "mcd_play retry=%d, status=0x%02x\n", retry, status); if (st < 0) return (ENXIO); sc->data.audio_status = CD_AS_PLAY_IN_PROGRESS; return (0); }
static void mcd_doread(struct mcd_softc *sc, int state, struct mcd_mbx *mbxin) { struct mcd_mbx *mbx; struct bio *bp; int rm, i, k; struct mcd_read2 rbuf; int blknum; caddr_t addr; mbx = (state!=MCD_S_BEGIN) ? sc->ch_mbxsave : mbxin; bp = mbx->bp; loop: switch (state) { case MCD_S_BEGIN: mbx = sc->ch_mbxsave = mbxin; case MCD_S_BEGIN1: retry_status: /* get status */ MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDGETSTAT); mbx->count = RDELAY_WAITSTAT; sc->ch_state = MCD_S_WAITSTAT; sc->ch = timeout(mcd_timeout, (caddr_t)sc, hz/100); /* XXX */ return; case MCD_S_WAITSTAT: sc->ch_state = MCD_S_WAITSTAT; untimeout(mcd_timeout,(caddr_t)sc, sc->ch); if (mbx->count-- >= 0) { if (MCD_READ(sc, MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) { sc->ch_state = MCD_S_WAITSTAT; timeout(mcd_timeout, (caddr_t)sc, hz/100); /* XXX */ return; } sc->data.status = MCD_READ(sc, MCD_REG_STATUS) & 0xFF; if (sc->data.status & MCD_ST_CMDCHECK) goto retry_status; if (mcd_setflags(sc) < 0) goto changed; MCD_TRACE("got WAITSTAT delay=%d\n", RDELAY_WAITSTAT-mbx->count); /* reject, if audio active */ if (sc->data.status & MCDAUDIOBSY) { device_printf(sc->dev, "audio is active\n"); goto readerr; } retry_mode: /* to check for raw/cooked mode */ if (sc->data.flags & MCDREADRAW) { rm = MCD_MD_RAW; mbx->sz = MCDRBLK; } else { rm = MCD_MD_COOKED; mbx->sz = sc->data.blksize; } if (rm == sc->data.curr_mode) goto modedone; mbx->count = RDELAY_WAITMODE; sc->data.curr_mode = MCD_MD_UNKNOWN; mbx->mode = rm; MCD_WRITE(sc, MCD_REG_COMMAND, MCD_CMDSETMODE); MCD_WRITE(sc, MCD_REG_COMMAND, rm); sc->ch_state = MCD_S_WAITMODE; sc->ch = timeout(mcd_timeout, (caddr_t)sc, hz/100); /* XXX */ return; } else { device_printf(sc->dev, "timeout getstatus\n"); goto readerr; } case MCD_S_WAITMODE: sc->ch_state = MCD_S_WAITMODE; untimeout(mcd_timeout, (caddr_t)sc, sc->ch); if (mbx->count-- < 0) { device_printf(sc->dev, "timeout set mode\n"); goto readerr; } if (MCD_READ(sc, MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) { sc->ch_state = MCD_S_WAITMODE; sc->ch = timeout(mcd_timeout, (caddr_t)sc, hz/100); return; } sc->data.status = MCD_READ(sc, MCD_REG_STATUS) & 0xFF; if (sc->data.status & MCD_ST_CMDCHECK) { sc->data.curr_mode = MCD_MD_UNKNOWN; goto retry_mode; } if (mcd_setflags(sc) < 0) goto changed; sc->data.curr_mode = mbx->mode; MCD_TRACE("got WAITMODE delay=%d\n", RDELAY_WAITMODE-mbx->count); modedone: /* for first block */ mbx->nblk = (bp->bio_bcount + (mbx->sz-1)) / mbx->sz; mbx->skip = 0; nextblock: blknum = (bp->bio_blkno / (mbx->sz/DEV_BSIZE)) + mbx->skip/mbx->sz; MCD_TRACE("mcd_doread: read blknum=%d for bp=%p\n", blknum, bp); /* build parameter block */ hsg2msf(blknum,rbuf.start_msf); retry_read: /* send the read command */ critical_enter(); MCD_WRITE(sc, MCD_REG_COMMAND, sc->data.read_command); MCD_WRITE(sc, MCD_REG_COMMAND, rbuf.start_msf[0]); MCD_WRITE(sc, MCD_REG_COMMAND, rbuf.start_msf[1]); MCD_WRITE(sc, MCD_REG_COMMAND, rbuf.start_msf[2]); MCD_WRITE(sc, MCD_REG_COMMAND, 0); MCD_WRITE(sc, MCD_REG_COMMAND, 0); MCD_WRITE(sc, MCD_REG_COMMAND, 1); critical_exit(); /* Spin briefly (<= 2ms) to avoid missing next block */ for (i = 0; i < 20; i++) { k = MCD_READ(sc, MCD_FLAGS); if (!(k & MFL_DATA_NOT_AVAIL)) goto got_it; DELAY(100); } mbx->count = RDELAY_WAITREAD; sc->ch_state = MCD_S_WAITREAD; sc->ch = timeout(mcd_timeout, (caddr_t)sc, hz/100); /* XXX */ return; case MCD_S_WAITREAD: sc->ch_state = MCD_S_WAITREAD; untimeout(mcd_timeout, (caddr_t)sc, sc->ch); if (mbx->count-- > 0) { k = MCD_READ(sc, MCD_FLAGS); if (!(k & MFL_DATA_NOT_AVAIL)) { /* XXX */ MCD_TRACE("got data delay=%d\n", RDELAY_WAITREAD-mbx->count); got_it: /* data is ready */ addr = bp->bio_data + mbx->skip; MCD_WRITE(sc, MCD_REG_CTL2,0x04); /* XXX */ for (i=0; i<mbx->sz; i++) *addr++ = MCD_READ(sc, MCD_REG_RDATA); MCD_WRITE(sc, MCD_REG_CTL2,0x0c); /* XXX */ k = MCD_READ(sc, MCD_FLAGS); /* If we still have some junk, read it too */ if (!(k & MFL_DATA_NOT_AVAIL)) { MCD_WRITE(sc, MCD_REG_CTL2, 0x04); /* XXX */ (void)MCD_READ(sc, MCD_REG_RDATA); (void)MCD_READ(sc, MCD_REG_RDATA); MCD_WRITE(sc, MCD_REG_CTL2, 0x0c); /* XXX */ } if (--mbx->nblk > 0) { mbx->skip += mbx->sz; goto nextblock; } /* return buffer */ bp->bio_resid = 0; biodone(bp); sc->data.flags &= ~(MCDMBXBSY|MCDREADRAW); mcd_start(sc); return; } if (!(k & MFL_STATUS_NOT_AVAIL)) { sc->data.status = MCD_READ(sc, MCD_REG_STATUS) & 0xFF; if (sc->data.status & MCD_ST_CMDCHECK) goto retry_read; if (mcd_setflags(sc) < 0) goto changed; } sc->ch_state = MCD_S_WAITREAD; sc->ch = timeout(mcd_timeout, (caddr_t)sc, hz/100); /* XXX */ return; } else { device_printf(sc->dev, "timeout read data\n"); goto readerr; } } readerr: if (mbx->retry-- > 0) { device_printf(sc->dev, "retrying\n"); state = MCD_S_BEGIN1; goto loop; } harderr: /* invalidate the buffer */ bp->bio_flags |= BIO_ERROR; bp->bio_resid = bp->bio_bcount; biodone(bp); sc->data.flags &= ~(MCDMBXBSY|MCDREADRAW); mcd_start(sc); return; changed: device_printf(sc->dev, "media changed\n"); goto harderr; #ifdef NOTDEF device_printf(sc->dev, "unit timeout, resetting\n"); MCD_WRITE(sc, MCD_REG_RESET, MCD_CMDRESET); DELAY(300000); (void)mcd_getstat(sc, 1); (void)mcd_getstat(sc, 1); /*sc->data.status &= ~MCDDSKCHNG; */ sc->data.debug = 1; /* preventive set debug mode */ #endif }
static int mcdioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { struct mcd_softc *sc; int retry,r; sc = (struct mcd_softc *)dev->si_drv1; if (mcd_getstat(sc, 1) == -1) /* detect disk change too */ return (EIO); MCD_TRACE("ioctl called 0x%lx\n", cmd); switch (cmd) { case CDIOCSETPATCH: case CDIOCGETVOL: case CDIOCSETVOL: case CDIOCSETMONO: case CDIOCSETSTERIO: case CDIOCSETMUTE: case CDIOCSETLEFT: case CDIOCSETRIGHT: return (EINVAL); case CDIOCEJECT: return mcd_eject(sc); case CDIOCSETDEBUG: sc->data.debug = 1; return (0); case CDIOCCLRDEBUG: sc->data.debug = 0; return (0); case CDIOCRESET: return mcd_hard_reset(sc); case CDIOCALLOW: return mcd_lock_door(sc, MCD_LK_UNLOCK); case CDIOCPREVENT: return mcd_lock_door(sc, MCD_LK_LOCK); case CDIOCCLOSE: return mcd_inject(sc); } if (!(sc->data.flags & MCDVALID)) { if ( (sc->data.status & (MCDDSKCHNG|MCDDOOROPEN)) || !(sc->data.status & MCDDSKIN)) for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) { (void) tsleep((caddr_t)sc, PSOCK | PCATCH, "mcdsn2", hz/WAIT_FRAC); if ((r = mcd_getstat(sc, 1)) == -1) return (EIO); if (r != -2) break; } if ( (sc->data.status & (MCDDOOROPEN|MCDDSKCHNG)) || !(sc->data.status & MCDDSKIN) || mcdsize(dev) < 0 ) return (ENXIO); sc->data.flags |= MCDVALID; sc->data.partflags |= MCDREADRAW; (void) mcd_lock_door(sc, MCD_LK_LOCK); if (!(sc->data.flags & MCDVALID)) return (ENXIO); } switch (cmd) { case DIOCGMEDIASIZE: *(off_t *)addr = (off_t)sc->data.disksize * sc->data.blksize; return (0); break; case DIOCGSECTORSIZE: *(u_int *)addr = sc->data.blksize; return (0); break; case CDIOCPLAYTRACKS: return mcd_playtracks(sc, (struct ioc_play_track *) addr); case CDIOCPLAYBLOCKS: return mcd_playblocks(sc, (struct ioc_play_blocks *) addr); case CDIOCPLAYMSF: return mcd_playmsf(sc, (struct ioc_play_msf *) addr); case CDIOCREADSUBCHANNEL: return mcd_subchan(sc, (struct ioc_read_subchannel *) addr); case CDIOREADTOCHEADER: return mcd_toc_header(sc, (struct ioc_toc_header *) addr); case CDIOREADTOCENTRYS: return mcd_toc_entrys(sc, (struct ioc_read_toc_entry *) addr); case CDIOCRESUME: return mcd_resume(sc); case CDIOCPAUSE: return mcd_pause(sc); case CDIOCSTART: if (mcd_setmode(sc, MCD_MD_COOKED) != 0) return (EIO); return (0); case CDIOCSTOP: return mcd_stop(sc); default: return (ENOTTY); } /*NOTREACHED*/ }
/* * State machine to process read requests. * Initialize with MCD_S_BEGIN: calculate sizes, and set mode * MCD_S_WAITMODE: waits for status reply from set mode, set read command * MCD_S_WAITREAD: wait for read ready, read data. */ int mcdintr(void *arg) { struct mcd_softc *sc = arg; struct mcd_mbx *mbx = &sc->mbx; struct buf *bp = mbx->bp; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; int i; u_char x; bcd_t msf[3]; switch (mbx->state) { case MCD_S_IDLE: return 0; case MCD_S_BEGIN: tryagain: if (mbx->mode == sc->lastmode) goto firstblock; sc->lastmode = MCD_MD_UNKNOWN; bus_space_write_1(iot, ioh, MCD_COMMAND, MCD_CMDSETMODE); bus_space_write_1(iot, ioh, MCD_COMMAND, mbx->mode); mbx->count = RDELAY_WAITMODE; mbx->state = MCD_S_WAITMODE; case MCD_S_WAITMODE: callout_stop(&sc->sc_pintr_ch); for (i = 20; i; i--) { x = bus_space_read_1(iot, ioh, MCD_XFER); if ((x & MCD_XF_STATUSUNAVAIL) == 0) break; delay(50); } if (i == 0) goto hold; sc->status = bus_space_read_1(iot, ioh, MCD_STATUS); mcd_setflags(sc); if ((sc->flags & MCDF_LOADED) == 0) goto changed; MCD_TRACE("doread: got WAITMODE delay=%d\n", RDELAY_WAITMODE - mbx->count); sc->lastmode = mbx->mode; firstblock: MCD_TRACE("doread: read blkno=%d for bp=0x%p\n", (int) mbx->blkno, bp); /* Build parameter block. */ hsg2msf(mbx->blkno, msf); /* Send the read command. */ bus_space_write_1(iot, ioh, MCD_COMMAND, sc->readcmd); bus_space_write_1(iot, ioh, MCD_COMMAND, msf[0]); bus_space_write_1(iot, ioh, MCD_COMMAND, msf[1]); bus_space_write_1(iot, ioh, MCD_COMMAND, msf[2]); bus_space_write_1(iot, ioh, MCD_COMMAND, 0); bus_space_write_1(iot, ioh, MCD_COMMAND, 0); bus_space_write_1(iot, ioh, MCD_COMMAND, mbx->nblk); mbx->count = RDELAY_WAITREAD; mbx->state = MCD_S_WAITREAD; case MCD_S_WAITREAD: callout_stop(&sc->sc_pintr_ch); nextblock: loop: for (i = 20; i; i--) { x = bus_space_read_1(iot, ioh, MCD_XFER); if ((x & MCD_XF_DATAUNAVAIL) == 0) goto gotblock; if ((x & MCD_XF_STATUSUNAVAIL) == 0) break; delay(50); } if (i == 0) goto hold; sc->status = bus_space_read_1(iot, ioh, MCD_STATUS); mcd_setflags(sc); if ((sc->flags & MCDF_LOADED) == 0) goto changed; #if 0 printf("%s: got status byte %02x during read\n", device_xname(sc->sc_dev), (u_int)sc->status); #endif goto loop; gotblock: MCD_TRACE("doread: got data delay=%d\n", RDELAY_WAITREAD - mbx->count); /* Data is ready. */ bus_space_write_1(iot, ioh, MCD_CTL2, 0x04); /* XXX */ bus_space_read_multi_1(iot, ioh, MCD_RDATA, (char *)bp->b_data + mbx->skip, mbx->sz); bus_space_write_1(iot, ioh, MCD_CTL2, 0x0c); /* XXX */ mbx->blkno += 1; mbx->skip += mbx->sz; if (--mbx->nblk > 0) goto nextblock; mbx->state = MCD_S_IDLE; /* Return buffer. */ bp->b_resid = 0; disk_unbusy(&sc->sc_dk, bp->b_bcount, (bp->b_flags & B_READ)); biodone(bp); mcdstart(sc); return 1; hold: if (mbx->count-- < 0) { printf("%s: timeout in state %d", device_xname(sc->sc_dev), mbx->state); goto readerr; } #if 0 printf("%s: sleep in state %d\n", device_xname(sc->sc_dev), mbx->state); #endif callout_reset(&sc->sc_pintr_ch, hz / 100, mcd_pseudointr, sc); return -1; } readerr: if (mbx->retry-- > 0) { printf("; retrying\n"); goto tryagain; } else printf("; giving up\n"); changed: /* Invalidate the buffer. */ bp->b_error = EIO; bp->b_resid = bp->b_bcount - mbx->skip; disk_unbusy(&sc->sc_dk, (bp->b_bcount - bp->b_resid), (bp->b_flags & B_READ)); biodone(bp); mcdstart(sc); return -1; #ifdef notyet printf("%s: unit timeout; resetting\n", device_xname(sc->sc_dev)); bus_space_write_1(iot, ioh, MCD_RESET, MCD_CMDRESET); delay(300000); (void) mcd_getstat(sc, 1); (void) mcd_getstat(sc, 1); /*sc->status &= ~MCD_ST_DSKCHNG; */ sc->debug = 1; /* preventive set debug mode */ #endif }