From b1f245fc31c6671599be500d6e488985b665e286 Mon Sep 17 00:00:00 2001 From: ken Date: Sat, 4 Nov 2000 02:05:25 +0000 Subject: 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. --- sys/cam/scsi/scsi_cd.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) 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) { -- cgit v1.1