162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* -*-linux-c-*-
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci * vendor-specific code for SCSI CD-ROM's goes here.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This is needed becauce most of the new features (multisession and
762306a36Sopenharmony_ci * the like) are too new to be included into the SCSI-II standard (to
862306a36Sopenharmony_ci * be exact: there is'nt anything in my draft copy).
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Aug 1997: Ha! Got a SCSI-3 cdrom spec across my fingers. SCSI-3 does
1162306a36Sopenharmony_ci *           multisession using the READ TOC command (like SONY).
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci *           Rearranged stuff here: SCSI-3 is included allways, support
1462306a36Sopenharmony_ci *           for NEC/TOSHIBA/HP commands is optional.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *   Gerd Knorr <kraxel@cs.tu-berlin.de>
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * --------------------------------------------------------------------------
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * support for XA/multisession-CD's
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci *   - NEC:     Detection and support of multisession CD's.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci *   - TOSHIBA: Detection and support of multisession CD's.
2562306a36Sopenharmony_ci *              Some XA-Sector tweaking, required for older drives.
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci *   - SONY:    Detection and support of multisession CD's.
2862306a36Sopenharmony_ci *              added by Thomas Quinot <thomas@cuivre.freenix.fr>
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci *   - PIONEER, HITACHI, PLEXTOR, MATSHITA, TEAC, PHILIPS: known to
3162306a36Sopenharmony_ci *              work with SONY (SCSI3 now)  code.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci *   - HP:      Much like SONY, but a little different... (Thomas)
3462306a36Sopenharmony_ci *              HP-Writers only ??? Maybe other CD-Writers work with this too ?
3562306a36Sopenharmony_ci *              HP 6020 writers now supported.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <linux/cdrom.h>
3962306a36Sopenharmony_ci#include <linux/errno.h>
4062306a36Sopenharmony_ci#include <linux/string.h>
4162306a36Sopenharmony_ci#include <linux/bcd.h>
4262306a36Sopenharmony_ci#include <linux/blkdev.h>
4362306a36Sopenharmony_ci#include <linux/slab.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include <scsi/scsi.h>
4662306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
4762306a36Sopenharmony_ci#include <scsi/scsi_device.h>
4862306a36Sopenharmony_ci#include <scsi/scsi_host.h>
4962306a36Sopenharmony_ci#include <scsi/scsi_ioctl.h>
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#include "sr.h"
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#if 0
5462306a36Sopenharmony_ci#define DEBUG
5562306a36Sopenharmony_ci#endif
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* here are some constants to sort the vendors into groups */
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define VENDOR_SCSI3           1	/* default: scsi-3 mmc */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define VENDOR_NEC             2
6262306a36Sopenharmony_ci#define VENDOR_TOSHIBA         3
6362306a36Sopenharmony_ci#define VENDOR_WRITER          4	/* pre-scsi3 writers */
6462306a36Sopenharmony_ci#define VENDOR_CYGNAL_85ED     5	/* CD-on-a-chip */
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define VENDOR_TIMEOUT	30*HZ
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_civoid sr_vendor_init(Scsi_CD *cd)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	const char *vendor = cd->device->vendor;
7162306a36Sopenharmony_ci	const char *model = cd->device->model;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* default */
7462306a36Sopenharmony_ci	cd->vendor = VENDOR_SCSI3;
7562306a36Sopenharmony_ci	if (cd->readcd_known)
7662306a36Sopenharmony_ci		/* this is true for scsi3/mmc drives - no more checks */
7762306a36Sopenharmony_ci		return;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (cd->device->type == TYPE_WORM) {
8062306a36Sopenharmony_ci		cd->vendor = VENDOR_WRITER;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	} else if (!strncmp(vendor, "NEC", 3)) {
8362306a36Sopenharmony_ci		cd->vendor = VENDOR_NEC;
8462306a36Sopenharmony_ci		if (!strncmp(model, "CD-ROM DRIVE:25", 15) ||
8562306a36Sopenharmony_ci		    !strncmp(model, "CD-ROM DRIVE:36", 15) ||
8662306a36Sopenharmony_ci		    !strncmp(model, "CD-ROM DRIVE:83", 15) ||
8762306a36Sopenharmony_ci		    !strncmp(model, "CD-ROM DRIVE:84 ", 16)
8862306a36Sopenharmony_ci#if 0
8962306a36Sopenharmony_ci		/* my NEC 3x returns the read-raw data if a read-raw
9062306a36Sopenharmony_ci		   is followed by a read for the same sector - aeb */
9162306a36Sopenharmony_ci		    || !strncmp(model, "CD-ROM DRIVE:500", 16)
9262306a36Sopenharmony_ci#endif
9362306a36Sopenharmony_ci		    )
9462306a36Sopenharmony_ci			/* these can't handle multisession, may hang */
9562306a36Sopenharmony_ci			cd->cdi.mask |= CDC_MULTI_SESSION;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	} else if (!strncmp(vendor, "TOSHIBA", 7)) {
9862306a36Sopenharmony_ci		cd->vendor = VENDOR_TOSHIBA;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	} else if (!strncmp(vendor, "Beurer", 6) &&
10162306a36Sopenharmony_ci		   !strncmp(model, "Gluco Memory", 12)) {
10262306a36Sopenharmony_ci		/* The Beurer GL50 evo uses a Cygnal-manufactured CD-on-a-chip
10362306a36Sopenharmony_ci		   that only accepts a subset of SCSI commands.  Most of the
10462306a36Sopenharmony_ci		   not-implemented commands are fine to fail, but a few,
10562306a36Sopenharmony_ci		   particularly around the MMC or Audio commands, will put the
10662306a36Sopenharmony_ci		   device into an unrecoverable state, so they need to be
10762306a36Sopenharmony_ci		   avoided at all costs.
10862306a36Sopenharmony_ci		*/
10962306a36Sopenharmony_ci		cd->vendor = VENDOR_CYGNAL_85ED;
11062306a36Sopenharmony_ci		cd->cdi.mask |= (
11162306a36Sopenharmony_ci			CDC_MULTI_SESSION |
11262306a36Sopenharmony_ci			CDC_CLOSE_TRAY | CDC_OPEN_TRAY |
11362306a36Sopenharmony_ci			CDC_LOCK |
11462306a36Sopenharmony_ci			CDC_GENERIC_PACKET |
11562306a36Sopenharmony_ci			CDC_PLAY_AUDIO
11662306a36Sopenharmony_ci			);
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/* small handy function for switching block length using MODE SELECT,
12262306a36Sopenharmony_ci * used by sr_read_sector() */
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ciint sr_set_blocklength(Scsi_CD *cd, int blocklength)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	unsigned char *buffer;	/* the buffer for the ioctl */
12762306a36Sopenharmony_ci	struct packet_command cgc;
12862306a36Sopenharmony_ci	struct ccs_modesel_head *modesel;
12962306a36Sopenharmony_ci	int rc, density = 0;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (cd->vendor == VENDOR_TOSHIBA)
13262306a36Sopenharmony_ci		density = (blocklength > 2048) ? 0x81 : 0x83;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	buffer = kmalloc(512, GFP_KERNEL);
13562306a36Sopenharmony_ci	if (!buffer)
13662306a36Sopenharmony_ci		return -ENOMEM;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci#ifdef DEBUG
13962306a36Sopenharmony_ci	sr_printk(KERN_INFO, cd, "MODE SELECT 0x%x/%d\n", density, blocklength);
14062306a36Sopenharmony_ci#endif
14162306a36Sopenharmony_ci	memset(&cgc, 0, sizeof(struct packet_command));
14262306a36Sopenharmony_ci	cgc.cmd[0] = MODE_SELECT;
14362306a36Sopenharmony_ci	cgc.cmd[1] = (1 << 4);
14462306a36Sopenharmony_ci	cgc.cmd[4] = 12;
14562306a36Sopenharmony_ci	modesel = (struct ccs_modesel_head *) buffer;
14662306a36Sopenharmony_ci	memset(modesel, 0, sizeof(*modesel));
14762306a36Sopenharmony_ci	modesel->block_desc_length = 0x08;
14862306a36Sopenharmony_ci	modesel->density = density;
14962306a36Sopenharmony_ci	modesel->block_length_med = (blocklength >> 8) & 0xff;
15062306a36Sopenharmony_ci	modesel->block_length_lo = blocklength & 0xff;
15162306a36Sopenharmony_ci	cgc.buffer = buffer;
15262306a36Sopenharmony_ci	cgc.buflen = sizeof(*modesel);
15362306a36Sopenharmony_ci	cgc.data_direction = DMA_TO_DEVICE;
15462306a36Sopenharmony_ci	cgc.timeout = VENDOR_TIMEOUT;
15562306a36Sopenharmony_ci	if (0 == (rc = sr_do_ioctl(cd, &cgc))) {
15662306a36Sopenharmony_ci		cd->device->sector_size = blocklength;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci#ifdef DEBUG
15962306a36Sopenharmony_ci	else
16062306a36Sopenharmony_ci		sr_printk(KERN_INFO, cd,
16162306a36Sopenharmony_ci			  "switching blocklength to %d bytes failed\n",
16262306a36Sopenharmony_ci			  blocklength);
16362306a36Sopenharmony_ci#endif
16462306a36Sopenharmony_ci	kfree(buffer);
16562306a36Sopenharmony_ci	return rc;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci/* This function gets called after a media change. Checks if the CD is
16962306a36Sopenharmony_ci   multisession, asks for offset etc. */
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ciint sr_cd_check(struct cdrom_device_info *cdi)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	Scsi_CD *cd = cdi->handle;
17462306a36Sopenharmony_ci	unsigned long sector;
17562306a36Sopenharmony_ci	unsigned char *buffer;	/* the buffer for the ioctl */
17662306a36Sopenharmony_ci	struct packet_command cgc;
17762306a36Sopenharmony_ci	int rc, no_multi;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (cd->cdi.mask & CDC_MULTI_SESSION)
18062306a36Sopenharmony_ci		return 0;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	buffer = kmalloc(512, GFP_KERNEL);
18362306a36Sopenharmony_ci	if (!buffer)
18462306a36Sopenharmony_ci		return -ENOMEM;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	sector = 0;		/* the multisession sector offset goes here  */
18762306a36Sopenharmony_ci	no_multi = 0;		/* flag: the drive can't handle multisession */
18862306a36Sopenharmony_ci	rc = 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	memset(&cgc, 0, sizeof(struct packet_command));
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	switch (cd->vendor) {
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	case VENDOR_SCSI3:
19562306a36Sopenharmony_ci		cgc.cmd[0] = READ_TOC;
19662306a36Sopenharmony_ci		cgc.cmd[8] = 12;
19762306a36Sopenharmony_ci		cgc.cmd[9] = 0x40;
19862306a36Sopenharmony_ci		cgc.buffer = buffer;
19962306a36Sopenharmony_ci		cgc.buflen = 12;
20062306a36Sopenharmony_ci		cgc.quiet = 1;
20162306a36Sopenharmony_ci		cgc.data_direction = DMA_FROM_DEVICE;
20262306a36Sopenharmony_ci		cgc.timeout = VENDOR_TIMEOUT;
20362306a36Sopenharmony_ci		rc = sr_do_ioctl(cd, &cgc);
20462306a36Sopenharmony_ci		if (rc != 0)
20562306a36Sopenharmony_ci			break;
20662306a36Sopenharmony_ci		if ((buffer[0] << 8) + buffer[1] < 0x0a) {
20762306a36Sopenharmony_ci			sr_printk(KERN_INFO, cd, "Hmm, seems the drive "
20862306a36Sopenharmony_ci			   "doesn't support multisession CD's\n");
20962306a36Sopenharmony_ci			no_multi = 1;
21062306a36Sopenharmony_ci			break;
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci		sector = buffer[11] + (buffer[10] << 8) +
21362306a36Sopenharmony_ci		    (buffer[9] << 16) + (buffer[8] << 24);
21462306a36Sopenharmony_ci		if (buffer[6] <= 1) {
21562306a36Sopenharmony_ci			/* ignore sector offsets from first track */
21662306a36Sopenharmony_ci			sector = 0;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci		break;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	case VENDOR_NEC:{
22162306a36Sopenharmony_ci			unsigned long min, sec, frame;
22262306a36Sopenharmony_ci			cgc.cmd[0] = 0xde;
22362306a36Sopenharmony_ci			cgc.cmd[1] = 0x03;
22462306a36Sopenharmony_ci			cgc.cmd[2] = 0xb0;
22562306a36Sopenharmony_ci			cgc.buffer = buffer;
22662306a36Sopenharmony_ci			cgc.buflen = 0x16;
22762306a36Sopenharmony_ci			cgc.quiet = 1;
22862306a36Sopenharmony_ci			cgc.data_direction = DMA_FROM_DEVICE;
22962306a36Sopenharmony_ci			cgc.timeout = VENDOR_TIMEOUT;
23062306a36Sopenharmony_ci			rc = sr_do_ioctl(cd, &cgc);
23162306a36Sopenharmony_ci			if (rc != 0)
23262306a36Sopenharmony_ci				break;
23362306a36Sopenharmony_ci			if (buffer[14] != 0 && buffer[14] != 0xb0) {
23462306a36Sopenharmony_ci				sr_printk(KERN_INFO, cd, "Hmm, seems the cdrom "
23562306a36Sopenharmony_ci					  "doesn't support multisession CD's\n");
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci				no_multi = 1;
23862306a36Sopenharmony_ci				break;
23962306a36Sopenharmony_ci			}
24062306a36Sopenharmony_ci			min = bcd2bin(buffer[15]);
24162306a36Sopenharmony_ci			sec = bcd2bin(buffer[16]);
24262306a36Sopenharmony_ci			frame = bcd2bin(buffer[17]);
24362306a36Sopenharmony_ci			sector = min * CD_SECS * CD_FRAMES + sec * CD_FRAMES + frame;
24462306a36Sopenharmony_ci			break;
24562306a36Sopenharmony_ci		}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	case VENDOR_TOSHIBA:{
24862306a36Sopenharmony_ci			unsigned long min, sec, frame;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci			/* we request some disc information (is it a XA-CD ?,
25162306a36Sopenharmony_ci			 * where starts the last session ?) */
25262306a36Sopenharmony_ci			cgc.cmd[0] = 0xc7;
25362306a36Sopenharmony_ci			cgc.cmd[1] = 0x03;
25462306a36Sopenharmony_ci			cgc.buffer = buffer;
25562306a36Sopenharmony_ci			cgc.buflen = 4;
25662306a36Sopenharmony_ci			cgc.quiet = 1;
25762306a36Sopenharmony_ci			cgc.data_direction = DMA_FROM_DEVICE;
25862306a36Sopenharmony_ci			cgc.timeout = VENDOR_TIMEOUT;
25962306a36Sopenharmony_ci			rc = sr_do_ioctl(cd, &cgc);
26062306a36Sopenharmony_ci			if (rc == -EINVAL) {
26162306a36Sopenharmony_ci				sr_printk(KERN_INFO, cd, "Hmm, seems the drive "
26262306a36Sopenharmony_ci					  "doesn't support multisession CD's\n");
26362306a36Sopenharmony_ci				no_multi = 1;
26462306a36Sopenharmony_ci				break;
26562306a36Sopenharmony_ci			}
26662306a36Sopenharmony_ci			if (rc != 0)
26762306a36Sopenharmony_ci				break;
26862306a36Sopenharmony_ci			min = bcd2bin(buffer[1]);
26962306a36Sopenharmony_ci			sec = bcd2bin(buffer[2]);
27062306a36Sopenharmony_ci			frame = bcd2bin(buffer[3]);
27162306a36Sopenharmony_ci			sector = min * CD_SECS * CD_FRAMES + sec * CD_FRAMES + frame;
27262306a36Sopenharmony_ci			if (sector)
27362306a36Sopenharmony_ci				sector -= CD_MSF_OFFSET;
27462306a36Sopenharmony_ci			sr_set_blocklength(cd, 2048);
27562306a36Sopenharmony_ci			break;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	case VENDOR_WRITER:
27962306a36Sopenharmony_ci		cgc.cmd[0] = READ_TOC;
28062306a36Sopenharmony_ci		cgc.cmd[8] = 0x04;
28162306a36Sopenharmony_ci		cgc.cmd[9] = 0x40;
28262306a36Sopenharmony_ci		cgc.buffer = buffer;
28362306a36Sopenharmony_ci		cgc.buflen = 0x04;
28462306a36Sopenharmony_ci		cgc.quiet = 1;
28562306a36Sopenharmony_ci		cgc.data_direction = DMA_FROM_DEVICE;
28662306a36Sopenharmony_ci		cgc.timeout = VENDOR_TIMEOUT;
28762306a36Sopenharmony_ci		rc = sr_do_ioctl(cd, &cgc);
28862306a36Sopenharmony_ci		if (rc != 0) {
28962306a36Sopenharmony_ci			break;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci		if ((rc = buffer[2]) == 0) {
29262306a36Sopenharmony_ci			sr_printk(KERN_WARNING, cd,
29362306a36Sopenharmony_ci				  "No finished session\n");
29462306a36Sopenharmony_ci			break;
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci		cgc.cmd[0] = READ_TOC;	/* Read TOC */
29762306a36Sopenharmony_ci		cgc.cmd[6] = rc & 0x7f;	/* number of last session */
29862306a36Sopenharmony_ci		cgc.cmd[8] = 0x0c;
29962306a36Sopenharmony_ci		cgc.cmd[9] = 0x40;
30062306a36Sopenharmony_ci		cgc.buffer = buffer;
30162306a36Sopenharmony_ci		cgc.buflen = 12;
30262306a36Sopenharmony_ci		cgc.quiet = 1;
30362306a36Sopenharmony_ci		cgc.data_direction = DMA_FROM_DEVICE;
30462306a36Sopenharmony_ci		cgc.timeout = VENDOR_TIMEOUT;
30562306a36Sopenharmony_ci		rc = sr_do_ioctl(cd, &cgc);
30662306a36Sopenharmony_ci		if (rc != 0) {
30762306a36Sopenharmony_ci			break;
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci		sector = buffer[11] + (buffer[10] << 8) +
31062306a36Sopenharmony_ci		    (buffer[9] << 16) + (buffer[8] << 24);
31162306a36Sopenharmony_ci		break;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	default:
31462306a36Sopenharmony_ci		/* should not happen */
31562306a36Sopenharmony_ci		sr_printk(KERN_WARNING, cd,
31662306a36Sopenharmony_ci			  "unknown vendor code (%i), not initialized ?\n",
31762306a36Sopenharmony_ci			  cd->vendor);
31862306a36Sopenharmony_ci		sector = 0;
31962306a36Sopenharmony_ci		no_multi = 1;
32062306a36Sopenharmony_ci		break;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci	cd->ms_offset = sector;
32362306a36Sopenharmony_ci	cd->xa_flag = 0;
32462306a36Sopenharmony_ci	if (CDS_AUDIO != sr_disk_status(cdi) && 1 == sr_is_xa(cd))
32562306a36Sopenharmony_ci		cd->xa_flag = 1;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (2048 != cd->device->sector_size) {
32862306a36Sopenharmony_ci		sr_set_blocklength(cd, 2048);
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci	if (no_multi)
33162306a36Sopenharmony_ci		cdi->mask |= CDC_MULTI_SESSION;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci#ifdef DEBUG
33462306a36Sopenharmony_ci	if (sector)
33562306a36Sopenharmony_ci		sr_printk(KERN_DEBUG, cd, "multisession offset=%lu\n",
33662306a36Sopenharmony_ci			  sector);
33762306a36Sopenharmony_ci#endif
33862306a36Sopenharmony_ci	kfree(buffer);
33962306a36Sopenharmony_ci	return rc;
34062306a36Sopenharmony_ci}
341