Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 17 Apr 2001 14:41:37 +0200 (CEST)
From:      un1i@rz.uni-karlsruhe.de
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   kern/26644: [PATCH] ATA/ATAPI driver doesn't implement CDIOCREADSUBCHANNEL correctly
Message-ID:  <200104171241.f3HCfbd01363@i609.hadiko.de>

next in thread | raw e-mail | index | archive | help

>Number:         26644
>Category:       kern
>Synopsis:       [PATCH] ATA/ATAPI driver doesn't implement CDIOCREADSUBCHANNEL correctly
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Apr 17 05:50:01 PDT 2001
>Closed-Date:
>Last-Modified:
>Originator:     Philipp Mergenthaler
>Release:        FreeBSD 5.0-CURRENT i386
>Organization:
Univeristy of Karlsruhe
>Environment:
System: FreeBSD i609.hadiko.de 5.0-CURRENT FreeBSD 5.0-CURRENT #368: Tue Apr 17 12:35:00 CEST 2001 p@i609.hadiko.de:/usr/obj/usr/src/sys/I609 i386


>Description:
1) The ioctl CDIOCREADSUBCHANNEL can be used to request position information,
   the media catalogue or track info. However, atapi-cd.c:acdioctl()
   always returns the position information.

2) At the same place, the function lba2msf() is used to compute the track
   relative address. However, this function adds an offset of 150 frames
   to convert from LBA (logical block address) to MSF (minute, second, frame).
   This is correct for absolute adresses, but not in this case.

>How-To-Repeat:

1) Execute "cdcontrol -f /dev/acd0 status". Note that the media catalog
   number is bogus or contains control characters.

2) Execute "cdcontrol -f /dev/acd0 play 1 && cdcontrol -f /dev/acd0 status"
   and note that the "current position" starts at 0:02.00, not 0:00.00 as
   expected.

   
>Fix:

The following two patches are alternative. The first one tries to stay
with the current style: copy all data from cdp->subchan to struct data
and do the lba-msf conversion with lba2msf().

The second patch isn't so lengthy, it copies data directly from
cdp->subchan to the userland and leaves the lba-msf conversion to
the drive.

I could imagine that reasons for the original style would be to isolate
struct cd_sub_channel_info and struct subchan in case one of them changes,
and to work around broken drives that can't do the lba-msf conversion.

I'm not sure how relevant these are. The code in /sys/cam/scsi/scsi_cd.c
looks more like my second patch.

You'll notice that I always ask the drive for position information first.
This is because the drive will return an error if the media catalog
or track information is requested while an audio play operation is
in progress. Therefore I only request that if the audio status is ok.


Index: atapi-cd.c
===================================================================
RCS file: /ncvs/src/sys/dev/ata/atapi-cd.c,v
retrieving revision 1.88
diff -u -r1.88 atapi-cd.c
--- atapi-cd.c	2001/04/05 11:17:33	1.88
+++ atapi-cd.c	2001/04/17 10:31:09
@@ -726,6 +726,8 @@
 	    struct ioc_read_subchannel *args =
 		(struct ioc_read_subchannel *)addr;
 	    struct cd_sub_channel_info data;
+	    u_int8_t format;
+	    int i;
 	    int len = args->data_len;
 	    int32_t abslba, rellba;
 	    int8_t ccb[16] = { ATAPI_READ_SUBCHANNEL, 0, 0x40, 1, 0, 0, 0,
@@ -738,31 +740,108 @@
 		break;
 	    }
 
+	    format=args->data_format;
+	    if ((format != CD_CURRENT_POSITION) &&
+		(format != CD_MEDIA_CATALOG) && (format != CD_TRACK_INFO)) {
+		error = EINVAL;
+		break;
+	    }
+
 	    if ((error = atapi_queue_cmd(cdp->atp, ccb, (caddr_t)&cdp->subchan, 
 					 sizeof(cdp->subchan), ATPR_F_READ, 10,
 					 NULL, NULL))) {
 		break;
 	    }
