patch-2.1.34 linux/drivers/scsi/sr_vendor.c

Next file: linux/fs/binfmt_em86.c
Previous file: linux/drivers/scsi/sr_ioctl.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.33/linux/drivers/scsi/sr_vendor.c linux/drivers/scsi/sr_vendor.c
@@ -7,6 +7,23 @@
  * be exact: there is'nt anything in my draft copy).
  *
  *   Gerd Knorr <kraxel@cs.tu-berlin.de> 
+ *
+ * --------------------------------------------------------------------------
+ *
+ * support for XA/multisession-CD's
+ * 
+ *   - NEC:     Detection and support of multisession CD's.
+ *     
+ *   - TOSHIBA: Detection and support of multisession CD's.
+ *              Some XA-Sector tweaking, required for older drives.
+ *
+ *   - SONY:	Detection and support of multisession CD's.
+ *              added by Thomas Quinot <operator@melchior.cuivre.fdn.fr>
+ *
+ *   - PIONEER, HITACHI, PLEXTOR, MATSHITA, TEAC: known to work with SONY code.
+ *
+ *   - HP:	Much like SONY, but a little different... (Thomas)
+ *              HP-Writers only ??? Maybe other CD-Writers work with this too ?
  */
 
 #include <linux/errno.h>
@@ -27,9 +44,11 @@
 #define VENDOR_NEC             2
 #define VENDOR_TOSHIBA         3
 #define VENDOR_SONY_LIKE       4   /* much drives are Sony compatible */
-#define VENDOR_HP              5
+#define VENDOR_HP              5   /* HP Writers, others too ?? */
 
+#if 0
 #define DEBUG
+#endif
 
 void
 sr_vendor_init(int minor)
@@ -37,9 +56,13 @@
 	char *vendor = scsi_CDs[minor].device->vendor;
 	char *model  = scsi_CDs[minor].device->model;
 		
-	if (!strncmp (vendor, "NEC", 3)) {
+	if ((!strncmp(vendor,"HP",2) || !strncmp(vendor,"PHILIPS",7)) &&
+	    scsi_CDs[minor].device->type == TYPE_WORM) {
+		scsi_CDs[minor].vendor = VENDOR_HP;
+
+	} else if (!strncmp (vendor, "NEC", 3)) {
 		scsi_CDs[minor].vendor = VENDOR_NEC;
-		if (!strncmp (model,"CD-ROM DRIVE:25", 15) ||
+		if (!strncmp (model,"CD-ROM DRIVE:25", 15)  ||
 		    !strncmp (model,"CD-ROM DRIVE:36", 15)  ||
 		    !strncmp (model,"CD-ROM DRIVE:83", 15)  ||
 		    !strncmp (model,"CD-ROM DRIVE:84 ",16))
@@ -49,45 +72,102 @@
 	} else if (!strncmp (vendor, "TOSHIBA", 7)) {
 		scsi_CDs[minor].vendor = VENDOR_TOSHIBA;
 		
-	} else if (!strncmp (vendor, "HP", 2)) {
-		scsi_CDs[minor].vendor = VENDOR_HP;
-		
 	} else {
 		/* most drives can handled like sony ones, so we take
 		 * it as default */
 		scsi_CDs[minor].vendor = VENDOR_SONY_LIKE;
+#ifdef DEBUG
+		printk(KERN_DEBUG
+		       "sr: using \"Sony group\" multisession code\n");
+#endif
 	}
 }
 
 
+/* small handy function for switching block length using MODE SELECT,
+ * used by sr_read_sector() */
 
-/*
- * support for XA/multisession-CD's
- * 
- *   - NEC:     Detection and support of multisession CD's.
- *     
- *   - TOSHIBA: Detection and support of multisession CD's.
- *              Some XA-Sector tweaking, required for older drives.
- *
- *   - SONY:	Detection and support of multisession CD's.
- *              added by Thomas Quinot <operator@melchior.cuivre.fdn.fr>
- *
- *   - PIONEER, HITACHI, PLEXTOR, MATSHITA: known to work with SONY code.
+static int
+set_density_and_blocklength(int minor, unsigned char *buffer,
+			    int density, int blocklength)
+{
+	unsigned char		cmd[12];    /* the scsi-command */
+	struct ccs_modesel_head	*modesel;
+	int			rc;
+
+	memset(cmd,0,12);
+	cmd[0] = MODE_SELECT;
+	cmd[1] = (scsi_CDs[minor].device->lun << 5) | (1 << 4);
+	cmd[4] = 12;
+	modesel = (struct ccs_modesel_head*)buffer;
+	memset(modesel,0,sizeof(*modesel));
+	modesel->block_desc_length = 0x08;
+	modesel->density           = density;
+	modesel->block_length_med  = (blocklength >> 8 ) & 0xff;
+	modesel->block_length_lo   =  blocklength        & 0xff;
+	rc = sr_do_ioctl(minor, cmd, buffer, sizeof(*modesel));
+#ifdef DEBUG
+	if (rc)
+		printk("sr: switching blocklength to %d bytes failed\n",
+		       blocklength);
+#endif
+	return rc;
+}
+
+
+/* read a sector with other than 2048 bytes length 
+ * dest is assumed to be allocated with scsi_malloc
  *
- *   - HP:	Much like SONY, but a little different... (Thomas)
- *              HP-Writers only ???
+ * XXX maybe we have to do some locking here.
  */
 
