18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  sr.c Copyright (C) 1992 David Giller
48c2ecf20Sopenharmony_ci *           Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  adapted from:
78c2ecf20Sopenharmony_ci *      sd.c Copyright (C) 1992 Drew Eckhardt
88c2ecf20Sopenharmony_ci *      Linux scsi disk driver by
98c2ecf20Sopenharmony_ci *              Drew Eckhardt <drew@colorado.edu>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *	Modified by Eric Youngdale ericy@andante.org to
128c2ecf20Sopenharmony_ci *	add scatter-gather, multiple outstanding request, and other
138c2ecf20Sopenharmony_ci *	enhancements.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci *      Modified by Eric Youngdale eric@andante.org to support loadable
168c2ecf20Sopenharmony_ci *      low-level scsi drivers.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci *      Modified by Thomas Quinot thomas@melchior.cuivre.fdn.fr to
198c2ecf20Sopenharmony_ci *      provide auto-eject.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci *      Modified by Gerd Knorr <kraxel@cs.tu-berlin.de> to support the
228c2ecf20Sopenharmony_ci *      generic cdrom interface
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci *      Modified by Jens Axboe <axboe@suse.de> - Uniform sr_packet()
258c2ecf20Sopenharmony_ci *      interface, capabilities probe additions, ioctl cleanups, etc.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci *	Modified by Richard Gooch <rgooch@atnf.csiro.au> to support devfs
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci *	Modified by Jens Axboe <axboe@suse.de> - support DVD-RAM
308c2ecf20Sopenharmony_ci *	transparently and lose the GHOST hack
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci *	Modified by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
338c2ecf20Sopenharmony_ci *	check resource allocation in sr_init and some cleanups
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include <linux/module.h>
378c2ecf20Sopenharmony_ci#include <linux/fs.h>
388c2ecf20Sopenharmony_ci#include <linux/kernel.h>
398c2ecf20Sopenharmony_ci#include <linux/mm.h>
408c2ecf20Sopenharmony_ci#include <linux/bio.h>
418c2ecf20Sopenharmony_ci#include <linux/compat.h>
428c2ecf20Sopenharmony_ci#include <linux/string.h>
438c2ecf20Sopenharmony_ci#include <linux/errno.h>
448c2ecf20Sopenharmony_ci#include <linux/cdrom.h>
458c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
468c2ecf20Sopenharmony_ci#include <linux/init.h>
478c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
488c2ecf20Sopenharmony_ci#include <linux/blk-pm.h>
498c2ecf20Sopenharmony_ci#include <linux/mutex.h>
508c2ecf20Sopenharmony_ci#include <linux/slab.h>
518c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
528c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#include <scsi/scsi.h>
578c2ecf20Sopenharmony_ci#include <scsi/scsi_dbg.h>
588c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h>
598c2ecf20Sopenharmony_ci#include <scsi/scsi_driver.h>
608c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
618c2ecf20Sopenharmony_ci#include <scsi/scsi_eh.h>
628c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
638c2ecf20Sopenharmony_ci#include <scsi/scsi_ioctl.h>	/* For the door lock/unlock commands */
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#include "scsi_logging.h"
668c2ecf20Sopenharmony_ci#include "sr.h"
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SCSI cdrom (sr) driver");
708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
718c2ecf20Sopenharmony_ciMODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_CDROM_MAJOR);
728c2ecf20Sopenharmony_ciMODULE_ALIAS_SCSI_DEVICE(TYPE_ROM);
738c2ecf20Sopenharmony_ciMODULE_ALIAS_SCSI_DEVICE(TYPE_WORM);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define SR_DISKS	256
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci#define SR_CAPABILITIES \
788c2ecf20Sopenharmony_ci	(CDC_CLOSE_TRAY|CDC_OPEN_TRAY|CDC_LOCK|CDC_SELECT_SPEED| \
798c2ecf20Sopenharmony_ci	 CDC_SELECT_DISC|CDC_MULTI_SESSION|CDC_MCN|CDC_MEDIA_CHANGED| \
808c2ecf20Sopenharmony_ci	 CDC_PLAY_AUDIO|CDC_RESET|CDC_DRIVE_STATUS| \
818c2ecf20Sopenharmony_ci	 CDC_CD_R|CDC_CD_RW|CDC_DVD|CDC_DVD_R|CDC_DVD_RAM|CDC_GENERIC_PACKET| \
828c2ecf20Sopenharmony_ci	 CDC_MRW|CDC_MRW_W|CDC_RAM)
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int sr_probe(struct device *);
858c2ecf20Sopenharmony_cistatic int sr_remove(struct device *);
868c2ecf20Sopenharmony_cistatic blk_status_t sr_init_command(struct scsi_cmnd *SCpnt);
878c2ecf20Sopenharmony_cistatic int sr_done(struct scsi_cmnd *);
888c2ecf20Sopenharmony_cistatic int sr_runtime_suspend(struct device *dev);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic const struct dev_pm_ops sr_pm_ops = {
918c2ecf20Sopenharmony_ci	.runtime_suspend	= sr_runtime_suspend,
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic struct scsi_driver sr_template = {
958c2ecf20Sopenharmony_ci	.gendrv = {
968c2ecf20Sopenharmony_ci		.name   	= "sr",
978c2ecf20Sopenharmony_ci		.owner		= THIS_MODULE,
988c2ecf20Sopenharmony_ci		.probe		= sr_probe,
998c2ecf20Sopenharmony_ci		.remove		= sr_remove,
1008c2ecf20Sopenharmony_ci		.pm		= &sr_pm_ops,
1018c2ecf20Sopenharmony_ci	},
1028c2ecf20Sopenharmony_ci	.init_command		= sr_init_command,
1038c2ecf20Sopenharmony_ci	.done			= sr_done,
1048c2ecf20Sopenharmony_ci};
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic unsigned long sr_index_bits[SR_DISKS / BITS_PER_LONG];
1078c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sr_index_lock);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/* This semaphore is used to mediate the 0->1 reference get in the
1108c2ecf20Sopenharmony_ci * face of object destruction (i.e. we can't allow a get on an
1118c2ecf20Sopenharmony_ci * object after last put) */
1128c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sr_ref_mutex);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic int sr_open(struct cdrom_device_info *, int);
1158c2ecf20Sopenharmony_cistatic void sr_release(struct cdrom_device_info *);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic void get_sectorsize(struct scsi_cd *);
1188c2ecf20Sopenharmony_cistatic void get_capabilities(struct scsi_cd *);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic unsigned int sr_check_events(struct cdrom_device_info *cdi,
1218c2ecf20Sopenharmony_ci				    unsigned int clearing, int slot);
1228c2ecf20Sopenharmony_cistatic int sr_packet(struct cdrom_device_info *, struct packet_command *);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic const struct cdrom_device_ops sr_dops = {
1258c2ecf20Sopenharmony_ci	.open			= sr_open,
1268c2ecf20Sopenharmony_ci	.release	 	= sr_release,
1278c2ecf20Sopenharmony_ci	.drive_status	 	= sr_drive_status,
1288c2ecf20Sopenharmony_ci	.check_events		= sr_check_events,
1298c2ecf20Sopenharmony_ci	.tray_move		= sr_tray_move,
1308c2ecf20Sopenharmony_ci	.lock_door		= sr_lock_door,
1318c2ecf20Sopenharmony_ci	.select_speed		= sr_select_speed,
1328c2ecf20Sopenharmony_ci	.get_last_session	= sr_get_last_session,
1338c2ecf20Sopenharmony_ci	.get_mcn		= sr_get_mcn,
1348c2ecf20Sopenharmony_ci	.reset			= sr_reset,
1358c2ecf20Sopenharmony_ci	.audio_ioctl		= sr_audio_ioctl,
1368c2ecf20Sopenharmony_ci	.capability		= SR_CAPABILITIES,
1378c2ecf20Sopenharmony_ci	.generic_packet		= sr_packet,
1388c2ecf20Sopenharmony_ci};
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void sr_kref_release(struct kref *kref);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic inline struct scsi_cd *scsi_cd(struct gendisk *disk)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	return container_of(disk->private_data, struct scsi_cd, driver);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic int sr_runtime_suspend(struct device *dev)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	struct scsi_cd *cd = dev_get_drvdata(dev);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (!cd)	/* E.g.: runtime suspend following sr_remove() */
1528c2ecf20Sopenharmony_ci		return 0;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (cd->media_present)
1558c2ecf20Sopenharmony_ci		return -EBUSY;
1568c2ecf20Sopenharmony_ci	else
1578c2ecf20Sopenharmony_ci		return 0;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/*
1618c2ecf20Sopenharmony_ci * The get and put routines for the struct scsi_cd.  Note this entity
1628c2ecf20Sopenharmony_ci * has a scsi_device pointer and owns a reference to this.
1638c2ecf20Sopenharmony_ci */
1648c2ecf20Sopenharmony_cistatic inline struct scsi_cd *scsi_cd_get(struct gendisk *disk)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct scsi_cd *cd = NULL;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	mutex_lock(&sr_ref_mutex);
1698c2ecf20Sopenharmony_ci	if (disk->private_data == NULL)
1708c2ecf20Sopenharmony_ci		goto out;
1718c2ecf20Sopenharmony_ci	cd = scsi_cd(disk);
1728c2ecf20Sopenharmony_ci	kref_get(&cd->kref);
1738c2ecf20Sopenharmony_ci	if (scsi_device_get(cd->device)) {
1748c2ecf20Sopenharmony_ci		kref_put(&cd->kref, sr_kref_release);
1758c2ecf20Sopenharmony_ci		cd = NULL;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci out:
1788c2ecf20Sopenharmony_ci	mutex_unlock(&sr_ref_mutex);
1798c2ecf20Sopenharmony_ci	return cd;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic void scsi_cd_put(struct scsi_cd *cd)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct scsi_device *sdev = cd->device;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	mutex_lock(&sr_ref_mutex);
1878c2ecf20Sopenharmony_ci	kref_put(&cd->kref, sr_kref_release);
1888c2ecf20Sopenharmony_ci	scsi_device_put(sdev);
1898c2ecf20Sopenharmony_ci	mutex_unlock(&sr_ref_mutex);
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic unsigned int sr_get_events(struct scsi_device *sdev)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	u8 buf[8];
1958c2ecf20Sopenharmony_ci	u8 cmd[] = { GET_EVENT_STATUS_NOTIFICATION,
1968c2ecf20Sopenharmony_ci		     1,			/* polled */
1978c2ecf20Sopenharmony_ci		     0, 0,		/* reserved */
1988c2ecf20Sopenharmony_ci		     1 << 4,		/* notification class: media */
1998c2ecf20Sopenharmony_ci		     0, 0,		/* reserved */
2008c2ecf20Sopenharmony_ci		     0, sizeof(buf),	/* allocation length */
2018c2ecf20Sopenharmony_ci		     0,			/* control */
2028c2ecf20Sopenharmony_ci	};
2038c2ecf20Sopenharmony_ci	struct event_header *eh = (void *)buf;
2048c2ecf20Sopenharmony_ci	struct media_event_desc *med = (void *)(buf + 4);
2058c2ecf20Sopenharmony_ci	struct scsi_sense_hdr sshdr;
2068c2ecf20Sopenharmony_ci	int result;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, sizeof(buf),
2098c2ecf20Sopenharmony_ci				  &sshdr, SR_TIMEOUT, MAX_RETRIES, NULL);
2108c2ecf20Sopenharmony_ci	if (scsi_sense_valid(&sshdr) && sshdr.sense_key == UNIT_ATTENTION)
2118c2ecf20Sopenharmony_ci		return DISK_EVENT_MEDIA_CHANGE;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (result || be16_to_cpu(eh->data_len) < sizeof(*med))
2148c2ecf20Sopenharmony_ci		return 0;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (eh->nea || eh->notification_class != 0x4)
2178c2ecf20Sopenharmony_ci		return 0;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (med->media_event_code == 1)
2208c2ecf20Sopenharmony_ci		return DISK_EVENT_EJECT_REQUEST;
2218c2ecf20Sopenharmony_ci	else if (med->media_event_code == 2)
2228c2ecf20Sopenharmony_ci		return DISK_EVENT_MEDIA_CHANGE;
2238c2ecf20Sopenharmony_ci	else if (med->media_event_code == 3)
2248c2ecf20Sopenharmony_ci		return DISK_EVENT_MEDIA_CHANGE;
2258c2ecf20Sopenharmony_ci	return 0;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/*
2298c2ecf20Sopenharmony_ci * This function checks to see if the media has been changed or eject
2308c2ecf20Sopenharmony_ci * button has been pressed.  It is possible that we have already
2318c2ecf20Sopenharmony_ci * sensed a change, or the drive may have sensed one and not yet
2328c2ecf20Sopenharmony_ci * reported it.  The past events are accumulated in sdev->changed and
2338c2ecf20Sopenharmony_ci * returned together with the current state.
2348c2ecf20Sopenharmony_ci */
2358c2ecf20Sopenharmony_cistatic unsigned int sr_check_events(struct cdrom_device_info *cdi,
2368c2ecf20Sopenharmony_ci				    unsigned int clearing, int slot)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct scsi_cd *cd = cdi->handle;
2398c2ecf20Sopenharmony_ci	bool last_present;
2408c2ecf20Sopenharmony_ci	struct scsi_sense_hdr sshdr;
2418c2ecf20Sopenharmony_ci	unsigned int events;
2428c2ecf20Sopenharmony_ci	int ret;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/* no changer support */
2458c2ecf20Sopenharmony_ci	if (CDSL_CURRENT != slot)
2468c2ecf20Sopenharmony_ci		return 0;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	events = sr_get_events(cd->device);
2498c2ecf20Sopenharmony_ci	cd->get_event_changed |= events & DISK_EVENT_MEDIA_CHANGE;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/*
2528c2ecf20Sopenharmony_ci	 * If earlier GET_EVENT_STATUS_NOTIFICATION and TUR did not agree
2538c2ecf20Sopenharmony_ci	 * for several times in a row.  We rely on TUR only for this likely
2548c2ecf20Sopenharmony_ci	 * broken device, to prevent generating incorrect media changed
2558c2ecf20Sopenharmony_ci	 * events for every open().
2568c2ecf20Sopenharmony_ci	 */
2578c2ecf20Sopenharmony_ci	if (cd->ignore_get_event) {
2588c2ecf20Sopenharmony_ci		events &= ~DISK_EVENT_MEDIA_CHANGE;
2598c2ecf20Sopenharmony_ci		goto do_tur;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/*
2638c2ecf20Sopenharmony_ci	 * GET_EVENT_STATUS_NOTIFICATION is enough unless MEDIA_CHANGE
2648c2ecf20Sopenharmony_ci	 * is being cleared.  Note that there are devices which hang
2658c2ecf20Sopenharmony_ci	 * if asked to execute TUR repeatedly.
2668c2ecf20Sopenharmony_ci	 */
2678c2ecf20Sopenharmony_ci	if (cd->device->changed) {
2688c2ecf20Sopenharmony_ci		events |= DISK_EVENT_MEDIA_CHANGE;
2698c2ecf20Sopenharmony_ci		cd->device->changed = 0;
2708c2ecf20Sopenharmony_ci		cd->tur_changed = true;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (!(clearing & DISK_EVENT_MEDIA_CHANGE))
2748c2ecf20Sopenharmony_ci		return events;
2758c2ecf20Sopenharmony_cido_tur:
2768c2ecf20Sopenharmony_ci	/* let's see whether the media is there with TUR */
2778c2ecf20Sopenharmony_ci	last_present = cd->media_present;
2788c2ecf20Sopenharmony_ci	ret = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/*
2818c2ecf20Sopenharmony_ci	 * Media is considered to be present if TUR succeeds or fails with
2828c2ecf20Sopenharmony_ci	 * sense data indicating something other than media-not-present
2838c2ecf20Sopenharmony_ci	 * (ASC 0x3a).
2848c2ecf20Sopenharmony_ci	 */
2858c2ecf20Sopenharmony_ci	cd->media_present = scsi_status_is_good(ret) ||
2868c2ecf20Sopenharmony_ci		(scsi_sense_valid(&sshdr) && sshdr.asc != 0x3a);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (last_present != cd->media_present)
2898c2ecf20Sopenharmony_ci		cd->device->changed = 1;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (cd->device->changed) {
2928c2ecf20Sopenharmony_ci		events |= DISK_EVENT_MEDIA_CHANGE;
2938c2ecf20Sopenharmony_ci		cd->device->changed = 0;
2948c2ecf20Sopenharmony_ci		cd->tur_changed = true;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (cd->ignore_get_event)
2988c2ecf20Sopenharmony_ci		return events;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* check whether GET_EVENT is reporting spurious MEDIA_CHANGE */
3018c2ecf20Sopenharmony_ci	if (!cd->tur_changed) {
3028c2ecf20Sopenharmony_ci		if (cd->get_event_changed) {
3038c2ecf20Sopenharmony_ci			if (cd->tur_mismatch++ > 8) {
3048c2ecf20Sopenharmony_ci				sr_printk(KERN_WARNING, cd,
3058c2ecf20Sopenharmony_ci					  "GET_EVENT and TUR disagree continuously, suppress GET_EVENT events\n");
3068c2ecf20Sopenharmony_ci				cd->ignore_get_event = true;
3078c2ecf20Sopenharmony_ci			}
3088c2ecf20Sopenharmony_ci		} else {
3098c2ecf20Sopenharmony_ci			cd->tur_mismatch = 0;
3108c2ecf20Sopenharmony_ci		}
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci	cd->tur_changed = false;
3138c2ecf20Sopenharmony_ci	cd->get_event_changed = false;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return events;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci/*
3198c2ecf20Sopenharmony_ci * sr_done is the interrupt routine for the device driver.
3208c2ecf20Sopenharmony_ci *
3218c2ecf20Sopenharmony_ci * It will be notified on the end of a SCSI read / write, and will take one
3228c2ecf20Sopenharmony_ci * of several actions based on success or failure.
3238c2ecf20Sopenharmony_ci */
3248c2ecf20Sopenharmony_cistatic int sr_done(struct scsi_cmnd *SCpnt)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	int result = SCpnt->result;
3278c2ecf20Sopenharmony_ci	int this_count = scsi_bufflen(SCpnt);
3288c2ecf20Sopenharmony_ci	int good_bytes = (result == 0 ? this_count : 0);
3298c2ecf20Sopenharmony_ci	int block_sectors = 0;
3308c2ecf20Sopenharmony_ci	long error_sector;
3318c2ecf20Sopenharmony_ci	struct scsi_cd *cd = scsi_cd(SCpnt->request->rq_disk);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci#ifdef DEBUG
3348c2ecf20Sopenharmony_ci	scmd_printk(KERN_INFO, SCpnt, "done: %x\n", result);
3358c2ecf20Sopenharmony_ci#endif
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/*
3388c2ecf20Sopenharmony_ci	 * Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial
3398c2ecf20Sopenharmony_ci	 * success.  Since this is a relatively rare error condition, no
3408c2ecf20Sopenharmony_ci	 * care is taken to avoid unnecessary additional work such as
3418c2ecf20Sopenharmony_ci	 * memcpy's that could be avoided.
3428c2ecf20Sopenharmony_ci	 */
3438c2ecf20Sopenharmony_ci	if (driver_byte(result) != 0 &&		/* An error occurred */
3448c2ecf20Sopenharmony_ci	    (SCpnt->sense_buffer[0] & 0x7f) == 0x70) { /* Sense current */
3458c2ecf20Sopenharmony_ci		switch (SCpnt->sense_buffer[2]) {
3468c2ecf20Sopenharmony_ci		case MEDIUM_ERROR:
3478c2ecf20Sopenharmony_ci		case VOLUME_OVERFLOW:
3488c2ecf20Sopenharmony_ci		case ILLEGAL_REQUEST:
3498c2ecf20Sopenharmony_ci			if (!(SCpnt->sense_buffer[0] & 0x90))
3508c2ecf20Sopenharmony_ci				break;
3518c2ecf20Sopenharmony_ci			error_sector =
3528c2ecf20Sopenharmony_ci				get_unaligned_be32(&SCpnt->sense_buffer[3]);
3538c2ecf20Sopenharmony_ci			if (SCpnt->request->bio != NULL)
3548c2ecf20Sopenharmony_ci				block_sectors =
3558c2ecf20Sopenharmony_ci					bio_sectors(SCpnt->request->bio);
3568c2ecf20Sopenharmony_ci			if (block_sectors < 4)
3578c2ecf20Sopenharmony_ci				block_sectors = 4;
3588c2ecf20Sopenharmony_ci			if (cd->device->sector_size == 2048)
3598c2ecf20Sopenharmony_ci				error_sector <<= 2;
3608c2ecf20Sopenharmony_ci			error_sector &= ~(block_sectors - 1);
3618c2ecf20Sopenharmony_ci			good_bytes = (error_sector -
3628c2ecf20Sopenharmony_ci				      blk_rq_pos(SCpnt->request)) << 9;
3638c2ecf20Sopenharmony_ci			if (good_bytes < 0 || good_bytes >= this_count)
3648c2ecf20Sopenharmony_ci				good_bytes = 0;
3658c2ecf20Sopenharmony_ci			/*
3668c2ecf20Sopenharmony_ci			 * The SCSI specification allows for the value
3678c2ecf20Sopenharmony_ci			 * returned by READ CAPACITY to be up to 75 2K
3688c2ecf20Sopenharmony_ci			 * sectors past the last readable block.
3698c2ecf20Sopenharmony_ci			 * Therefore, if we hit a medium error within the
3708c2ecf20Sopenharmony_ci			 * last 75 2K sectors, we decrease the saved size
3718c2ecf20Sopenharmony_ci			 * value.
3728c2ecf20Sopenharmony_ci			 */
3738c2ecf20Sopenharmony_ci			if (error_sector < get_capacity(cd->disk) &&
3748c2ecf20Sopenharmony_ci			    cd->capacity - error_sector < 4 * 75)
3758c2ecf20Sopenharmony_ci				set_capacity(cd->disk, error_sector);
3768c2ecf20Sopenharmony_ci			break;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		case RECOVERED_ERROR:
3798c2ecf20Sopenharmony_ci			good_bytes = this_count;
3808c2ecf20Sopenharmony_ci			break;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci		default:
3838c2ecf20Sopenharmony_ci			break;
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	return good_bytes;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic blk_status_t sr_init_command(struct scsi_cmnd *SCpnt)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	int block = 0, this_count, s_size;
3938c2ecf20Sopenharmony_ci	struct scsi_cd *cd;
3948c2ecf20Sopenharmony_ci	struct request *rq = SCpnt->request;
3958c2ecf20Sopenharmony_ci	blk_status_t ret;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	ret = scsi_alloc_sgtables(SCpnt);
3988c2ecf20Sopenharmony_ci	if (ret != BLK_STS_OK)
3998c2ecf20Sopenharmony_ci		return ret;
4008c2ecf20Sopenharmony_ci	cd = scsi_cd(rq->rq_disk);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	SCSI_LOG_HLQUEUE(1, scmd_printk(KERN_INFO, SCpnt,
4038c2ecf20Sopenharmony_ci		"Doing sr request, block = %d\n", block));
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (!cd->device || !scsi_device_online(cd->device)) {
4068c2ecf20Sopenharmony_ci		SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
4078c2ecf20Sopenharmony_ci			"Finishing %u sectors\n", blk_rq_sectors(rq)));
4088c2ecf20Sopenharmony_ci		SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
4098c2ecf20Sopenharmony_ci			"Retry with 0x%p\n", SCpnt));
4108c2ecf20Sopenharmony_ci		goto out;
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (cd->device->changed) {
4148c2ecf20Sopenharmony_ci		/*
4158c2ecf20Sopenharmony_ci		 * quietly refuse to do anything to a changed disc until the
4168c2ecf20Sopenharmony_ci		 * changed bit has been reset
4178c2ecf20Sopenharmony_ci		 */
4188c2ecf20Sopenharmony_ci		goto out;
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/*
4228c2ecf20Sopenharmony_ci	 * we do lazy blocksize switching (when reading XA sectors,
4238c2ecf20Sopenharmony_ci	 * see CDROMREADMODE2 ioctl)
4248c2ecf20Sopenharmony_ci	 */
4258c2ecf20Sopenharmony_ci	s_size = cd->device->sector_size;
4268c2ecf20Sopenharmony_ci	if (s_size > 2048) {
4278c2ecf20Sopenharmony_ci		if (!in_interrupt())
4288c2ecf20Sopenharmony_ci			sr_set_blocklength(cd, 2048);
4298c2ecf20Sopenharmony_ci		else
4308c2ecf20Sopenharmony_ci			scmd_printk(KERN_INFO, SCpnt,
4318c2ecf20Sopenharmony_ci				    "can't switch blocksize: in interrupt\n");
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (s_size != 512 && s_size != 1024 && s_size != 2048) {
4358c2ecf20Sopenharmony_ci		scmd_printk(KERN_ERR, SCpnt, "bad sector size %d\n", s_size);
4368c2ecf20Sopenharmony_ci		goto out;
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	switch (req_op(rq)) {
4408c2ecf20Sopenharmony_ci	case REQ_OP_WRITE:
4418c2ecf20Sopenharmony_ci		if (!cd->writeable)
4428c2ecf20Sopenharmony_ci			goto out;
4438c2ecf20Sopenharmony_ci		SCpnt->cmnd[0] = WRITE_10;
4448c2ecf20Sopenharmony_ci		cd->cdi.media_written = 1;
4458c2ecf20Sopenharmony_ci		break;
4468c2ecf20Sopenharmony_ci	case REQ_OP_READ:
4478c2ecf20Sopenharmony_ci		SCpnt->cmnd[0] = READ_10;
4488c2ecf20Sopenharmony_ci		break;
4498c2ecf20Sopenharmony_ci	default:
4508c2ecf20Sopenharmony_ci		blk_dump_rq_flags(rq, "Unknown sr command");
4518c2ecf20Sopenharmony_ci		goto out;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	{
4558c2ecf20Sopenharmony_ci		struct scatterlist *sg;
4568c2ecf20Sopenharmony_ci		int i, size = 0, sg_count = scsi_sg_count(SCpnt);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		scsi_for_each_sg(SCpnt, sg, sg_count, i)
4598c2ecf20Sopenharmony_ci			size += sg->length;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci		if (size != scsi_bufflen(SCpnt)) {
4628c2ecf20Sopenharmony_ci			scmd_printk(KERN_ERR, SCpnt,
4638c2ecf20Sopenharmony_ci				"mismatch count %d, bytes %d\n",
4648c2ecf20Sopenharmony_ci				size, scsi_bufflen(SCpnt));
4658c2ecf20Sopenharmony_ci			if (scsi_bufflen(SCpnt) > size)
4668c2ecf20Sopenharmony_ci				SCpnt->sdb.length = size;
4678c2ecf20Sopenharmony_ci		}
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/*
4718c2ecf20Sopenharmony_ci	 * request doesn't start on hw block boundary, add scatter pads
4728c2ecf20Sopenharmony_ci	 */
4738c2ecf20Sopenharmony_ci	if (((unsigned int)blk_rq_pos(rq) % (s_size >> 9)) ||
4748c2ecf20Sopenharmony_ci	    (scsi_bufflen(SCpnt) % s_size)) {
4758c2ecf20Sopenharmony_ci		scmd_printk(KERN_NOTICE, SCpnt, "unaligned transfer\n");
4768c2ecf20Sopenharmony_ci		goto out;
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	this_count = (scsi_bufflen(SCpnt) >> 9) / (s_size >> 9);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
4838c2ecf20Sopenharmony_ci					"%s %d/%u 512 byte blocks.\n",
4848c2ecf20Sopenharmony_ci					(rq_data_dir(rq) == WRITE) ?
4858c2ecf20Sopenharmony_ci					"writing" : "reading",
4868c2ecf20Sopenharmony_ci					this_count, blk_rq_sectors(rq)));
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	SCpnt->cmnd[1] = 0;
4898c2ecf20Sopenharmony_ci	block = (unsigned int)blk_rq_pos(rq) / (s_size >> 9);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (this_count > 0xffff) {
4928c2ecf20Sopenharmony_ci		this_count = 0xffff;
4938c2ecf20Sopenharmony_ci		SCpnt->sdb.length = this_count * s_size;
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	put_unaligned_be32(block, &SCpnt->cmnd[2]);
4978c2ecf20Sopenharmony_ci	SCpnt->cmnd[6] = SCpnt->cmnd[9] = 0;
4988c2ecf20Sopenharmony_ci	put_unaligned_be16(this_count, &SCpnt->cmnd[7]);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	/*
5018c2ecf20Sopenharmony_ci	 * We shouldn't disconnect in the middle of a sector, so with a dumb
5028c2ecf20Sopenharmony_ci	 * host adapter, it's safe to assume that we can at least transfer
5038c2ecf20Sopenharmony_ci	 * this many bytes between each connect / disconnect.
5048c2ecf20Sopenharmony_ci	 */
5058c2ecf20Sopenharmony_ci	SCpnt->transfersize = cd->device->sector_size;
5068c2ecf20Sopenharmony_ci	SCpnt->underflow = this_count << 9;
5078c2ecf20Sopenharmony_ci	SCpnt->allowed = MAX_RETRIES;
5088c2ecf20Sopenharmony_ci	SCpnt->cmd_len = 10;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	/*
5118c2ecf20Sopenharmony_ci	 * This indicates that the command is ready from our end to be queued.
5128c2ecf20Sopenharmony_ci	 */
5138c2ecf20Sopenharmony_ci	return BLK_STS_OK;
5148c2ecf20Sopenharmony_ci out:
5158c2ecf20Sopenharmony_ci	scsi_free_sgtables(SCpnt);
5168c2ecf20Sopenharmony_ci	return BLK_STS_IOERR;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic void sr_revalidate_disk(struct scsi_cd *cd)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct scsi_sense_hdr sshdr;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	/* if the unit is not ready, nothing more to do */
5248c2ecf20Sopenharmony_ci	if (scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr))
5258c2ecf20Sopenharmony_ci		return;
5268c2ecf20Sopenharmony_ci	sr_cd_check(&cd->cdi);
5278c2ecf20Sopenharmony_ci	get_sectorsize(cd);
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic int sr_block_open(struct block_device *bdev, fmode_t mode)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	struct scsi_cd *cd;
5338c2ecf20Sopenharmony_ci	struct scsi_device *sdev;
5348c2ecf20Sopenharmony_ci	int ret = -ENXIO;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	cd = scsi_cd_get(bdev->bd_disk);
5378c2ecf20Sopenharmony_ci	if (!cd)
5388c2ecf20Sopenharmony_ci		goto out;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	sdev = cd->device;
5418c2ecf20Sopenharmony_ci	scsi_autopm_get_device(sdev);
5428c2ecf20Sopenharmony_ci	if (bdev_check_media_change(bdev))
5438c2ecf20Sopenharmony_ci		sr_revalidate_disk(cd);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	mutex_lock(&cd->lock);
5468c2ecf20Sopenharmony_ci	ret = cdrom_open(&cd->cdi, bdev, mode);
5478c2ecf20Sopenharmony_ci	mutex_unlock(&cd->lock);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	scsi_autopm_put_device(sdev);
5508c2ecf20Sopenharmony_ci	if (ret)
5518c2ecf20Sopenharmony_ci		scsi_cd_put(cd);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ciout:
5548c2ecf20Sopenharmony_ci	return ret;
5558c2ecf20Sopenharmony_ci}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cistatic void sr_block_release(struct gendisk *disk, fmode_t mode)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	struct scsi_cd *cd = scsi_cd(disk);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	mutex_lock(&cd->lock);
5628c2ecf20Sopenharmony_ci	cdrom_release(&cd->cdi, mode);
5638c2ecf20Sopenharmony_ci	mutex_unlock(&cd->lock);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	scsi_cd_put(cd);
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
5698c2ecf20Sopenharmony_ci			  unsigned long arg)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	struct scsi_cd *cd = scsi_cd(bdev->bd_disk);
5728c2ecf20Sopenharmony_ci	struct scsi_device *sdev = cd->device;
5738c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
5748c2ecf20Sopenharmony_ci	int ret;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	mutex_lock(&cd->lock);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	ret = scsi_ioctl_block_when_processing_errors(sdev, cmd,
5798c2ecf20Sopenharmony_ci			(mode & FMODE_NDELAY) != 0);
5808c2ecf20Sopenharmony_ci	if (ret)
5818c2ecf20Sopenharmony_ci		goto out;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	scsi_autopm_get_device(sdev);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	/*
5868c2ecf20Sopenharmony_ci	 * Send SCSI addressing ioctls directly to mid level, send other
5878c2ecf20Sopenharmony_ci	 * ioctls to cdrom/block level.
5888c2ecf20Sopenharmony_ci	 */
5898c2ecf20Sopenharmony_ci	switch (cmd) {
5908c2ecf20Sopenharmony_ci	case SCSI_IOCTL_GET_IDLUN:
5918c2ecf20Sopenharmony_ci	case SCSI_IOCTL_GET_BUS_NUMBER:
5928c2ecf20Sopenharmony_ci		ret = scsi_ioctl(sdev, cmd, argp);
5938c2ecf20Sopenharmony_ci		goto put;
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	ret = cdrom_ioctl(&cd->cdi, bdev, mode, cmd, arg);
5978c2ecf20Sopenharmony_ci	if (ret != -ENOSYS)
5988c2ecf20Sopenharmony_ci		goto put;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	ret = scsi_ioctl(sdev, cmd, argp);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ciput:
6038c2ecf20Sopenharmony_ci	scsi_autopm_put_device(sdev);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ciout:
6068c2ecf20Sopenharmony_ci	mutex_unlock(&cd->lock);
6078c2ecf20Sopenharmony_ci	return ret;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
6118c2ecf20Sopenharmony_cistatic int sr_block_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
6128c2ecf20Sopenharmony_ci			  unsigned long arg)
6138c2ecf20Sopenharmony_ci{
6148c2ecf20Sopenharmony_ci	struct scsi_cd *cd = scsi_cd(bdev->bd_disk);
6158c2ecf20Sopenharmony_ci	struct scsi_device *sdev = cd->device;
6168c2ecf20Sopenharmony_ci	void __user *argp = compat_ptr(arg);
6178c2ecf20Sopenharmony_ci	int ret;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	mutex_lock(&cd->lock);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	ret = scsi_ioctl_block_when_processing_errors(sdev, cmd,
6228c2ecf20Sopenharmony_ci			(mode & FMODE_NDELAY) != 0);
6238c2ecf20Sopenharmony_ci	if (ret)
6248c2ecf20Sopenharmony_ci		goto out;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	scsi_autopm_get_device(sdev);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	/*
6298c2ecf20Sopenharmony_ci	 * Send SCSI addressing ioctls directly to mid level, send other
6308c2ecf20Sopenharmony_ci	 * ioctls to cdrom/block level.
6318c2ecf20Sopenharmony_ci	 */
6328c2ecf20Sopenharmony_ci	switch (cmd) {
6338c2ecf20Sopenharmony_ci	case SCSI_IOCTL_GET_IDLUN:
6348c2ecf20Sopenharmony_ci	case SCSI_IOCTL_GET_BUS_NUMBER:
6358c2ecf20Sopenharmony_ci		ret = scsi_compat_ioctl(sdev, cmd, argp);
6368c2ecf20Sopenharmony_ci		goto put;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	ret = cdrom_ioctl(&cd->cdi, bdev, mode, cmd, (unsigned long)argp);
6408c2ecf20Sopenharmony_ci	if (ret != -ENOSYS)
6418c2ecf20Sopenharmony_ci		goto put;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	ret = scsi_compat_ioctl(sdev, cmd, argp);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ciput:
6468c2ecf20Sopenharmony_ci	scsi_autopm_put_device(sdev);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ciout:
6498c2ecf20Sopenharmony_ci	mutex_unlock(&cd->lock);
6508c2ecf20Sopenharmony_ci	return ret;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci#endif
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_cistatic unsigned int sr_block_check_events(struct gendisk *disk,
6568c2ecf20Sopenharmony_ci					  unsigned int clearing)
6578c2ecf20Sopenharmony_ci{
6588c2ecf20Sopenharmony_ci	unsigned int ret = 0;
6598c2ecf20Sopenharmony_ci	struct scsi_cd *cd;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	cd = scsi_cd_get(disk);
6628c2ecf20Sopenharmony_ci	if (!cd)
6638c2ecf20Sopenharmony_ci		return 0;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	if (!atomic_read(&cd->device->disk_events_disable_depth))
6668c2ecf20Sopenharmony_ci		ret = cdrom_check_events(&cd->cdi, clearing);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	scsi_cd_put(cd);
6698c2ecf20Sopenharmony_ci	return ret;
6708c2ecf20Sopenharmony_ci}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cistatic const struct block_device_operations sr_bdops =
6738c2ecf20Sopenharmony_ci{
6748c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
6758c2ecf20Sopenharmony_ci	.open		= sr_block_open,
6768c2ecf20Sopenharmony_ci	.release	= sr_block_release,
6778c2ecf20Sopenharmony_ci	.ioctl		= sr_block_ioctl,
6788c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
6798c2ecf20Sopenharmony_ci	.compat_ioctl	= sr_block_compat_ioctl,
6808c2ecf20Sopenharmony_ci#endif
6818c2ecf20Sopenharmony_ci	.check_events	= sr_block_check_events,
6828c2ecf20Sopenharmony_ci};
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_cistatic int sr_open(struct cdrom_device_info *cdi, int purpose)
6858c2ecf20Sopenharmony_ci{
6868c2ecf20Sopenharmony_ci	struct scsi_cd *cd = cdi->handle;
6878c2ecf20Sopenharmony_ci	struct scsi_device *sdev = cd->device;
6888c2ecf20Sopenharmony_ci	int retval;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	/*
6918c2ecf20Sopenharmony_ci	 * If the device is in error recovery, wait until it is done.
6928c2ecf20Sopenharmony_ci	 * If the device is offline, then disallow any access to it.
6938c2ecf20Sopenharmony_ci	 */
6948c2ecf20Sopenharmony_ci	retval = -ENXIO;
6958c2ecf20Sopenharmony_ci	if (!scsi_block_when_processing_errors(sdev))
6968c2ecf20Sopenharmony_ci		goto error_out;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	return 0;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cierror_out:
7018c2ecf20Sopenharmony_ci	return retval;
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_cistatic void sr_release(struct cdrom_device_info *cdi)
7058c2ecf20Sopenharmony_ci{
7068c2ecf20Sopenharmony_ci	struct scsi_cd *cd = cdi->handle;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	if (cd->device->sector_size > 2048)
7098c2ecf20Sopenharmony_ci		sr_set_blocklength(cd, 2048);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic int sr_probe(struct device *dev)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct scsi_device *sdev = to_scsi_device(dev);
7168c2ecf20Sopenharmony_ci	struct gendisk *disk;
7178c2ecf20Sopenharmony_ci	struct scsi_cd *cd;
7188c2ecf20Sopenharmony_ci	int minor, error;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	scsi_autopm_get_device(sdev);
7218c2ecf20Sopenharmony_ci	error = -ENODEV;
7228c2ecf20Sopenharmony_ci	if (sdev->type != TYPE_ROM && sdev->type != TYPE_WORM)
7238c2ecf20Sopenharmony_ci		goto fail;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	error = -ENOMEM;
7268c2ecf20Sopenharmony_ci	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
7278c2ecf20Sopenharmony_ci	if (!cd)
7288c2ecf20Sopenharmony_ci		goto fail;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	kref_init(&cd->kref);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	disk = alloc_disk(1);
7338c2ecf20Sopenharmony_ci	if (!disk)
7348c2ecf20Sopenharmony_ci		goto fail_free;
7358c2ecf20Sopenharmony_ci	mutex_init(&cd->lock);
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	spin_lock(&sr_index_lock);
7388c2ecf20Sopenharmony_ci	minor = find_first_zero_bit(sr_index_bits, SR_DISKS);
7398c2ecf20Sopenharmony_ci	if (minor == SR_DISKS) {
7408c2ecf20Sopenharmony_ci		spin_unlock(&sr_index_lock);
7418c2ecf20Sopenharmony_ci		error = -EBUSY;
7428c2ecf20Sopenharmony_ci		goto fail_put;
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci	__set_bit(minor, sr_index_bits);
7458c2ecf20Sopenharmony_ci	spin_unlock(&sr_index_lock);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	disk->major = SCSI_CDROM_MAJOR;
7488c2ecf20Sopenharmony_ci	disk->first_minor = minor;
7498c2ecf20Sopenharmony_ci	sprintf(disk->disk_name, "sr%d", minor);
7508c2ecf20Sopenharmony_ci	disk->fops = &sr_bdops;
7518c2ecf20Sopenharmony_ci	disk->flags = GENHD_FL_CD | GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE;
7528c2ecf20Sopenharmony_ci	disk->events = DISK_EVENT_MEDIA_CHANGE | DISK_EVENT_EJECT_REQUEST;
7538c2ecf20Sopenharmony_ci	disk->event_flags = DISK_EVENT_FLAG_POLL | DISK_EVENT_FLAG_UEVENT;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	blk_queue_rq_timeout(sdev->request_queue, SR_TIMEOUT);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	cd->device = sdev;
7588c2ecf20Sopenharmony_ci	cd->disk = disk;
7598c2ecf20Sopenharmony_ci	cd->driver = &sr_template;
7608c2ecf20Sopenharmony_ci	cd->disk = disk;
7618c2ecf20Sopenharmony_ci	cd->capacity = 0x1fffff;
7628c2ecf20Sopenharmony_ci	cd->device->changed = 1;	/* force recheck CD type */
7638c2ecf20Sopenharmony_ci	cd->media_present = 1;
7648c2ecf20Sopenharmony_ci	cd->use = 1;
7658c2ecf20Sopenharmony_ci	cd->readcd_known = 0;
7668c2ecf20Sopenharmony_ci	cd->readcd_cdda = 0;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	cd->cdi.ops = &sr_dops;
7698c2ecf20Sopenharmony_ci	cd->cdi.handle = cd;
7708c2ecf20Sopenharmony_ci	cd->cdi.mask = 0;
7718c2ecf20Sopenharmony_ci	cd->cdi.capacity = 1;
7728c2ecf20Sopenharmony_ci	sprintf(cd->cdi.name, "sr%d", minor);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	sdev->sector_size = 2048;	/* A guess, just in case */
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* FIXME: need to handle a get_capabilities failure properly ?? */
7778c2ecf20Sopenharmony_ci	get_capabilities(cd);
7788c2ecf20Sopenharmony_ci	sr_vendor_init(cd);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	set_capacity(disk, cd->capacity);
7818c2ecf20Sopenharmony_ci	disk->private_data = &cd->driver;
7828c2ecf20Sopenharmony_ci	disk->queue = sdev->request_queue;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	if (register_cdrom(disk, &cd->cdi))
7858c2ecf20Sopenharmony_ci		goto fail_minor;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	/*
7888c2ecf20Sopenharmony_ci	 * Initialize block layer runtime PM stuffs before the
7898c2ecf20Sopenharmony_ci	 * periodic event checking request gets started in add_disk.
7908c2ecf20Sopenharmony_ci	 */
7918c2ecf20Sopenharmony_ci	blk_pm_runtime_init(sdev->request_queue, dev);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, cd);
7948c2ecf20Sopenharmony_ci	disk->flags |= GENHD_FL_REMOVABLE;
7958c2ecf20Sopenharmony_ci	sr_revalidate_disk(cd);
7968c2ecf20Sopenharmony_ci	device_add_disk(&sdev->sdev_gendev, disk, NULL);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	sdev_printk(KERN_DEBUG, sdev,
7998c2ecf20Sopenharmony_ci		    "Attached scsi CD-ROM %s\n", cd->cdi.name);
8008c2ecf20Sopenharmony_ci	scsi_autopm_put_device(cd->device);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	return 0;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cifail_minor:
8058c2ecf20Sopenharmony_ci	spin_lock(&sr_index_lock);
8068c2ecf20Sopenharmony_ci	clear_bit(minor, sr_index_bits);
8078c2ecf20Sopenharmony_ci	spin_unlock(&sr_index_lock);
8088c2ecf20Sopenharmony_cifail_put:
8098c2ecf20Sopenharmony_ci	put_disk(disk);
8108c2ecf20Sopenharmony_ci	mutex_destroy(&cd->lock);
8118c2ecf20Sopenharmony_cifail_free:
8128c2ecf20Sopenharmony_ci	kfree(cd);
8138c2ecf20Sopenharmony_cifail:
8148c2ecf20Sopenharmony_ci	scsi_autopm_put_device(sdev);
8158c2ecf20Sopenharmony_ci	return error;
8168c2ecf20Sopenharmony_ci}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cistatic void get_sectorsize(struct scsi_cd *cd)
8208c2ecf20Sopenharmony_ci{
8218c2ecf20Sopenharmony_ci	unsigned char cmd[10];
8228c2ecf20Sopenharmony_ci	unsigned char buffer[8];
8238c2ecf20Sopenharmony_ci	int the_result, retries = 3;
8248c2ecf20Sopenharmony_ci	int sector_size;
8258c2ecf20Sopenharmony_ci	struct request_queue *queue;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	do {
8288c2ecf20Sopenharmony_ci		cmd[0] = READ_CAPACITY;
8298c2ecf20Sopenharmony_ci		memset((void *) &cmd[1], 0, 9);
8308c2ecf20Sopenharmony_ci		memset(buffer, 0, sizeof(buffer));
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci		/* Do the command and wait.. */
8338c2ecf20Sopenharmony_ci		the_result = scsi_execute_req(cd->device, cmd, DMA_FROM_DEVICE,
8348c2ecf20Sopenharmony_ci					      buffer, sizeof(buffer), NULL,
8358c2ecf20Sopenharmony_ci					      SR_TIMEOUT, MAX_RETRIES, NULL);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci		retries--;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	} while (the_result && retries);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	if (the_result) {
8438c2ecf20Sopenharmony_ci		cd->capacity = 0x1fffff;
8448c2ecf20Sopenharmony_ci		sector_size = 2048;	/* A guess, just in case */
8458c2ecf20Sopenharmony_ci	} else {
8468c2ecf20Sopenharmony_ci		long last_written;
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci		cd->capacity = 1 + get_unaligned_be32(&buffer[0]);
8498c2ecf20Sopenharmony_ci		/*
8508c2ecf20Sopenharmony_ci		 * READ_CAPACITY doesn't return the correct size on
8518c2ecf20Sopenharmony_ci		 * certain UDF media.  If last_written is larger, use
8528c2ecf20Sopenharmony_ci		 * it instead.
8538c2ecf20Sopenharmony_ci		 *
8548c2ecf20Sopenharmony_ci		 * http://bugzilla.kernel.org/show_bug.cgi?id=9668
8558c2ecf20Sopenharmony_ci		 */
8568c2ecf20Sopenharmony_ci		if (!cdrom_get_last_written(&cd->cdi, &last_written))
8578c2ecf20Sopenharmony_ci			cd->capacity = max_t(long, cd->capacity, last_written);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci		sector_size = get_unaligned_be32(&buffer[4]);
8608c2ecf20Sopenharmony_ci		switch (sector_size) {
8618c2ecf20Sopenharmony_ci			/*
8628c2ecf20Sopenharmony_ci			 * HP 4020i CD-Recorder reports 2340 byte sectors
8638c2ecf20Sopenharmony_ci			 * Philips CD-Writers report 2352 byte sectors
8648c2ecf20Sopenharmony_ci			 *
8658c2ecf20Sopenharmony_ci			 * Use 2k sectors for them..
8668c2ecf20Sopenharmony_ci			 */
8678c2ecf20Sopenharmony_ci		case 0:
8688c2ecf20Sopenharmony_ci		case 2340:
8698c2ecf20Sopenharmony_ci		case 2352:
8708c2ecf20Sopenharmony_ci			sector_size = 2048;
8718c2ecf20Sopenharmony_ci			fallthrough;
8728c2ecf20Sopenharmony_ci		case 2048:
8738c2ecf20Sopenharmony_ci			cd->capacity *= 4;
8748c2ecf20Sopenharmony_ci			fallthrough;
8758c2ecf20Sopenharmony_ci		case 512:
8768c2ecf20Sopenharmony_ci			break;
8778c2ecf20Sopenharmony_ci		default:
8788c2ecf20Sopenharmony_ci			sr_printk(KERN_INFO, cd,
8798c2ecf20Sopenharmony_ci				  "unsupported sector size %d.", sector_size);
8808c2ecf20Sopenharmony_ci			cd->capacity = 0;
8818c2ecf20Sopenharmony_ci		}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci		cd->device->sector_size = sector_size;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci		/*
8868c2ecf20Sopenharmony_ci		 * Add this so that we have the ability to correctly gauge
8878c2ecf20Sopenharmony_ci		 * what the device is capable of.
8888c2ecf20Sopenharmony_ci		 */
8898c2ecf20Sopenharmony_ci		set_capacity(cd->disk, cd->capacity);
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	queue = cd->device->request_queue;
8938c2ecf20Sopenharmony_ci	blk_queue_logical_block_size(queue, sector_size);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	return;
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cistatic void get_capabilities(struct scsi_cd *cd)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	unsigned char *buffer;
9018c2ecf20Sopenharmony_ci	struct scsi_mode_data data;
9028c2ecf20Sopenharmony_ci	struct scsi_sense_hdr sshdr;
9038c2ecf20Sopenharmony_ci	unsigned int ms_len = 128;
9048c2ecf20Sopenharmony_ci	int rc, n;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	static const char *loadmech[] =
9078c2ecf20Sopenharmony_ci	{
9088c2ecf20Sopenharmony_ci		"caddy",
9098c2ecf20Sopenharmony_ci		"tray",
9108c2ecf20Sopenharmony_ci		"pop-up",
9118c2ecf20Sopenharmony_ci		"",
9128c2ecf20Sopenharmony_ci		"changer",
9138c2ecf20Sopenharmony_ci		"cartridge changer",
9148c2ecf20Sopenharmony_ci		"",
9158c2ecf20Sopenharmony_ci		""
9168c2ecf20Sopenharmony_ci	};
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	/* allocate transfer buffer */
9208c2ecf20Sopenharmony_ci	buffer = kmalloc(512, GFP_KERNEL);
9218c2ecf20Sopenharmony_ci	if (!buffer) {
9228c2ecf20Sopenharmony_ci		sr_printk(KERN_ERR, cd, "out of memory.\n");
9238c2ecf20Sopenharmony_ci		return;
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	/* eat unit attentions */
9278c2ecf20Sopenharmony_ci	scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	/* ask for mode page 0x2a */
9308c2ecf20Sopenharmony_ci	rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, ms_len,
9318c2ecf20Sopenharmony_ci			     SR_TIMEOUT, 3, &data, NULL);
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	if (rc < 0 || data.length > ms_len ||
9348c2ecf20Sopenharmony_ci	    data.header_length + data.block_descriptor_length > data.length) {
9358c2ecf20Sopenharmony_ci		/* failed, drive doesn't have capabilities mode page */
9368c2ecf20Sopenharmony_ci		cd->cdi.speed = 1;
9378c2ecf20Sopenharmony_ci		cd->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R |
9388c2ecf20Sopenharmony_ci				 CDC_DVD | CDC_DVD_RAM |
9398c2ecf20Sopenharmony_ci				 CDC_SELECT_DISC | CDC_SELECT_SPEED |
9408c2ecf20Sopenharmony_ci				 CDC_MRW | CDC_MRW_W | CDC_RAM);
9418c2ecf20Sopenharmony_ci		kfree(buffer);
9428c2ecf20Sopenharmony_ci		sr_printk(KERN_INFO, cd, "scsi-1 drive");
9438c2ecf20Sopenharmony_ci		return;
9448c2ecf20Sopenharmony_ci	}
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	n = data.header_length + data.block_descriptor_length;
9478c2ecf20Sopenharmony_ci	cd->cdi.speed = get_unaligned_be16(&buffer[n + 8]) / 176;
9488c2ecf20Sopenharmony_ci	cd->readcd_known = 1;
9498c2ecf20Sopenharmony_ci	cd->readcd_cdda = buffer[n + 5] & 0x01;
9508c2ecf20Sopenharmony_ci	/* print some capability bits */
9518c2ecf20Sopenharmony_ci	sr_printk(KERN_INFO, cd,
9528c2ecf20Sopenharmony_ci		  "scsi3-mmc drive: %dx/%dx %s%s%s%s%s%s\n",
9538c2ecf20Sopenharmony_ci		  get_unaligned_be16(&buffer[n + 14]) / 176,
9548c2ecf20Sopenharmony_ci		  cd->cdi.speed,
9558c2ecf20Sopenharmony_ci		  buffer[n + 3] & 0x01 ? "writer " : "", /* CD Writer */
9568c2ecf20Sopenharmony_ci		  buffer[n + 3] & 0x20 ? "dvd-ram " : "",
9578c2ecf20Sopenharmony_ci		  buffer[n + 2] & 0x02 ? "cd/rw " : "", /* can read rewriteable */
9588c2ecf20Sopenharmony_ci		  buffer[n + 4] & 0x20 ? "xa/form2 " : "",	/* can read xa/from2 */
9598c2ecf20Sopenharmony_ci		  buffer[n + 5] & 0x01 ? "cdda " : "", /* can read audio data */
9608c2ecf20Sopenharmony_ci		  loadmech[buffer[n + 6] >> 5]);
9618c2ecf20Sopenharmony_ci	if ((buffer[n + 6] >> 5) == 0)
9628c2ecf20Sopenharmony_ci		/* caddy drives can't close tray... */
9638c2ecf20Sopenharmony_ci		cd->cdi.mask |= CDC_CLOSE_TRAY;
9648c2ecf20Sopenharmony_ci	if ((buffer[n + 2] & 0x8) == 0)
9658c2ecf20Sopenharmony_ci		/* not a DVD drive */
9668c2ecf20Sopenharmony_ci		cd->cdi.mask |= CDC_DVD;
9678c2ecf20Sopenharmony_ci	if ((buffer[n + 3] & 0x20) == 0)
9688c2ecf20Sopenharmony_ci		/* can't write DVD-RAM media */
9698c2ecf20Sopenharmony_ci		cd->cdi.mask |= CDC_DVD_RAM;
9708c2ecf20Sopenharmony_ci	if ((buffer[n + 3] & 0x10) == 0)
9718c2ecf20Sopenharmony_ci		/* can't write DVD-R media */
9728c2ecf20Sopenharmony_ci		cd->cdi.mask |= CDC_DVD_R;
9738c2ecf20Sopenharmony_ci	if ((buffer[n + 3] & 0x2) == 0)
9748c2ecf20Sopenharmony_ci		/* can't write CD-RW media */
9758c2ecf20Sopenharmony_ci		cd->cdi.mask |= CDC_CD_RW;
9768c2ecf20Sopenharmony_ci	if ((buffer[n + 3] & 0x1) == 0)
9778c2ecf20Sopenharmony_ci		/* can't write CD-R media */
9788c2ecf20Sopenharmony_ci		cd->cdi.mask |= CDC_CD_R;
9798c2ecf20Sopenharmony_ci	if ((buffer[n + 6] & 0x8) == 0)
9808c2ecf20Sopenharmony_ci		/* can't eject */
9818c2ecf20Sopenharmony_ci		cd->cdi.mask |= CDC_OPEN_TRAY;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	if ((buffer[n + 6] >> 5) == mechtype_individual_changer ||
9848c2ecf20Sopenharmony_ci	    (buffer[n + 6] >> 5) == mechtype_cartridge_changer)
9858c2ecf20Sopenharmony_ci		cd->cdi.capacity =
9868c2ecf20Sopenharmony_ci		    cdrom_number_of_slots(&cd->cdi);
9878c2ecf20Sopenharmony_ci	if (cd->cdi.capacity <= 1)
9888c2ecf20Sopenharmony_ci		/* not a changer */
9898c2ecf20Sopenharmony_ci		cd->cdi.mask |= CDC_SELECT_DISC;
9908c2ecf20Sopenharmony_ci	/*else    I don't think it can close its tray
9918c2ecf20Sopenharmony_ci		cd->cdi.mask |= CDC_CLOSE_TRAY; */
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	/*
9948c2ecf20Sopenharmony_ci	 * if DVD-RAM, MRW-W or CD-RW, we are randomly writable
9958c2ecf20Sopenharmony_ci	 */
9968c2ecf20Sopenharmony_ci	if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) !=
9978c2ecf20Sopenharmony_ci			(CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) {
9988c2ecf20Sopenharmony_ci		cd->writeable = 1;
9998c2ecf20Sopenharmony_ci	}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	kfree(buffer);
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci/*
10058c2ecf20Sopenharmony_ci * sr_packet() is the entry point for the generic commands generated
10068c2ecf20Sopenharmony_ci * by the Uniform CD-ROM layer.
10078c2ecf20Sopenharmony_ci */
10088c2ecf20Sopenharmony_cistatic int sr_packet(struct cdrom_device_info *cdi,
10098c2ecf20Sopenharmony_ci		struct packet_command *cgc)
10108c2ecf20Sopenharmony_ci{
10118c2ecf20Sopenharmony_ci	struct scsi_cd *cd = cdi->handle;
10128c2ecf20Sopenharmony_ci	struct scsi_device *sdev = cd->device;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	if (cgc->cmd[0] == GPCMD_READ_DISC_INFO && sdev->no_read_disc_info)
10158c2ecf20Sopenharmony_ci		return -EDRIVE_CANT_DO_THIS;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if (cgc->timeout <= 0)
10188c2ecf20Sopenharmony_ci		cgc->timeout = IOCTL_TIMEOUT;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	sr_do_ioctl(cd, cgc);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	return cgc->stat;
10238c2ecf20Sopenharmony_ci}
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci/**
10268c2ecf20Sopenharmony_ci *	sr_kref_release - Called to free the scsi_cd structure
10278c2ecf20Sopenharmony_ci *	@kref: pointer to embedded kref
10288c2ecf20Sopenharmony_ci *
10298c2ecf20Sopenharmony_ci *	sr_ref_mutex must be held entering this routine.  Because it is
10308c2ecf20Sopenharmony_ci *	called on last put, you should always use the scsi_cd_get()
10318c2ecf20Sopenharmony_ci *	scsi_cd_put() helpers which manipulate the semaphore directly
10328c2ecf20Sopenharmony_ci *	and never do a direct kref_put().
10338c2ecf20Sopenharmony_ci **/
10348c2ecf20Sopenharmony_cistatic void sr_kref_release(struct kref *kref)
10358c2ecf20Sopenharmony_ci{
10368c2ecf20Sopenharmony_ci	struct scsi_cd *cd = container_of(kref, struct scsi_cd, kref);
10378c2ecf20Sopenharmony_ci	struct gendisk *disk = cd->disk;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	spin_lock(&sr_index_lock);
10408c2ecf20Sopenharmony_ci	clear_bit(MINOR(disk_devt(disk)), sr_index_bits);
10418c2ecf20Sopenharmony_ci	spin_unlock(&sr_index_lock);
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	unregister_cdrom(&cd->cdi);
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	disk->private_data = NULL;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	put_disk(disk);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	mutex_destroy(&cd->lock);
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	kfree(cd);
10528c2ecf20Sopenharmony_ci}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_cistatic int sr_remove(struct device *dev)
10558c2ecf20Sopenharmony_ci{
10568c2ecf20Sopenharmony_ci	struct scsi_cd *cd = dev_get_drvdata(dev);
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	scsi_autopm_get_device(cd->device);
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	del_gendisk(cd->disk);
10618c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, NULL);
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	mutex_lock(&sr_ref_mutex);
10648c2ecf20Sopenharmony_ci	kref_put(&cd->kref, sr_kref_release);
10658c2ecf20Sopenharmony_ci	mutex_unlock(&sr_ref_mutex);
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	return 0;
10688c2ecf20Sopenharmony_ci}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_cistatic int __init init_sr(void)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	int rc;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	rc = register_blkdev(SCSI_CDROM_MAJOR, "sr");
10758c2ecf20Sopenharmony_ci	if (rc)
10768c2ecf20Sopenharmony_ci		return rc;
10778c2ecf20Sopenharmony_ci	rc = scsi_register_driver(&sr_template.gendrv);
10788c2ecf20Sopenharmony_ci	if (rc)
10798c2ecf20Sopenharmony_ci		unregister_blkdev(SCSI_CDROM_MAJOR, "sr");
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	return rc;
10828c2ecf20Sopenharmony_ci}
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_cistatic void __exit exit_sr(void)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	scsi_unregister_driver(&sr_template.gendrv);
10878c2ecf20Sopenharmony_ci	unregister_blkdev(SCSI_CDROM_MAJOR, "sr");
10888c2ecf20Sopenharmony_ci}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_cimodule_init(init_sr);
10918c2ecf20Sopenharmony_cimodule_exit(exit_sr);
10928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1093