diff options
author | ken <ken@FreeBSD.org> | 2000-11-04 02:05:25 +0000 |
---|---|---|
committer | ken <ken@FreeBSD.org> | 2000-11-04 02:05:25 +0000 |
commit | b1f245fc31c6671599be500d6e488985b665e286 (patch) | |
tree | 298634e3c04a6f9aa172185a9f6e6effef1aa79d | |
parent | 9b7d83c3842e1e892a8efd791970211df5aef9ae (diff) | |
download | FreeBSD-src-b1f245fc31c6671599be500d6e488985b665e286.zip FreeBSD-src-b1f245fc31c6671599be500d6e488985b665e286.tar.gz |
Fix a problem with the previous revision (1.42) that showed up with audio
CDs.
With audio CDs, you can't just do a READ(10) call on most drives without
first setting the blocksize with a mode select command. The disklabel code
does a read of the first sector of the media to find a label if it exists.
This caused drives to return an error when an audio CD was in the drive,
due to the problem described above.
The solution is to read the table of contents on the CD, and only attempt
to read the disklabel if the first track is a data track.
This works on all the various CD and DVD media I have tried, but further
testing (especially with Video CDs and other mode 2 media) will be
needed to determine if this is a universal solution.
-rw-r--r-- | sys/cam/scsi/scsi_cd.c | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/sys/cam/scsi/scsi_cd.c b/sys/cam/scsi/scsi_cd.c index 8025560..662327e 100644 --- a/sys/cam/scsi/scsi_cd.c +++ b/sys/cam/scsi/scsi_cd.c @@ -207,6 +207,7 @@ static int cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags); static void cdprevent(struct cam_periph *periph, int action); static int cdsize(dev_t dev, u_int32_t *size); +static int cdfirsttrackisdata(struct cam_periph *periph); static int cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start, struct cd_toc_entry *data, u_int32_t len); @@ -920,6 +921,17 @@ cdopen(dev_t dev, int flags, int fmt, struct proc *p) } /* + * If we get a non-zero return, revert back to not reading the + * label off the disk. The first track is likely audio, which + * won't have a disklabel. + */ + if ((error = cdfirsttrackisdata(periph)) != 0) { + softc->disk.d_dsflags &= ~DSO_COMPATLABEL; + softc->disk.d_dsflags |= DSO_NOLABELS; + error = 0; + } + + /* * Build prototype label for whole disk. * Should take information about different data tracks from the * TOC and put it in the partition table. @@ -993,6 +1005,13 @@ cdclose(dev_t dev, int flag, int fmt, struct proc *p) cdprevent(periph, PR_ALLOW); /* + * Unconditionally set the dsopen() flags back to their default + * state. + */ + softc->disk.d_dsflags &= ~DSO_NOLABELS; + softc->disk.d_dsflags |= DSO_COMPATLABEL; + + /* * Since we're closing this CD, mark the blocksize as unavailable. * It will be marked as available whence the CD is opened again. */ @@ -2542,6 +2561,80 @@ cdsize(dev_t dev, u_int32_t *size) } +/* + * The idea here is to try to figure out whether the first track is data or + * audio. If it is data, we can at least attempt to read a disklabel off + * the first sector of the disk. If it is audio, there won't be a + * disklabel. + * + * This routine returns 0 if the first track is data, and non-zero if there + * is an error or the first track is audio. (If either non-zero case, we + * should not attempt to read the disklabel.) + */ +static int +cdfirsttrackisdata(struct cam_periph *periph) +{ + struct cdtocdata { + struct ioc_toc_header header; + struct cd_toc_entry entries[100]; + }; + struct cd_softc *softc; + struct ioc_toc_header *th; + struct cdtocdata *data; + int num_entries, i; + int error, first_track_audio; + + error = 0; + first_track_audio = -1; + + softc = (struct cd_softc *)periph->softc; + + data = malloc(sizeof(struct cdtocdata), M_TEMP, M_WAITOK); + + th = &data->header; + error = cdreadtoc(periph, 0, 0, (struct cd_toc_entry *)data, + sizeof(*data)); + + if (error) + goto bailout; + + if (softc->quirks & CD_Q_BCD_TRACKS) { + /* we are going to have to convert the BCD + * encoding on the cd to what is expected + */ + th->starting_track = + bcd2bin(th->starting_track); + th->ending_track = bcd2bin(th->ending_track); + } + th->len = scsi_2btoul((u_int8_t *)&th->len); + + if ((th->len - 2) > 0) + num_entries = (th->len - 2) / sizeof(struct cd_toc_entry); + else + num_entries = 0; + + for (i = 0; i < num_entries; i++) { + if (data->entries[i].track == th->starting_track) { + if (data->entries[i].control & 0x4) + first_track_audio = 0; + else + first_track_audio = 1; + break; + } + } + + if (first_track_audio == -1) + error = ENOENT; + else if (first_track_audio == 1) + error = EINVAL; + else + error = 0; +bailout: + free(data, M_TEMP); + + return(error); +} + static int cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) { |