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