-	    abslba = cdp->subchan.abslba;
-	    rellba = cdp->subchan.rellba;
-	    if (args->address_format == CD_MSF_FORMAT) {
-		lba2msf(ntohl(abslba),
-		    &data.what.position.absaddr.msf.minute,
-		    &data.what.position.absaddr.msf.second,
-		    &data.what.position.absaddr.msf.frame);
-		lba2msf(ntohl(rellba),
-		    &data.what.position.reladdr.msf.minute,
-		    &data.what.position.reladdr.msf.second,
-		    &data.what.position.reladdr.msf.frame);
-	    } else {
-		data.what.position.absaddr.lba = abslba;
-		data.what.position.reladdr.lba = rellba;
+
+	    /*
+	     * Ask for media catalogue or track info only if no audio play
+	     * operation is in progress or the drive will return an error.
+	     */
+
+	    if ((format == CD_MEDIA_CATALOG) || (format == CD_TRACK_INFO)) {
+		if (cdp->subchan.header.audio_status == 0x11) {
+		    error = EINVAL;
+		    break;
+		}
+
+		ccb[3] = format;
+		if (format == CD_TRACK_INFO)
+		    ccb[6] = args->track;
+
+		if ((error = atapi_queue_cmd(cdp->atp, ccb,
+					     (caddr_t)&cdp->subchan, 
+					     sizeof(cdp->subchan), ATPR_F_READ,
+					     10, NULL, NULL))) {
+		    break;
+		}
+	    }
+
+	    data.header.audio_status = cdp->subchan.header.audio_status;
+	    data.header.data_len[0] = cdp->subchan.header.data_length >> 8;
+	    data.header.data_len[1] = cdp->subchan.header.data_length & 255;
+
+	    switch (format) {
+	    case CD_CURRENT_POSITION:
+		{
+		    abslba = cdp->subchan.what.position.abslba;
+		    rellba = cdp->subchan.what.position.rellba;
+		    if (args->address_format == CD_MSF_FORMAT) {
+			lba2msf(ntohl(abslba),
+			    &data.what.position.absaddr.msf.minute,
+			    &data.what.position.absaddr.msf.second,
+			    &data.what.position.absaddr.msf.frame);
+			lba2msf(ntohl(rellba - 150),
+			    &data.what.position.reladdr.msf.minute,
+			    &data.what.position.reladdr.msf.second,
+			    &data.what.position.reladdr.msf.frame);
+		    } else {
+			data.what.position.absaddr.lba = abslba;
+			data.what.position.reladdr.lba = rellba;
+		    }
+		    data.what.position.data_format =
+			cdp->subchan.what.position.data_format;
+		    data.what.position.control =
+			cdp->subchan.what.position.control & 0xf;
+		    data.what.position.addr_type =
+			cdp->subchan.what.position.control >> 4;
+		    data.what.position.track_number =
+			cdp->subchan.what.position.track;
+		    data.what.position.index_number =
+			cdp->subchan.what.position.indx;
+		    break;
+		}
+
+	    case CD_MEDIA_CATALOG:
+		{
+		    data.what.media_catalog.data_format =
+			cdp->subchan.what.media_catalog.data_format;
+		    data.what.media_catalog.mc_valid =
+			cdp->subchan.what.media_catalog.mc_valid;
+		    for(i = 0; i < 15; i++)
+			    data.what.media_catalog.mc_number[i] =
+				cdp->subchan.what.media_catalog.mc_number[i];
+		    break;
+		}
+		
+	    case CD_TRACK_INFO:
+		{
+		    data.what.track_info.data_format =
+			cdp->subchan.what.track_info.data_format;
+		    data.what.track_info.control =
+			cdp->subchan.what.track_info.control & 0xf;
+		    data.what.track_info.addr_type =
+			cdp->subchan.what.track_info.control >> 4;
+		    data.what.track_info.track_number =
+			cdp->subchan.what.track_info.track;
+		    data.what.track_info.ti_valid =
+			cdp->subchan.what.track_info.ti_valid;
+		    for(i = 0; i < 15; i++)
+			    data.what.track_info.ti_number[i] =
+				cdp->subchan.what.track_info.ti_number[i];
+		    break;
+
+		}
 	    }
-	    data.header.audio_status = cdp->subchan.audio_status;
-	    data.what.position.control = cdp->subchan.control & 0xf;
-	    data.what.position.addr_type = cdp->subchan.control >> 4;
-	    data.what.position.track_number = cdp->subchan.track;
-	    data.what.position.index_number = cdp->subchan.indx;
 	    error = copyout(&data, args->data, len);
 	    break;
 	}
