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