+int
+sr_read_sector(int minor, int lba, int blksize, unsigned char *dest)
+{
+	unsigned char   *buffer;    /* the buffer for the ioctl */
+	unsigned char   cmd[12];    /* the scsi-command */
+	int             rc, density;
+
+	density = (scsi_CDs[minor].vendor == VENDOR_TOSHIBA) ? 0x83 : 0;
+
+	buffer = (unsigned char *) scsi_malloc(512);
+	if (!buffer) return -ENOMEM;
+
+	rc = set_density_and_blocklength(minor, buffer, density, blksize);
+	if (!rc) {
+		memset(cmd,0,12);
+		cmd[0] = READ_10;
+		cmd[1] = (scsi_CDs[minor].device->lun << 5);
+		cmd[2] = (unsigned char)(lba >> 24) & 0xff;
+		cmd[3] = (unsigned char)(lba >> 16) & 0xff;
+		cmd[4] = (unsigned char)(lba >>  8) & 0xff;
+		cmd[5] = (unsigned char) lba        & 0xff;
+		cmd[8] = 1;
+		rc = sr_do_ioctl(minor, cmd, dest, blksize);
+		set_density_and_blocklength(minor, buffer, density, 2048);
+	}
+	
+	scsi_free(buffer, 512);
+	return rc;
+}
+
+
+/* This function gets called after a media change. Checks if the CD is
+   multisession, asks for offset etc. */
+
 #define BCD_TO_BIN(x)    ((((int)x & 0xf0) >> 4)*10 + ((int)x & 0x0f))
 
 int sr_cd_check(struct cdrom_device_info *cdi)
 {
 	unsigned long   sector,min,sec,frame;
-	unsigned char   *buffer;    /* the buffer for the ioctl */
-	unsigned char   cmd[12];    /* the scsi-command */
+	unsigned char   *buffer;     /* the buffer for the ioctl */
+	unsigned char   *raw_sector; 
+	unsigned char   cmd[12];     /* the scsi-command */
 	int             rc,is_xa,no_multi,minor;
 
 	minor = MINOR(cdi->dev);
+	if (scsi_CDs[minor].cdi.mask & CDC_MULTI_SESSION)
+		return 0;
 	
 	buffer = (unsigned char *) scsi_malloc(512);
 	if(!buffer) return -ENOMEM;
@@ -116,7 +196,6 @@
 		sec    = BCD_TO_BIN(buffer[16]);
 		frame  = BCD_TO_BIN(buffer[17]);
 		sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
-		is_xa  = (buffer[14] == 0xb0);
 		break;
 		
 	case VENDOR_TOSHIBA:
@@ -141,36 +220,6 @@
 		sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
 		if (sector)
 			sector -= CD_BLOCK_OFFSET;
-		is_xa  = (buffer[0] == 0x20);
-#if 0
-		/* this is required for some CD's:
-		 *   - Enhanced-CD (Hardware tells wrong XA-flag)
-		 *   - these broken non-XA multisession CD's
-		 */
-		if (is_xa == 0 && sector != 0) {
-			printk(KERN_WARNING "Warning: multisession offset "
-			       "found, setting XA-flag\n");
-			is_xa = 1;
-		}
-#endif
-		/* now the XA-Sector tweaking: set_density... */
-		memset(cmd,0,12);
-		cmd[0] = MODE_SELECT;
-		cmd[1] = (scsi_CDs[minor].device->lun << 5)
-			| (1 << 4);
-		cmd[4] = 12;
-		memset(buffer,0,12);
-		buffer[ 3] = 0x08;
-		buffer[ 4] = 0x83;
-		buffer[10] = 0x08;
-		rc = sr_do_ioctl(minor, cmd, buffer, 12);
-		if (rc != 0) {
-			break;
-		}
-#if 0
-		/* shoult'nt be required any more */
-		scsi_CDs[minor].needs_sector_size = 1;
-#endif
 		break;
 
 	case VENDOR_HP:
@@ -209,15 +258,10 @@
 		sector = buffer[11] + (buffer[10] << 8) +
 			(buffer[9] << 16) + (buffer[8] << 24);
 #endif
-		is_xa = !!sector;
 		break;
 
 	case VENDOR_SONY_LIKE:
 		/* Thomas QUINOT <thomas@melchior.cuivre.fdn.fr> */
-#ifdef DEBUG
-		printk(KERN_DEBUG
-		       "sr: use \"Sony group\" multisession code\n");
-#endif
 		memset(cmd,0,12);
 		cmd[0] = READ_TOC;
 		cmd[1] = (scsi_CDs[minor].device->lun << 5);
@@ -227,7 +271,7 @@
 		if (rc != 0) {
 			break;
 		}
-		if ((buffer[0] << 8) + buffer[1] != 0x0a) {
+		if ((buffer[0] << 8) + buffer[1] < 0x0a) {
 			printk(KERN_INFO "sr (sony): Hmm, seems the drive doesn't support multisession CD's\n");
 			no_multi = 1;
 			break;
@@ -238,7 +282,6 @@
 			/* ignore sector offsets from first track */
 			sector = 0;
 		}
-		is_xa = !!sector;
 		break;
 		
 	case VENDOR_CAN_NOT_HANDLE:
@@ -255,19 +298,39 @@
 		no_multi = 1;
 		break;
 	}
-    
+   
+	scsi_CDs[minor].xa_flag = 0;
+	if (CDS_AUDIO != sr_disk_status(cdi)) { 
+	    /* read a sector in raw mode to check the sector format */
+	    raw_sector = (unsigned char *) scsi_malloc(2048+512);
+	    if (!buffer) return -ENOMEM;
+	    if (0 == sr_read_sector(minor,sector+16,CD_FRAMESIZE_RAW1,
+				    raw_sector)){
+		is_xa = (raw_sector[3] == 0x02);
+		if (sector > 0 && !is_xa)
+			printk(KERN_INFO "sr: broken CD found: It is "
+			       "multisession, but has'nt XA sectors\n");
+	    } else {
+		/* read a raw sector failed for some reason. */
+		is_xa = (sector > 0);
+	    }
+	    scsi_free(raw_sector, 2048+512);
+	}
 #ifdef DEBUG
-	if (sector)
-		printk(KERN_DEBUG
-		       "sr: multisession CD detected, offset: %lu\n",sector);
+	else printk("sr: audio CD found\n");
 #endif
 
 	scsi_CDs[minor].ms_offset = sector;
 	scsi_CDs[minor].xa_flag = is_xa;
 	if (no_multi)
 		cdi->mask |= CDC_MULTI_SESSION;
-	
-	scsi_free(buffer, 512);
 
+#ifdef DEBUG
+	printk(KERN_DEBUG
+	       "sr: multisession offset=%lu, XA=%s\n",
+	       sector,is_xa ? "yes" : "no");
+#endif
+
+	scsi_free(buffer, 512);
 	return rc;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov