xref: /linux/drivers/scsi/sr_vendor.c (revision 7b32b8e018d8f8cc94c808a5fa84a3f889441b91)
11da177e4SLinus Torvalds /* -*-linux-c-*-
21da177e4SLinus Torvalds 
31da177e4SLinus Torvalds  * vendor-specific code for SCSI CD-ROM's goes here.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * This is needed becauce most of the new features (multisession and
61da177e4SLinus Torvalds  * the like) are too new to be included into the SCSI-II standard (to
71da177e4SLinus Torvalds  * be exact: there is'nt anything in my draft copy).
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Aug 1997: Ha! Got a SCSI-3 cdrom spec across my fingers. SCSI-3 does
101da177e4SLinus Torvalds  *           multisession using the READ TOC command (like SONY).
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  *           Rearranged stuff here: SCSI-3 is included allways, support
131da177e4SLinus Torvalds  *           for NEC/TOSHIBA/HP commands is optional.
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  *   Gerd Knorr <kraxel@cs.tu-berlin.de>
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  * --------------------------------------------------------------------------
181da177e4SLinus Torvalds  *
191da177e4SLinus Torvalds  * support for XA/multisession-CD's
201da177e4SLinus Torvalds  *
211da177e4SLinus Torvalds  *   - NEC:     Detection and support of multisession CD's.
221da177e4SLinus Torvalds  *
231da177e4SLinus Torvalds  *   - TOSHIBA: Detection and support of multisession CD's.
241da177e4SLinus Torvalds  *              Some XA-Sector tweaking, required for older drives.
251da177e4SLinus Torvalds  *
261da177e4SLinus Torvalds  *   - SONY:    Detection and support of multisession CD's.
271da177e4SLinus Torvalds  *              added by Thomas Quinot <thomas@cuivre.freenix.fr>
281da177e4SLinus Torvalds  *
291da177e4SLinus Torvalds  *   - PIONEER, HITACHI, PLEXTOR, MATSHITA, TEAC, PHILIPS: known to
301da177e4SLinus Torvalds  *              work with SONY (SCSI3 now)  code.
311da177e4SLinus Torvalds  *
321da177e4SLinus Torvalds  *   - HP:      Much like SONY, but a little different... (Thomas)
331da177e4SLinus Torvalds  *              HP-Writers only ??? Maybe other CD-Writers work with this too ?
341da177e4SLinus Torvalds  *              HP 6020 writers now supported.
351da177e4SLinus Torvalds  */
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds #include <linux/config.h>
381da177e4SLinus Torvalds #include <linux/cdrom.h>
391da177e4SLinus Torvalds #include <linux/errno.h>
401da177e4SLinus Torvalds #include <linux/string.h>
411da177e4SLinus Torvalds #include <linux/bcd.h>
421da177e4SLinus Torvalds #include <linux/blkdev.h>
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds #include <scsi/scsi.h>
451da177e4SLinus Torvalds #include <scsi/scsi_cmnd.h>
461da177e4SLinus Torvalds #include <scsi/scsi_device.h>
471da177e4SLinus Torvalds #include <scsi/scsi_host.h>
481da177e4SLinus Torvalds #include <scsi/scsi_ioctl.h>
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds #include "sr.h"
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds #if 0
531da177e4SLinus Torvalds #define DEBUG
541da177e4SLinus Torvalds #endif
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds /* here are some constants to sort the vendors into groups */
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds #define VENDOR_SCSI3           1	/* default: scsi-3 mmc */
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds #define VENDOR_NEC             2
611da177e4SLinus Torvalds #define VENDOR_TOSHIBA         3
621da177e4SLinus Torvalds #define VENDOR_WRITER          4	/* pre-scsi3 writers */
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds #define VENDOR_TIMEOUT	30*HZ
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds void sr_vendor_init(Scsi_CD *cd)
671da177e4SLinus Torvalds {
681da177e4SLinus Torvalds #ifndef CONFIG_BLK_DEV_SR_VENDOR
691da177e4SLinus Torvalds 	cd->vendor = VENDOR_SCSI3;
701da177e4SLinus Torvalds #else
71*7b32b8e0SMatthew Wilcox 	const char *vendor = cd->device->vendor;
72*7b32b8e0SMatthew Wilcox 	const char *model = cd->device->model;
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 	/* default */
751da177e4SLinus Torvalds 	cd->vendor = VENDOR_SCSI3;
761da177e4SLinus Torvalds 	if (cd->readcd_known)
771da177e4SLinus Torvalds 		/* this is true for scsi3/mmc drives - no more checks */
781da177e4SLinus Torvalds 		return;
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds 	if (cd->device->type == TYPE_WORM) {
811da177e4SLinus Torvalds 		cd->vendor = VENDOR_WRITER;
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds 	} else if (!strncmp(vendor, "NEC", 3)) {
841da177e4SLinus Torvalds 		cd->vendor = VENDOR_NEC;
851da177e4SLinus Torvalds 		if (!strncmp(model, "CD-ROM DRIVE:25", 15) ||
861da177e4SLinus Torvalds 		    !strncmp(model, "CD-ROM DRIVE:36", 15) ||
871da177e4SLinus Torvalds 		    !strncmp(model, "CD-ROM DRIVE:83", 15) ||
881da177e4SLinus Torvalds 		    !strncmp(model, "CD-ROM DRIVE:84 ", 16)
891da177e4SLinus Torvalds #if 0
901da177e4SLinus Torvalds 		/* my NEC 3x returns the read-raw data if a read-raw
911da177e4SLinus Torvalds 		   is followed by a read for the same sector - aeb */
921da177e4SLinus Torvalds 		    || !strncmp(model, "CD-ROM DRIVE:500", 16)
931da177e4SLinus Torvalds #endif
941da177e4SLinus Torvalds 		    )
951da177e4SLinus Torvalds 			/* these can't handle multisession, may hang */
961da177e4SLinus Torvalds 			cd->cdi.mask |= CDC_MULTI_SESSION;
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds 	} else if (!strncmp(vendor, "TOSHIBA", 7)) {
991da177e4SLinus Torvalds 		cd->vendor = VENDOR_TOSHIBA;
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	}
1021da177e4SLinus Torvalds #endif
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds /* small handy function for switching block length using MODE SELECT,
1071da177e4SLinus Torvalds  * used by sr_read_sector() */
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds int sr_set_blocklength(Scsi_CD *cd, int blocklength)
1101da177e4SLinus Torvalds {
1111da177e4SLinus Torvalds 	unsigned char *buffer;	/* the buffer for the ioctl */
1121da177e4SLinus Torvalds 	struct packet_command cgc;
1131da177e4SLinus Torvalds 	struct ccs_modesel_head *modesel;
1141da177e4SLinus Torvalds 	int rc, density = 0;
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds #ifdef CONFIG_BLK_DEV_SR_VENDOR
1171da177e4SLinus Torvalds 	if (cd->vendor == VENDOR_TOSHIBA)
1181da177e4SLinus Torvalds 		density = (blocklength > 2048) ? 0x81 : 0x83;
1191da177e4SLinus Torvalds #endif
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds 	buffer = (unsigned char *) kmalloc(512, GFP_KERNEL | GFP_DMA);
1221da177e4SLinus Torvalds 	if (!buffer)
1231da177e4SLinus Torvalds 		return -ENOMEM;
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds #ifdef DEBUG
1261da177e4SLinus Torvalds 	printk("%s: MODE SELECT 0x%x/%d\n", cd->cdi.name, density, blocklength);
1271da177e4SLinus Torvalds #endif
1281da177e4SLinus Torvalds 	memset(&cgc, 0, sizeof(struct packet_command));
1291da177e4SLinus Torvalds 	cgc.cmd[0] = MODE_SELECT;
1301da177e4SLinus Torvalds 	cgc.cmd[1] = (1 << 4);
1311da177e4SLinus Torvalds 	cgc.cmd[4] = 12;
1321da177e4SLinus Torvalds 	modesel = (struct ccs_modesel_head *) buffer;
1331da177e4SLinus Torvalds 	memset(modesel, 0, sizeof(*modesel));
1341da177e4SLinus Torvalds 	modesel->block_desc_length = 0x08;
1351da177e4SLinus Torvalds 	modesel->density = density;
1361da177e4SLinus Torvalds 	modesel->block_length_med = (blocklength >> 8) & 0xff;
1371da177e4SLinus Torvalds 	modesel->block_length_lo = blocklength & 0xff;
1381da177e4SLinus Torvalds 	cgc.buffer = buffer;
1391da177e4SLinus Torvalds 	cgc.buflen = sizeof(*modesel);
1401da177e4SLinus Torvalds 	cgc.data_direction = DMA_TO_DEVICE;
1411da177e4SLinus Torvalds 	cgc.timeout = VENDOR_TIMEOUT;
1421da177e4SLinus Torvalds 	if (0 == (rc = sr_do_ioctl(cd, &cgc))) {
1431da177e4SLinus Torvalds 		cd->device->sector_size = blocklength;
1441da177e4SLinus Torvalds 	}
1451da177e4SLinus Torvalds #ifdef DEBUG
1461da177e4SLinus Torvalds 	else
1471da177e4SLinus Torvalds 		printk("%s: switching blocklength to %d bytes failed\n",
1481da177e4SLinus Torvalds 		       cd->cdi.name, blocklength);
1491da177e4SLinus Torvalds #endif
1501da177e4SLinus Torvalds 	kfree(buffer);
1511da177e4SLinus Torvalds 	return rc;
1521da177e4SLinus Torvalds }
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds /* This function gets called after a media change. Checks if the CD is
1551da177e4SLinus Torvalds    multisession, asks for offset etc. */
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds int sr_cd_check(struct cdrom_device_info *cdi)
1581da177e4SLinus Torvalds {
1591da177e4SLinus Torvalds 	Scsi_CD *cd = cdi->handle;
1601da177e4SLinus Torvalds 	unsigned long sector;
1611da177e4SLinus Torvalds 	unsigned char *buffer;	/* the buffer for the ioctl */
1621da177e4SLinus Torvalds 	struct packet_command cgc;
1631da177e4SLinus Torvalds 	int rc, no_multi;
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	if (cd->cdi.mask & CDC_MULTI_SESSION)
1661da177e4SLinus Torvalds 		return 0;
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 	buffer = (unsigned char *) kmalloc(512, GFP_KERNEL | GFP_DMA);
1691da177e4SLinus Torvalds 	if (!buffer)
1701da177e4SLinus Torvalds 		return -ENOMEM;
1711da177e4SLinus Torvalds 
1721da177e4SLinus Torvalds 	sector = 0;		/* the multisession sector offset goes here  */
1731da177e4SLinus Torvalds 	no_multi = 0;		/* flag: the drive can't handle multisession */
1741da177e4SLinus Torvalds 	rc = 0;
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	memset(&cgc, 0, sizeof(struct packet_command));
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds 	switch (cd->vendor) {
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds 	case VENDOR_SCSI3:
1811da177e4SLinus Torvalds 		cgc.cmd[0] = READ_TOC;
1821da177e4SLinus Torvalds 		cgc.cmd[8] = 12;
1831da177e4SLinus Torvalds 		cgc.cmd[9] = 0x40;
1841da177e4SLinus Torvalds 		cgc.buffer = buffer;
1851da177e4SLinus Torvalds 		cgc.buflen = 12;
1861da177e4SLinus Torvalds 		cgc.quiet = 1;
1871da177e4SLinus Torvalds 		cgc.data_direction = DMA_FROM_DEVICE;
1881da177e4SLinus Torvalds 		cgc.timeout = VENDOR_TIMEOUT;
1891da177e4SLinus Torvalds 		rc = sr_do_ioctl(cd, &cgc);
1901da177e4SLinus Torvalds 		if (rc != 0)
1911da177e4SLinus Torvalds 			break;
1921da177e4SLinus Torvalds 		if ((buffer[0] << 8) + buffer[1] < 0x0a) {
1931da177e4SLinus Torvalds 			printk(KERN_INFO "%s: Hmm, seems the drive "
1941da177e4SLinus Torvalds 			   "doesn't support multisession CD's\n", cd->cdi.name);
1951da177e4SLinus Torvalds 			no_multi = 1;
1961da177e4SLinus Torvalds 			break;
1971da177e4SLinus Torvalds 		}
1981da177e4SLinus Torvalds 		sector = buffer[11] + (buffer[10] << 8) +
1991da177e4SLinus Torvalds 		    (buffer[9] << 16) + (buffer[8] << 24);
2001da177e4SLinus Torvalds 		if (buffer[6] <= 1) {
2011da177e4SLinus Torvalds 			/* ignore sector offsets from first track */
2021da177e4SLinus Torvalds 			sector = 0;
2031da177e4SLinus Torvalds 		}
2041da177e4SLinus Torvalds 		break;
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds #ifdef CONFIG_BLK_DEV_SR_VENDOR
2071da177e4SLinus Torvalds 	case VENDOR_NEC:{
2081da177e4SLinus Torvalds 			unsigned long min, sec, frame;
2091da177e4SLinus Torvalds 			cgc.cmd[0] = 0xde;
2101da177e4SLinus Torvalds 			cgc.cmd[1] = 0x03;
2111da177e4SLinus Torvalds 			cgc.cmd[2] = 0xb0;
2121da177e4SLinus Torvalds 			cgc.buffer = buffer;
2131da177e4SLinus Torvalds 			cgc.buflen = 0x16;
2141da177e4SLinus Torvalds 			cgc.quiet = 1;
2151da177e4SLinus Torvalds 			cgc.data_direction = DMA_FROM_DEVICE;
2161da177e4SLinus Torvalds 			cgc.timeout = VENDOR_TIMEOUT;
2171da177e4SLinus Torvalds 			rc = sr_do_ioctl(cd, &cgc);
2181da177e4SLinus Torvalds 			if (rc != 0)
2191da177e4SLinus Torvalds 				break;
2201da177e4SLinus Torvalds 			if (buffer[14] != 0 && buffer[14] != 0xb0) {
2211da177e4SLinus Torvalds 				printk(KERN_INFO "%s: Hmm, seems the cdrom "
2221da177e4SLinus Torvalds 				       "doesn't support multisession CD's\n",
2231da177e4SLinus Torvalds 				       cd->cdi.name);
2241da177e4SLinus Torvalds 				no_multi = 1;
2251da177e4SLinus Torvalds 				break;
2261da177e4SLinus Torvalds 			}
2271da177e4SLinus Torvalds 			min = BCD2BIN(buffer[15]);
2281da177e4SLinus Torvalds 			sec = BCD2BIN(buffer[16]);
2291da177e4SLinus Torvalds 			frame = BCD2BIN(buffer[17]);
2301da177e4SLinus Torvalds 			sector = min * CD_SECS * CD_FRAMES + sec * CD_FRAMES + frame;
2311da177e4SLinus Torvalds 			break;
2321da177e4SLinus Torvalds 		}
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 	case VENDOR_TOSHIBA:{
2351da177e4SLinus Torvalds 			unsigned long min, sec, frame;
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 			/* we request some disc information (is it a XA-CD ?,
2381da177e4SLinus Torvalds 			 * where starts the last session ?) */
2391da177e4SLinus Torvalds 			cgc.cmd[0] = 0xc7;
2401da177e4SLinus Torvalds 			cgc.cmd[1] = 0x03;
2411da177e4SLinus Torvalds 			cgc.buffer = buffer;
2421da177e4SLinus Torvalds 			cgc.buflen = 4;
2431da177e4SLinus Torvalds 			cgc.quiet = 1;
2441da177e4SLinus Torvalds 			cgc.data_direction = DMA_FROM_DEVICE;
2451da177e4SLinus Torvalds 			cgc.timeout = VENDOR_TIMEOUT;
2461da177e4SLinus Torvalds 			rc = sr_do_ioctl(cd, &cgc);
2471da177e4SLinus Torvalds 			if (rc == -EINVAL) {
2481da177e4SLinus Torvalds 				printk(KERN_INFO "%s: Hmm, seems the drive "
2491da177e4SLinus Torvalds 				       "doesn't support multisession CD's\n",
2501da177e4SLinus Torvalds 				       cd->cdi.name);
2511da177e4SLinus Torvalds 				no_multi = 1;
2521da177e4SLinus Torvalds 				break;
2531da177e4SLinus Torvalds 			}
2541da177e4SLinus Torvalds 			if (rc != 0)
2551da177e4SLinus Torvalds 				break;
2561da177e4SLinus Torvalds 			min = BCD2BIN(buffer[1]);
2571da177e4SLinus Torvalds 			sec = BCD2BIN(buffer[2]);
2581da177e4SLinus Torvalds 			frame = BCD2BIN(buffer[3]);
2591da177e4SLinus Torvalds 			sector = min * CD_SECS * CD_FRAMES + sec * CD_FRAMES + frame;
2601da177e4SLinus Torvalds 			if (sector)
2611da177e4SLinus Torvalds 				sector -= CD_MSF_OFFSET;
2621da177e4SLinus Torvalds 			sr_set_blocklength(cd, 2048);
2631da177e4SLinus Torvalds 			break;
2641da177e4SLinus Torvalds 		}
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds 	case VENDOR_WRITER:
2671da177e4SLinus Torvalds 		cgc.cmd[0] = READ_TOC;
2681da177e4SLinus Torvalds 		cgc.cmd[8] = 0x04;
2691da177e4SLinus Torvalds 		cgc.cmd[9] = 0x40;
2701da177e4SLinus Torvalds 		cgc.buffer = buffer;
2711da177e4SLinus Torvalds 		cgc.buflen = 0x04;
2721da177e4SLinus Torvalds 		cgc.quiet = 1;
2731da177e4SLinus Torvalds 		cgc.data_direction = DMA_FROM_DEVICE;
2741da177e4SLinus Torvalds 		cgc.timeout = VENDOR_TIMEOUT;
2751da177e4SLinus Torvalds 		rc = sr_do_ioctl(cd, &cgc);
2761da177e4SLinus Torvalds 		if (rc != 0) {
2771da177e4SLinus Torvalds 			break;
2781da177e4SLinus Torvalds 		}
2791da177e4SLinus Torvalds 		if ((rc = buffer[2]) == 0) {
2801da177e4SLinus Torvalds 			printk(KERN_WARNING
2811da177e4SLinus Torvalds 			       "%s: No finished session\n", cd->cdi.name);
2821da177e4SLinus Torvalds 			break;
2831da177e4SLinus Torvalds 		}
2841da177e4SLinus Torvalds 		cgc.cmd[0] = READ_TOC;	/* Read TOC */
2851da177e4SLinus Torvalds 		cgc.cmd[6] = rc & 0x7f;	/* number of last session */
2861da177e4SLinus Torvalds 		cgc.cmd[8] = 0x0c;
2871da177e4SLinus Torvalds 		cgc.cmd[9] = 0x40;
2881da177e4SLinus Torvalds 		cgc.buffer = buffer;
2891da177e4SLinus Torvalds 		cgc.buflen = 12;
2901da177e4SLinus Torvalds 		cgc.quiet = 1;
2911da177e4SLinus Torvalds 		cgc.data_direction = DMA_FROM_DEVICE;
2921da177e4SLinus Torvalds 		cgc.timeout = VENDOR_TIMEOUT;
2931da177e4SLinus Torvalds 		rc = sr_do_ioctl(cd, &cgc);
2941da177e4SLinus Torvalds 		if (rc != 0) {
2951da177e4SLinus Torvalds 			break;
2961da177e4SLinus Torvalds 		}
2971da177e4SLinus Torvalds 		sector = buffer[11] + (buffer[10] << 8) +
2981da177e4SLinus Torvalds 		    (buffer[9] << 16) + (buffer[8] << 24);
2991da177e4SLinus Torvalds 		break;
3001da177e4SLinus Torvalds #endif				/* CONFIG_BLK_DEV_SR_VENDOR */
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds 	default:
3031da177e4SLinus Torvalds 		/* should not happen */
3041da177e4SLinus Torvalds 		printk(KERN_WARNING
3051da177e4SLinus Torvalds 		   "%s: unknown vendor code (%i), not initialized ?\n",
3061da177e4SLinus Torvalds 		       cd->cdi.name, cd->vendor);
3071da177e4SLinus Torvalds 		sector = 0;
3081da177e4SLinus Torvalds 		no_multi = 1;
3091da177e4SLinus Torvalds 		break;
3101da177e4SLinus Torvalds 	}
3111da177e4SLinus Torvalds 	cd->ms_offset = sector;
3121da177e4SLinus Torvalds 	cd->xa_flag = 0;
3131da177e4SLinus Torvalds 	if (CDS_AUDIO != sr_disk_status(cdi) && 1 == sr_is_xa(cd))
3141da177e4SLinus Torvalds 		cd->xa_flag = 1;
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds 	if (2048 != cd->device->sector_size) {
3171da177e4SLinus Torvalds 		sr_set_blocklength(cd, 2048);
3181da177e4SLinus Torvalds 	}
3191da177e4SLinus Torvalds 	if (no_multi)
3201da177e4SLinus Torvalds 		cdi->mask |= CDC_MULTI_SESSION;
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds #ifdef DEBUG
3231da177e4SLinus Torvalds 	if (sector)
3241da177e4SLinus Torvalds 		printk(KERN_DEBUG "%s: multisession offset=%lu\n",
3251da177e4SLinus Torvalds 		       cd->cdi.name, sector);
3261da177e4SLinus Torvalds #endif
3271da177e4SLinus Torvalds 	kfree(buffer);
3281da177e4SLinus Torvalds 	return rc;
3291da177e4SLinus Torvalds }
330