int mcd_getqchan(struct mcd_softc *sc, union mcd_qchninfo *q, int qchn) { struct mcd_mbox mbx; int error; if (qchn == CD_TRACK_INFO) { if ((error = mcd_setmode(sc, MCD_MD_TOC)) != 0) return error; } else { if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0) return error; } if (qchn == CD_MEDIA_CATALOG) { if ((error = mcd_setupc(sc, MCD_UPC_ENABLE)) != 0) return error; } else { if ((error = mcd_setupc(sc, MCD_UPC_DISABLE)) != 0) return error; } mbx.cmd.opcode = MCD_CMDGETQCHN; mbx.cmd.length = 0; mbx.res.length = sizeof(mbx.res.data.qchninfo); if ((error = mcd_send(sc, &mbx, 1)) != 0) return error; *q = mbx.res.data.qchninfo; return 0; }
static int mcd_playtracks(struct mcd_softc *sc, struct ioc_play_track *pt) { struct mcd_read2 pb; int a = pt->start_track; int z = pt->end_track; int rc, i; if ((rc = mcd_read_toc(sc)) != 0) return (rc); if (sc->data.debug) device_printf(sc->dev, "playtracks from %d:%d to %d:%d\n", a, pt->start_index, z, pt->end_index); if ( a < bcd2bin(sc->data.volinfo.trk_low) || a > bcd2bin(sc->data.volinfo.trk_high) || a > z || z < bcd2bin(sc->data.volinfo.trk_low) || z > bcd2bin(sc->data.volinfo.trk_high)) return (EINVAL); for (i = 0; i < 3; i++) { pb.start_msf[i] = sc->data.toc[a].hd_pos_msf[i]; pb.end_msf[i] = sc->data.toc[z+1].hd_pos_msf[i]; } if (mcd_setmode(sc, MCD_MD_COOKED) != 0) return (EIO); return mcd_play(sc, &pb); }
static int mcd_playmsf(struct mcd_softc *sc, struct ioc_play_msf *p) { struct mcd_read2 pb; if (sc->data.debug) device_printf(sc->dev, "playmsf: from %d:%d.%d to %d:%d.%d\n", p->start_m, p->start_s, p->start_f, p->end_m, p->end_s, p->end_f); if ((p->start_m * 60 * 75 + p->start_s * 75 + p->start_f) >= (p->end_m * 60 * 75 + p->end_s * 75 + p->end_f) || (p->end_m * 60 * 75 + p->end_s * 75 + p->end_f) > M_msf(sc->data.volinfo.vol_msf) * 60 * 75 + S_msf(sc->data.volinfo.vol_msf) * 75 + F_msf(sc->data.volinfo.vol_msf)) return (EINVAL); pb.start_msf[0] = bin2bcd(p->start_m); pb.start_msf[1] = bin2bcd(p->start_s); pb.start_msf[2] = bin2bcd(p->start_f); pb.end_msf[0] = bin2bcd(p->end_m); pb.end_msf[1] = bin2bcd(p->end_s); pb.end_msf[2] = bin2bcd(p->end_f); if (mcd_setmode(sc, MCD_MD_COOKED) != 0) return (EIO); return mcd_play(sc, &pb); }
int mcdclose(dev_t dev, int flag, int fmt, struct lwp *l) { struct mcd_softc *sc = device_lookup_private(&mcd_cd, MCDUNIT(dev)); int part = MCDPART(dev); MCD_TRACE("close: partition=%d\n", part); mutex_enter(&sc->sc_lock); switch (fmt) { case S_IFCHR: sc->sc_dk.dk_copenmask &= ~(1 << part); break; case S_IFBLK: sc->sc_dk.dk_bopenmask &= ~(1 << part); break; } sc->sc_dk.dk_openmask = sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; if (sc->sc_dk.dk_openmask == 0) { /* XXXX Must wait for I/O to complete! */ #if 0 (void) mcd_setmode(sc, MCD_MD_SLEEP); #endif (void) mcd_setlock(sc, MCD_LK_UNLOCK); } mutex_exit(&sc->sc_lock); return 0; }
int mcd_playblocks(struct mcd_softc *sc, struct ioc_play_blocks *p) { struct mcd_mbox mbx; int error; if (sc->debug) printf("%s: playblocks: blkno %d length %d\n", device_xname(sc->sc_dev), p->blk, p->len); if (p->blk > sc->disksize || p->len > sc->disksize || (p->blk + p->len) > sc->disksize) return 0; if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0) return error; mbx.cmd.opcode = MCD_CMDREADSINGLESPEED; mbx.cmd.length = sizeof(mbx.cmd.data.play); hsg2msf(p->blk, mbx.cmd.data.play.start_msf); hsg2msf(p->blk + p->len, mbx.cmd.data.play.end_msf); sc->lastpb = mbx.cmd; mbx.res.length = 0; return mcd_send(sc, &mbx, 1); }
int mcd_playmsf(struct mcd_softc *sc, struct ioc_play_msf *p) { struct mcd_mbox mbx; int error; if (sc->debug) printf("%s: playmsf: from %d:%d.%d to %d:%d.%d\n", device_xname(sc->sc_dev), p->start_m, p->start_s, p->start_f, p->end_m, p->end_s, p->end_f); if ((p->start_m * 60 * 75 + p->start_s * 75 + p->start_f) >= (p->end_m * 60 * 75 + p->end_s * 75 + p->end_f)) return EINVAL; if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0) return error; mbx.cmd.opcode = MCD_CMDREADSINGLESPEED; mbx.cmd.length = sizeof(mbx.cmd.data.play); mbx.cmd.data.play.start_msf[0] = bin2bcd(p->start_m); mbx.cmd.data.play.start_msf[1] = bin2bcd(p->start_s); mbx.cmd.data.play.start_msf[2] = bin2bcd(p->start_f); mbx.cmd.data.play.end_msf[0] = bin2bcd(p->end_m); mbx.cmd.data.play.end_msf[1] = bin2bcd(p->end_s); mbx.cmd.data.play.end_msf[2] = bin2bcd(p->end_f); sc->lastpb = mbx.cmd; mbx.res.length = 0; return mcd_send(sc, &mbx, 1); }
int mcd_playtracks(struct mcd_softc *sc, struct ioc_play_track *p) { struct mcd_mbox mbx; int a = p->start_track; int z = p->end_track; int error; if (sc->debug) printf("%s: playtracks: from %d:%d to %d:%d\n", device_xname(sc->sc_dev), a, p->start_index, z, p->end_index); if (a < bcd2bin(sc->volinfo.trk_low) || a > bcd2bin(sc->volinfo.trk_high) || a > z || z < bcd2bin(sc->volinfo.trk_low) || z > bcd2bin(sc->volinfo.trk_high)) return EINVAL; if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0) return error; mbx.cmd.opcode = MCD_CMDREADSINGLESPEED; mbx.cmd.length = sizeof(mbx.cmd.data.play); mbx.cmd.data.play.start_msf[0] = sc->toc[a].toc.absolute_pos[0]; mbx.cmd.data.play.start_msf[1] = sc->toc[a].toc.absolute_pos[1]; mbx.cmd.data.play.start_msf[2] = sc->toc[a].toc.absolute_pos[2]; mbx.cmd.data.play.end_msf[0] = sc->toc[z+1].toc.absolute_pos[0]; mbx.cmd.data.play.end_msf[1] = sc->toc[z+1].toc.absolute_pos[1]; mbx.cmd.data.play.end_msf[2] = sc->toc[z+1].toc.absolute_pos[2]; sc->lastpb = mbx.cmd; mbx.res.length = 0; return mcd_send(sc, &mbx, 1); }
int mcd_read_toc(struct mcd_softc *sc) { struct ioc_toc_header th; union mcd_qchninfo q; int error, trk, idx, retry; if ((error = mcd_toc_header(sc, &th)) != 0) return error; if ((error = mcd_stop(sc)) != 0) return error; if (sc->debug) printf("%s: read_toc: reading qchannel info\n", device_xname(sc->sc_dev)); for (trk = th.starting_track; trk <= th.ending_track; trk++) sc->toc[trk].toc.idx_no = 0x00; trk = th.ending_track - th.starting_track + 1; for (retry = 300; retry && trk > 0; retry--) { if (mcd_getqchan(sc, &q, CD_TRACK_INFO) != 0) break; if (q.toc.trk_no != 0x00 || q.toc.idx_no == 0x00) continue; idx = bcd2bin(q.toc.idx_no); if (idx < MCD_MAXTOCS && sc->toc[idx].toc.idx_no == 0x00) { sc->toc[idx] = q; trk--; } } /* Inform the drive that we're finished so it turns off the light. */ if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0) return error; if (trk != 0) return EINVAL; /* Add a fake last+1 for mcd_playtracks(). */ idx = th.ending_track + 1; sc->toc[idx].toc.control = sc->toc[idx-1].toc.control; sc->toc[idx].toc.addr_type = sc->toc[idx-1].toc.addr_type; sc->toc[idx].toc.trk_no = 0x00; sc->toc[idx].toc.idx_no = 0xaa; sc->toc[idx].toc.absolute_pos[0] = sc->volinfo.vol_msf[0]; sc->toc[idx].toc.absolute_pos[1] = sc->volinfo.vol_msf[1]; sc->toc[idx].toc.absolute_pos[2] = sc->volinfo.vol_msf[2]; return 0; }
int mcd_resume(struct mcd_softc *sc) { struct mcd_mbox mbx; int error; if (sc->audio_status != CD_AS_PLAY_PAUSED) return EINVAL; if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0) return error; mbx.cmd = sc->lastpb; mbx.res.length = 0; return mcd_send(sc, &mbx, 1); }
static int mcd_playblocks(struct mcd_softc *sc, struct ioc_play_blocks *p) { struct mcd_read2 pb; if (sc->data.debug) device_printf(sc->dev, "playblocks: blkno %d length %d\n", p->blk, p->len); if (p->blk > sc->data.disksize || p->len > sc->data.disksize || p->blk < 0 || p->len < 0 || (p->blk + p->len) > sc->data.disksize) return (EINVAL); hsg2msf(p->blk, pb.start_msf); hsg2msf(p->blk + p->len, pb.end_msf); if (mcd_setmode(sc, MCD_MD_COOKED) != 0) return (EIO); return mcd_play(sc, &pb); }
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*/ }
static int mcd_subchan(struct mcd_softc *sc, struct ioc_read_subchannel *sch) { struct mcd_qchninfo q; struct cd_sub_channel_info data; int lba; if (sc->data.debug) device_printf(sc->dev, "subchan af=%d, df=%d\n", sch->address_format, sch->data_format); if (sch->address_format != CD_MSF_FORMAT && sch->address_format != CD_LBA_FORMAT) return (EINVAL); if (sch->data_format != CD_CURRENT_POSITION && sch->data_format != CD_MEDIA_CATALOG) return (EINVAL); if (mcd_setmode(sc, MCD_MD_COOKED) != 0) return (EIO); if (mcd_getqchan(sc, &q) < 0) return (EIO); data.header.audio_status = sc->data.audio_status; data.what.position.data_format = sch->data_format; switch (sch->data_format) { case CD_MEDIA_CATALOG: data.what.media_catalog.mc_valid = 1; data.what.media_catalog.mc_number[0] = '\0'; break; case CD_CURRENT_POSITION: data.what.position.control = q.control; data.what.position.addr_type = q.addr_type; data.what.position.track_number = bcd2bin(q.trk_no); data.what.position.index_number = bcd2bin(q.idx_no); switch (sch->address_format) { case CD_MSF_FORMAT: data.what.position.reladdr.msf.unused = 0; data.what.position.reladdr.msf.minute = bcd2bin(q.trk_size_msf[0]); data.what.position.reladdr.msf.second = bcd2bin(q.trk_size_msf[1]); data.what.position.reladdr.msf.frame = bcd2bin(q.trk_size_msf[2]); data.what.position.absaddr.msf.unused = 0; data.what.position.absaddr.msf.minute = bcd2bin(q.hd_pos_msf[0]); data.what.position.absaddr.msf.second = bcd2bin(q.hd_pos_msf[1]); data.what.position.absaddr.msf.frame = bcd2bin(q.hd_pos_msf[2]); break; case CD_LBA_FORMAT: lba = msf2hsg(q.trk_size_msf, 1); /* * Pre-gap has index number of 0, and decreasing MSF * address. Must be converted to negative LBA, per * SCSI spec. */ if (data.what.position.index_number == 0) lba = -lba; data.what.position.reladdr.lba = htonl(lba); data.what.position.absaddr.lba = htonl(msf2hsg(q.hd_pos_msf, 0)); break; } break; } return copyout(&data, sch->data, min(sizeof(struct cd_sub_channel_info), sch->data_len)); }
static int mcd_read_toc(struct mcd_softc *sc) { struct ioc_toc_header th; struct mcd_qchninfo q; int rc, trk, idx, retry; /* Only read TOC if needed */ if (sc->data.flags & MCDTOC) return (0); if (sc->data.debug) device_printf(sc->dev, "reading toc header\n"); if ((rc = mcd_toc_header(sc, &th)) != 0) return (rc); if (mcd_send(sc, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) return (EIO); if (mcd_setmode(sc, MCD_MD_TOC) != 0) return (EIO); if (sc->data.debug) device_printf(sc->dev, "get_toc reading qchannel info\n"); for(trk=th.starting_track; trk<=th.ending_track; trk++) sc->data.toc[trk].idx_no = 0; trk = th.ending_track - th.starting_track + 1; for(retry=0; retry<600 && trk>0; retry++) { if (mcd_getqchan(sc, &q) < 0) break; idx = bcd2bin(q.idx_no); if (idx>=th.starting_track && idx<=th.ending_track && q.trk_no==0) { if (sc->data.toc[idx].idx_no == 0) { sc->data.toc[idx] = q; trk--; } } } if (mcd_setmode(sc, MCD_MD_COOKED) != 0) return (EIO); if (trk != 0) return (ENXIO); /* add a fake last+1 */ idx = th.ending_track + 1; sc->data.toc[idx].control = sc->data.toc[idx-1].control; sc->data.toc[idx].addr_type = sc->data.toc[idx-1].addr_type; sc->data.toc[idx].trk_no = 0; sc->data.toc[idx].idx_no = MCD_LASTPLUS1; sc->data.toc[idx].hd_pos_msf[0] = sc->data.volinfo.vol_msf[0]; sc->data.toc[idx].hd_pos_msf[1] = sc->data.volinfo.vol_msf[1]; sc->data.toc[idx].hd_pos_msf[2] = sc->data.volinfo.vol_msf[2]; if (sc->data.debug) { int i; for (i = th.starting_track; i <= idx; i++) device_printf(sc->dev, "trk %d idx %d pos %d %d %d\n", i, sc->data.toc[i].idx_no > 0x99 ? sc->data.toc[i].idx_no : bcd2bin(sc->data.toc[i].idx_no), bcd2bin(sc->data.toc[i].hd_pos_msf[0]), bcd2bin(sc->data.toc[i].hd_pos_msf[1]), bcd2bin(sc->data.toc[i].hd_pos_msf[2])); } sc->data.flags |= MCDTOC; return (0); }
int mcdopen(dev_t dev, int flag, int fmt, struct lwp *l) { int error, part; struct mcd_softc *sc; sc = device_lookup_private(&mcd_cd, MCDUNIT(dev)); if (sc == NULL) return ENXIO; mutex_enter(&sc->sc_lock); if (sc->sc_dk.dk_openmask != 0) { /* * If any partition is open, but the disk has been invalidated, * disallow further opens. */ if ((sc->flags & MCDF_LOADED) == 0) { error = EIO; goto bad3; } } else { /* * Lock the drawer. This will also notice any pending disk * change or door open indicator and clear the MCDF_LOADED bit * if necessary. */ (void) mcd_setlock(sc, MCD_LK_LOCK); if ((sc->flags & MCDF_LOADED) == 0) { /* Partially reset the state. */ sc->lastmode = MCD_MD_UNKNOWN; sc->lastupc = MCD_UPC_UNKNOWN; sc->flags |= MCDF_LOADED; /* Set the mode, causing the disk to spin up. */ if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0) goto bad2; /* Load the physical device parameters. */ if (mcd_get_parms(sc) != 0) { error = ENXIO; goto bad2; } /* Read the table of contents. */ if ((error = mcd_read_toc(sc)) != 0) goto bad2; /* Fabricate a disk label. */ mcdgetdisklabel(sc); } } part = MCDPART(dev); MCD_TRACE("open: partition=%d disksize=%ld blksize=%d\n", part, sc->disksize, sc->blksize); /* Check that the partition exists. */ if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions || sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { error = ENXIO; goto bad; } /* Insure only one open at a time. */ switch (fmt) { case S_IFCHR: sc->sc_dk.dk_copenmask |= (1 << part); break; case S_IFBLK: sc->sc_dk.dk_bopenmask |= (1 << part); break; } sc->sc_dk.dk_openmask = sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; mutex_exit(&sc->sc_lock); return 0; bad2: sc->flags &= ~MCDF_LOADED; bad: if (sc->sc_dk.dk_openmask == 0) { #if 0 (void) mcd_setmode(sc, MCD_MD_SLEEP); #endif (void) mcd_setlock(sc, MCD_LK_UNLOCK); } bad3: mutex_exit(&sc->sc_lock); return error; }