Index: atapi-cd.h
===================================================================
RCS file: /ncvs/src/sys/dev/ata/atapi-cd.h,v
retrieving revision 1.25
diff -u -r1.25 atapi-cd.h
--- atapi-cd.h	2001/03/21 14:59:38	1.25
+++ atapi-cd.h	2001/04/17 10:19:46
@@ -311,16 +311,40 @@
     struct audiopage		au;		/* audio page info */
     struct audiopage		aumask;		/* audio page mask */
     struct cappage		cap;		/* capabilities page info */
-    struct {					/* subchannel info */
-	u_int8_t	void0;
-	u_int8_t	audio_status;
-	u_int16_t	data_length;
-	u_int8_t	data_format;
-	u_int8_t	control;
-	u_int8_t	track;
-	u_int8_t	indx;
-	u_int32_t	abslba;
-	u_int32_t	rellba;
+    struct {
+	struct {
+		u_int8_t	void0;
+		u_int8_t	audio_status;
+		u_int16_t	data_length;
+	} header;
+	union {					/* subchannel info */
+	    struct {
+		u_int8_t	data_format;
+		u_int8_t	control;
+		u_int8_t	track;
+		u_int8_t	indx;
+		u_int32_t	abslba;
+		u_int32_t	rellba;
+	    } position;
+	    struct {
+		u_int8_t	data_format;
+		u_int8_t	:8;
+		u_int8_t	:8;
+		u_int8_t	:8;
+		u_int8_t	:7;
+		u_int8_t	mc_valid:1;
+		u_int8_t	mc_number[15];
+	    } media_catalog;
+	    struct {
+		u_int8_t	data_format;
+		u_int8_t	control;
+		u_int8_t	track;
+		u_int8_t	:8;
+		u_int8_t	:7;
+		u_int8_t	ti_valid:1;
+		u_int8_t	ti_number[15];
+	    } track_info;
+	} what;
     } subchan;
     struct changer		*changer_info;	/* changer info */
     struct acd_softc		**driver;	/* softc's of changer slots */





Index: atapi-cd.c
===================================================================
RCS file: /ncvs/src/sys/dev/ata/atapi-cd.c,v
retrieving revision 1.88
diff -u -r1.88 atapi-cd.c
--- atapi-cd.c	2001/04/05 11:17:33	1.88
+++ atapi-cd.c	2001/04/17 12:25:23
@@ -725,45 +725,57 @@
 	{
 	    struct ioc_read_subchannel *args =
 		(struct ioc_read_subchannel *)addr;
-	    struct cd_sub_channel_info data;
+	    u_int8_t format;
 	    int len = args->data_len;
-	    int32_t abslba, rellba;
 	    int8_t ccb[16] = { ATAPI_READ_SUBCHANNEL, 0, 0x40, 1, 0, 0, 0,
 			       sizeof(cdp->subchan)>>8, sizeof(cdp->subchan),
 			       0, 0, 0, 0, 0, 0, 0 };
 
-	    if (len > sizeof(data) ||
+	    if (len > sizeof(struct cd_sub_channel_info) ||
 		len < sizeof(struct cd_sub_channel_header)) {
 		error = EINVAL;
 		break;
 	    }
 
+	    format=args->data_format;
+	    if ((format != CD_CURRENT_POSITION) &&
+		(format != CD_MEDIA_CATALOG) && (format != CD_TRACK_INFO)) {
+		error = EINVAL;
+		break;
+	    }
+
+	    ccb[1] = args->address_format & CD_MSF_FORMAT;
+
 	    if ((error = atapi_queue_cmd(cdp->atp, ccb, (caddr_t)&cdp->subchan, 
 					 sizeof(cdp->subchan), ATPR_F_READ, 10,
 					 NULL, NULL))) {
 		break;
 	    }
-	    abslba = cdp->subchan.abslba;
-	    rellba = cdp->subchan.rellba;
-	    if (args->address_format == CD_MSF_FORMAT) {
-		lba2msf(ntohl(abslba),
-		    &data.what.position.absaddr.msf.minute,
-		    &data.what.position.absaddr.msf.second,
-		    &data.what.position.absaddr.msf.frame);
-		lba2msf(ntohl(rellba),
-		    &data.what.position.reladdr.msf.minute,
-		    &data.what.position.reladdr.msf.second,
-		    &data.what.position.reladdr.msf.frame);
-	    } else {
-		data.what.position.absaddr.lba = abslba;
-		data.what.position.reladdr.lba = rellba;
+
+	    /*
+	     * Ask for media catalogue or track info only if no audio play
+	     * operation is in progress or the drive will return an error.
+	     */
+
+	    if ((format == CD_MEDIA_CATALOG) || (format == CD_TRACK_INFO)) {
+		if (cdp->subchan.header.audio_status == 0x11) {
+		    error = EINVAL;
+		    break;
+		}
+
+		ccb[3] = format;
+		if (format == CD_TRACK_INFO)
+		    ccb[6] = args->track;
+
+		if ((error = atapi_queue_cmd(cdp->atp, ccb,
+					     (caddr_t)&cdp->subchan, 
+					     sizeof(cdp->subchan), ATPR_F_READ,
+					     10, NULL, NULL))) {
+		    break;
+		}
 	    }
-	    data.header.audio_status = cdp->subchan.audio_status;
-	    data.what.position.control = cdp->subchan.control & 0xf;
-	    data.what.position.addr_type = cdp->subchan.control >> 4;
-	    data.what.position.track_number = cdp->subchan.track;
-	    data.what.position.index_number = cdp->subchan.indx;
-	    error = copyout(&data, args->data, len);
+
+	    error = copyout(&cdp->subchan, args->data, len);
 	    break;
 	}
 
Index: atapi-cd.h
===================================================================
RCS file: /ncvs/src/sys/dev/ata/atapi-cd.h,v
retrieving revision 1.25
diff -u -r1.25 atapi-cd.h
--- atapi-cd.h	2001/03/21 14:59:38	1.25
+++ atapi-cd.h	2001/04/17 10:19:46
@@ -311,16 +311,40 @@
     struct audiopage		au;		/* audio page info */
     struct audiopage		aumask;		/* audio page mask */
     struct cappage		cap;		/* capabilities page info */
-    struct {					/* subchannel info */
-	u_int8_t	void0;
-	u_int8_t	audio_status;
-	u_int16_t	data_length;
-	u_int8_t	data_format;
-	u_int8_t	control;
-	u_int8_t	track;
-	u_int8_t	indx;
-	u_int32_t	abslba;
-	u_int32_t	rellba;
+    struct {
+	struct {
+		u_int8_t	void0;
+		u_int8_t	audio_status;
+		u_int16_t	data_length;
+	} header;
+	union {					/* subchannel info */
+	    struct {
+		u_int8_t	data_format;
+		u_int8_t	control;
+		u_int8_t	track;
+		u_int8_t	indx;
+		u_int32_t	abslba;
+		u_int32_t	rellba;
+	    } position;
+	    struct {
+		u_int8_t	data_format;
+		u_int8_t	:8;
+		u_int8_t	:8;
+		u_int8_t	:8;
+		u_int8_t	:7;
+		u_int8_t	mc_valid:1;
+		u_int8_t	mc_number[15];
+	    } media_catalog;
+	    struct {
+		u_int8_t	data_format;
+		u_int8_t	control;
+		u_int8_t	track;
+		u_int8_t	:8;
+		u_int8_t	:7;
+		u_int8_t	ti_valid:1;
+		u_int8_t	ti_number[15];
+	    } track_info;
+	} what;
     } subchan;
     struct changer		*changer_info;	/* changer info */
     struct acd_softc		**driver;	/* softc's of changer slots */
>Release-Note:
>Audit-Trail:
>Unformatted:

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200104171241.f3HCfbd01363>