sr.c 25.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
/*
 *  sr.c Copyright (C) 1992 David Giller
 *           Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale
 *
 *  adapted from:
 *      sd.c Copyright (C) 1992 Drew Eckhardt
 *      Linux scsi disk driver by
 *              Drew Eckhardt <drew@colorado.edu>
 *
 *	Modified by Eric Youngdale ericy@andante.org to
 *	add scatter-gather, multiple outstanding request, and other
 *	enhancements.
 *
 *      Modified by Eric Youngdale eric@andante.org to support loadable
 *      low-level scsi drivers.
 *
 *      Modified by Thomas Quinot thomas@melchior.cuivre.fdn.fr to
 *      provide auto-eject.
 *
 *      Modified by Gerd Knorr <kraxel@cs.tu-berlin.de> to support the
 *      generic cdrom interface
 *
 *      Modified by Jens Axboe <axboe@suse.de> - Uniform sr_packet()
 *      interface, capabilities probe additions, ioctl cleanups, etc.
 *
 *	Modified by Richard Gooch <rgooch@atnf.csiro.au> to support devfs
 *
 *	Modified by Jens Axboe <axboe@suse.de> - support DVD-RAM
 *	transparently and lose the GHOST hack
 *
 *	Modified by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
 *	check resource allocation in sr_init and some cleanups
 */

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/bio.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/cdrom.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/blkdev.h>
46
#include <linux/mutex.h>
47
#include <linux/slab.h>
Aaron Lu's avatar
Aaron Lu committed
48
#include <linux/pm_runtime.h>
Linus Torvalds's avatar
Linus Torvalds committed
49 50 51 52 53 54
#include <asm/uaccess.h>

#include <scsi/scsi.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_driver.h>
55
#include <scsi/scsi_cmnd.h>
Linus Torvalds's avatar
Linus Torvalds committed
56 57 58 59 60 61 62 63
#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_ioctl.h>	/* For the door lock/unlock commands */

#include "scsi_logging.h"
#include "sr.h"


64 65 66
MODULE_DESCRIPTION("SCSI cdrom (sr) driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_CDROM_MAJOR);
67 68
MODULE_ALIAS_SCSI_DEVICE(TYPE_ROM);
MODULE_ALIAS_SCSI_DEVICE(TYPE_WORM);
69

Linus Torvalds's avatar
Linus Torvalds committed
70 71 72 73 74
#define SR_DISKS	256

#define SR_CAPABILITIES \
	(CDC_CLOSE_TRAY|CDC_OPEN_TRAY|CDC_LOCK|CDC_SELECT_SPEED| \
	 CDC_SELECT_DISC|CDC_MULTI_SESSION|CDC_MCN|CDC_MEDIA_CHANGED| \
75
	 CDC_PLAY_AUDIO|CDC_RESET|CDC_DRIVE_STATUS| \
Linus Torvalds's avatar
Linus Torvalds committed
76 77 78
	 CDC_CD_R|CDC_CD_RW|CDC_DVD|CDC_DVD_R|CDC_DVD_RAM|CDC_GENERIC_PACKET| \
	 CDC_MRW|CDC_MRW_W|CDC_RAM)

79
static DEFINE_MUTEX(sr_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
80 81
static int sr_probe(struct device *);
static int sr_remove(struct device *);
82
static int sr_init_command(struct scsi_cmnd *SCpnt);
83
static int sr_done(struct scsi_cmnd *);
Aaron Lu's avatar
Aaron Lu committed
84 85
static int sr_runtime_suspend(struct device *dev);

86
static const struct dev_pm_ops sr_pm_ops = {
Aaron Lu's avatar
Aaron Lu committed
87 88
	.runtime_suspend	= sr_runtime_suspend,
};
Linus Torvalds's avatar
Linus Torvalds committed
89 90 91 92

static struct scsi_driver sr_template = {
	.gendrv = {
		.name   	= "sr",
93
		.owner		= THIS_MODULE,
Linus Torvalds's avatar
Linus Torvalds committed
94 95
		.probe		= sr_probe,
		.remove		= sr_remove,
Aaron Lu's avatar
Aaron Lu committed
96
		.pm		= &sr_pm_ops,
Linus Torvalds's avatar
Linus Torvalds committed
97
	},
98
	.init_command		= sr_init_command,
99
	.done			= sr_done,
Linus Torvalds's avatar
Linus Torvalds committed
100 101 102 103 104 105 106 107
};

static unsigned long sr_index_bits[SR_DISKS / BITS_PER_LONG];
static DEFINE_SPINLOCK(sr_index_lock);

/* This semaphore is used to mediate the 0->1 reference get in the
 * face of object destruction (i.e. we can't allow a get on an
 * object after last put) */
108
static DEFINE_MUTEX(sr_ref_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
109 110 111 112 113 114 115

static int sr_open(struct cdrom_device_info *, int);
static void sr_release(struct cdrom_device_info *);

static void get_sectorsize(struct scsi_cd *);
static void get_capabilities(struct scsi_cd *);

Tejun Heo's avatar
Tejun Heo committed
116 117
static unsigned int sr_check_events(struct cdrom_device_info *cdi,
				    unsigned int clearing, int slot);
Linus Torvalds's avatar
Linus Torvalds committed
118 119 120 121 122 123
static int sr_packet(struct cdrom_device_info *, struct packet_command *);

static struct cdrom_device_ops sr_dops = {
	.open			= sr_open,
	.release	 	= sr_release,
	.drive_status	 	= sr_drive_status,
Tejun Heo's avatar
Tejun Heo committed
124
	.check_events		= sr_check_events,
Linus Torvalds's avatar
Linus Torvalds committed
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
	.tray_move		= sr_tray_move,
	.lock_door		= sr_lock_door,
	.select_speed		= sr_select_speed,
	.get_last_session	= sr_get_last_session,
	.get_mcn		= sr_get_mcn,
	.reset			= sr_reset,
	.audio_ioctl		= sr_audio_ioctl,
	.capability		= SR_CAPABILITIES,
	.generic_packet		= sr_packet,
};

static void sr_kref_release(struct kref *kref);

static inline struct scsi_cd *scsi_cd(struct gendisk *disk)
{
	return container_of(disk->private_data, struct scsi_cd, driver);
}

Aaron Lu's avatar
Aaron Lu committed
143 144 145 146
static int sr_runtime_suspend(struct device *dev)
{
	struct scsi_cd *cd = dev_get_drvdata(dev);

147 148 149
	if (!cd)	/* E.g.: runtime suspend following sr_remove() */
		return 0;

Aaron Lu's avatar
Aaron Lu committed
150 151 152 153 154 155
	if (cd->media_present)
		return -EBUSY;
	else
		return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
156 157 158 159 160 161 162 163
/*
 * The get and put routines for the struct scsi_cd.  Note this entity
 * has a scsi_device pointer and owns a reference to this.
 */
static inline struct scsi_cd *scsi_cd_get(struct gendisk *disk)
{
	struct scsi_cd *cd = NULL;

164
	mutex_lock(&sr_ref_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
165 166 167 168
	if (disk->private_data == NULL)
		goto out;
	cd = scsi_cd(disk);
	kref_get(&cd->kref);
169 170 171 172
	if (scsi_device_get(cd->device)) {
		kref_put(&cd->kref, sr_kref_release);
		cd = NULL;
	}
Linus Torvalds's avatar
Linus Torvalds committed
173
 out:
174
	mutex_unlock(&sr_ref_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
175 176 177
	return cd;
}

178
static void scsi_cd_put(struct scsi_cd *cd)
Linus Torvalds's avatar
Linus Torvalds committed
179 180 181
{
	struct scsi_device *sdev = cd->device;

182
	mutex_lock(&sr_ref_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
183 184
	kref_put(&cd->kref, sr_kref_release);
	scsi_device_put(sdev);
185
	mutex_unlock(&sr_ref_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
186 187
}

Tejun Heo's avatar
Tejun Heo committed
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
static unsigned int sr_get_events(struct scsi_device *sdev)
{
	u8 buf[8];
	u8 cmd[] = { GET_EVENT_STATUS_NOTIFICATION,
		     1,			/* polled */
		     0, 0,		/* reserved */
		     1 << 4,		/* notification class: media */
		     0, 0,		/* reserved */
		     0, sizeof(buf),	/* allocation length */
		     0,			/* control */
	};
	struct event_header *eh = (void *)buf;
	struct media_event_desc *med = (void *)(buf + 4);
	struct scsi_sense_hdr sshdr;
	int result;

	result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, sizeof(buf),
				  &sshdr, SR_TIMEOUT, MAX_RETRIES, NULL);
	if (scsi_sense_valid(&sshdr) && sshdr.sense_key == UNIT_ATTENTION)
		return DISK_EVENT_MEDIA_CHANGE;

	if (result || be16_to_cpu(eh->data_len) < sizeof(*med))
		return 0;

	if (eh->nea || eh->notification_class != 0x4)
		return 0;

	if (med->media_event_code == 1)
		return DISK_EVENT_EJECT_REQUEST;
	else if (med->media_event_code == 2)
		return DISK_EVENT_MEDIA_CHANGE;
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
222
/*
Tejun Heo's avatar
Tejun Heo committed
223 224 225 226 227
 * This function checks to see if the media has been changed or eject
 * button has been pressed.  It is possible that we have already
 * sensed a change, or the drive may have sensed one and not yet
 * reported it.  The past events are accumulated in sdev->changed and
 * returned together with the current state.
Linus Torvalds's avatar
Linus Torvalds committed
228
 */
Tejun Heo's avatar
Tejun Heo committed
229 230
static unsigned int sr_check_events(struct cdrom_device_info *cdi,
				    unsigned int clearing, int slot)
Linus Torvalds's avatar
Linus Torvalds committed
231 232
{
	struct scsi_cd *cd = cdi->handle;
Tejun Heo's avatar
Tejun Heo committed
233 234 235 236
	bool last_present;
	struct scsi_sense_hdr sshdr;
	unsigned int events;
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
237

Tejun Heo's avatar
Tejun Heo committed
238 239 240 241 242
	/* no changer support */
	if (CDSL_CURRENT != slot)
		return 0;

	events = sr_get_events(cd->device);
243 244 245 246 247 248 249 250 251 252 253 254 255
	cd->get_event_changed |= events & DISK_EVENT_MEDIA_CHANGE;

	/*
	 * If earlier GET_EVENT_STATUS_NOTIFICATION and TUR did not agree
	 * for several times in a row.  We rely on TUR only for this likely
	 * broken device, to prevent generating incorrect media changed
	 * events for every open().
	 */
	if (cd->ignore_get_event) {
		events &= ~DISK_EVENT_MEDIA_CHANGE;
		goto do_tur;
	}

Tejun Heo's avatar
Tejun Heo committed
256 257 258 259 260
	/*
	 * GET_EVENT_STATUS_NOTIFICATION is enough unless MEDIA_CHANGE
	 * is being cleared.  Note that there are devices which hang
	 * if asked to execute TUR repeatedly.
	 */
261 262 263 264 265
	if (cd->device->changed) {
		events |= DISK_EVENT_MEDIA_CHANGE;
		cd->device->changed = 0;
		cd->tur_changed = true;
	}
Tejun Heo's avatar
Tejun Heo committed
266

267 268 269
	if (!(clearing & DISK_EVENT_MEDIA_CHANGE))
		return events;
do_tur:
Tejun Heo's avatar
Tejun Heo committed
270 271 272
	/* let's see whether the media is there with TUR */
	last_present = cd->media_present;
	ret = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr);
Linus Torvalds's avatar
Linus Torvalds committed
273

274 275 276 277 278
	/*
	 * Media is considered to be present if TUR succeeds or fails with
	 * sense data indicating something other than media-not-present
	 * (ASC 0x3a).
	 */
Tejun Heo's avatar
Tejun Heo committed
279 280
	cd->media_present = scsi_status_is_good(ret) ||
		(scsi_sense_valid(&sshdr) && sshdr.asc != 0x3a);
Linus Torvalds's avatar
Linus Torvalds committed
281

Tejun Heo's avatar
Tejun Heo committed
282
	if (last_present != cd->media_present)
283 284
		cd->device->changed = 1;

Tejun Heo's avatar
Tejun Heo committed
285 286 287
	if (cd->device->changed) {
		events |= DISK_EVENT_MEDIA_CHANGE;
		cd->device->changed = 0;
288 289 290 291 292 293 294 295 296 297
		cd->tur_changed = true;
	}

	if (cd->ignore_get_event)
		return events;

	/* check whether GET_EVENT is reporting spurious MEDIA_CHANGE */
	if (!cd->tur_changed) {
		if (cd->get_event_changed) {
			if (cd->tur_mismatch++ > 8) {
Hannes Reinecke's avatar
Hannes Reinecke committed
298 299
				sr_printk(KERN_WARNING, cd,
					  "GET_EVENT and TUR disagree continuously, suppress GET_EVENT events\n");
300 301 302 303 304
				cd->ignore_get_event = true;
			}
		} else {
			cd->tur_mismatch = 0;
		}
Linus Torvalds's avatar
Linus Torvalds committed
305
	}
306 307
	cd->tur_changed = false;
	cd->get_event_changed = false;
308

Tejun Heo's avatar
Tejun Heo committed
309
	return events;
Linus Torvalds's avatar
Linus Torvalds committed
310
}
Tejun Heo's avatar
Tejun Heo committed
311

Linus Torvalds's avatar
Linus Torvalds committed
312
/*
313
 * sr_done is the interrupt routine for the device driver.
Linus Torvalds's avatar
Linus Torvalds committed
314
 *
315
 * It will be notified on the end of a SCSI read / write, and will take one
Linus Torvalds's avatar
Linus Torvalds committed
316 317
 * of several actions based on success or failure.
 */
318
static int sr_done(struct scsi_cmnd *SCpnt)
Linus Torvalds's avatar
Linus Torvalds committed
319 320
{
	int result = SCpnt->result;
321
	int this_count = scsi_bufflen(SCpnt);
Linus Torvalds's avatar
Linus Torvalds committed
322 323 324 325 326 327
	int good_bytes = (result == 0 ? this_count : 0);
	int block_sectors = 0;
	long error_sector;
	struct scsi_cd *cd = scsi_cd(SCpnt->request->rq_disk);

#ifdef DEBUG
Hannes Reinecke's avatar
Hannes Reinecke committed
328
	scmd_printk(KERN_INFO, SCpnt, "done: %x\n", result);
Linus Torvalds's avatar
Linus Torvalds committed
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
#endif

	/*
	 * Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial
	 * success.  Since this is a relatively rare error condition, no
	 * care is taken to avoid unnecessary additional work such as
	 * memcpy's that could be avoided.
	 */
	if (driver_byte(result) != 0 &&		/* An error occurred */
	    (SCpnt->sense_buffer[0] & 0x7f) == 0x70) { /* Sense current */
		switch (SCpnt->sense_buffer[2]) {
		case MEDIUM_ERROR:
		case VOLUME_OVERFLOW:
		case ILLEGAL_REQUEST:
			if (!(SCpnt->sense_buffer[0] & 0x90))
				break;
			error_sector = (SCpnt->sense_buffer[3] << 24) |
				(SCpnt->sense_buffer[4] << 16) |
				(SCpnt->sense_buffer[5] << 8) |
				SCpnt->sense_buffer[6];
			if (SCpnt->request->bio != NULL)
				block_sectors =
					bio_sectors(SCpnt->request->bio);
			if (block_sectors < 4)
				block_sectors = 4;
			if (cd->device->sector_size == 2048)
				error_sector <<= 2;
			error_sector &= ~(block_sectors - 1);
357 358
			good_bytes = (error_sector -
				      blk_rq_pos(SCpnt->request)) << 9;
Linus Torvalds's avatar
Linus Torvalds committed
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
			if (good_bytes < 0 || good_bytes >= this_count)
				good_bytes = 0;
			/*
			 * The SCSI specification allows for the value
			 * returned by READ CAPACITY to be up to 75 2K
			 * sectors past the last readable block.
			 * Therefore, if we hit a medium error within the
			 * last 75 2K sectors, we decrease the saved size
			 * value.
			 */
			if (error_sector < get_capacity(cd->disk) &&
			    cd->capacity - error_sector < 4 * 75)
				set_capacity(cd->disk, error_sector);
			break;

		case RECOVERED_ERROR:
			good_bytes = this_count;
			break;

		default:
			break;
		}
	}

383
	return good_bytes;
Linus Torvalds's avatar
Linus Torvalds committed
384 385
}

386
static int sr_init_command(struct scsi_cmnd *SCpnt)
Linus Torvalds's avatar
Linus Torvalds committed
387
{
388
	int block = 0, this_count, s_size;
389
	struct scsi_cd *cd;
390
	struct request *rq = SCpnt->request;
391 392
	int ret;

393
	ret = scsi_init_io(SCpnt);
394 395 396 397 398 399 400 401
	if (ret != BLKPREP_OK)
		goto out;
	SCpnt = rq->special;
	cd = scsi_cd(rq->rq_disk);

	/* from here on until we're complete, any goto out
	 * is used for a killable error condition */
	ret = BLKPREP_KILL;
Linus Torvalds's avatar
Linus Torvalds committed
402

Hannes Reinecke's avatar
Hannes Reinecke committed
403 404
	SCSI_LOG_HLQUEUE(1, scmd_printk(KERN_INFO, SCpnt,
		"Doing sr request, block = %d\n", block));
Linus Torvalds's avatar
Linus Torvalds committed
405 406

	if (!cd->device || !scsi_device_online(cd->device)) {
Hannes Reinecke's avatar
Hannes Reinecke committed
407 408 409 410
		SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
			"Finishing %u sectors\n", blk_rq_sectors(rq)));
		SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
			"Retry with 0x%p\n", SCpnt));
411
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
412 413 414 415 416 417 418
	}

	if (cd->device->changed) {
		/*
		 * quietly refuse to do anything to a changed disc until the
		 * changed bit has been reset
		 */
419
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
420 421 422 423 424 425 426 427 428 429 430
	}

	/*
	 * we do lazy blocksize switching (when reading XA sectors,
	 * see CDROMREADMODE2 ioctl) 
	 */
	s_size = cd->device->sector_size;
	if (s_size > 2048) {
		if (!in_interrupt())
			sr_set_blocklength(cd, 2048);
		else
Hannes Reinecke's avatar
Hannes Reinecke committed
431 432
			scmd_printk(KERN_INFO, SCpnt,
				    "can't switch blocksize: in interrupt\n");
Linus Torvalds's avatar
Linus Torvalds committed
433 434 435
	}

	if (s_size != 512 && s_size != 1024 && s_size != 2048) {
436
		scmd_printk(KERN_ERR, SCpnt, "bad sector size %d\n", s_size);
437
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
438 439
	}

440
	if (rq_data_dir(rq) == WRITE) {
441
		if (!cd->writeable)
442
			goto out;
Linus Torvalds's avatar
Linus Torvalds committed
443
		SCpnt->cmnd[0] = WRITE_10;
Hannes Reinecke's avatar
Hannes Reinecke committed
444
		cd->cdi.media_written = 1;
445
	} else if (rq_data_dir(rq) == READ) {
Linus Torvalds's avatar
Linus Torvalds committed
446 447
		SCpnt->cmnd[0] = READ_10;
	} else {
448 449
		blk_dump_rq_flags(rq, "Unknown sr command");
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
450 451 452
	}

	{
453 454
		struct scatterlist *sg;
		int i, size = 0, sg_count = scsi_sg_count(SCpnt);
Linus Torvalds's avatar
Linus Torvalds committed
455

456 457 458 459
		scsi_for_each_sg(SCpnt, sg, sg_count, i)
			size += sg->length;

		if (size != scsi_bufflen(SCpnt)) {
460 461
			scmd_printk(KERN_ERR, SCpnt,
				"mismatch count %d, bytes %d\n",
462 463 464
				size, scsi_bufflen(SCpnt));
			if (scsi_bufflen(SCpnt) > size)
				SCpnt->sdb.length = size;
Linus Torvalds's avatar
Linus Torvalds committed
465 466 467 468 469 470
		}
	}

	/*
	 * request doesn't start on hw block boundary, add scatter pads
	 */
471
	if (((unsigned int)blk_rq_pos(rq) % (s_size >> 9)) ||
472
	    (scsi_bufflen(SCpnt) % s_size)) {
473
		scmd_printk(KERN_NOTICE, SCpnt, "unaligned transfer\n");
474
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
475 476
	}

477
	this_count = (scsi_bufflen(SCpnt) >> 9) / (s_size >> 9);
Linus Torvalds's avatar
Linus Torvalds committed
478 479


Hannes Reinecke's avatar
Hannes Reinecke committed
480 481 482
	SCSI_LOG_HLQUEUE(2, scmd_printk(KERN_INFO, SCpnt,
					"%s %d/%u 512 byte blocks.\n",
					(rq_data_dir(rq) == WRITE) ?
Linus Torvalds's avatar
Linus Torvalds committed
483
					"writing" : "reading",
Hannes Reinecke's avatar
Hannes Reinecke committed
484
					this_count, blk_rq_sectors(rq)));
Linus Torvalds's avatar
Linus Torvalds committed
485 486

	SCpnt->cmnd[1] = 0;
487
	block = (unsigned int)blk_rq_pos(rq) / (s_size >> 9);
Linus Torvalds's avatar
Linus Torvalds committed
488 489 490

	if (this_count > 0xffff) {
		this_count = 0xffff;
491
		SCpnt->sdb.length = this_count * s_size;
Linus Torvalds's avatar
Linus Torvalds committed
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
	}

	SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff;
	SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff;
	SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff;
	SCpnt->cmnd[5] = (unsigned char) block & 0xff;
	SCpnt->cmnd[6] = SCpnt->cmnd[9] = 0;
	SCpnt->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff;
	SCpnt->cmnd[8] = (unsigned char) this_count & 0xff;

	/*
	 * We shouldn't disconnect in the middle of a sector, so with a dumb
	 * host adapter, it's safe to assume that we can at least transfer
	 * this many bytes between each connect / disconnect.
	 */
	SCpnt->transfersize = cd->device->sector_size;
	SCpnt->underflow = this_count << 9;
	SCpnt->allowed = MAX_RETRIES;

	/*
	 * This indicates that the command is ready from our end to be
	 * queued.
	 */
515 516
	ret = BLKPREP_OK;
 out:
517
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
518 519
}

Al Viro's avatar
Al Viro committed
520
static int sr_block_open(struct block_device *bdev, fmode_t mode)
Linus Torvalds's avatar
Linus Torvalds committed
521
{
522
	struct scsi_cd *cd;
Al Viro's avatar
Al Viro committed
523
	int ret = -ENXIO;
Linus Torvalds's avatar
Linus Torvalds committed
524

525
	mutex_lock(&sr_mutex);
526
	cd = scsi_cd_get(bdev->bd_disk);
Al Viro's avatar
Al Viro committed
527 528 529 530 531
	if (cd) {
		ret = cdrom_open(&cd->cdi, bdev, mode);
		if (ret)
			scsi_cd_put(cd);
	}
532
	mutex_unlock(&sr_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
533 534 535
	return ret;
}

536
static void sr_block_release(struct gendisk *disk, fmode_t mode)
Linus Torvalds's avatar
Linus Torvalds committed
537
{
Al Viro's avatar
Al Viro committed
538
	struct scsi_cd *cd = scsi_cd(disk);
539
	mutex_lock(&sr_mutex);
Al Viro's avatar
Al Viro committed
540
	cdrom_release(&cd->cdi, mode);
Linus Torvalds's avatar
Linus Torvalds committed
541
	scsi_cd_put(cd);
542
	mutex_unlock(&sr_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
543 544
}

Al Viro's avatar
Al Viro committed
545
static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
Linus Torvalds's avatar
Linus Torvalds committed
546 547
			  unsigned long arg)
{
Al Viro's avatar
Al Viro committed
548
	struct scsi_cd *cd = scsi_cd(bdev->bd_disk);
Linus Torvalds's avatar
Linus Torvalds committed
549
	struct scsi_device *sdev = cd->device;
550 551
	void __user *argp = (void __user *)arg;
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
552

553
	mutex_lock(&sr_mutex);
554

555 556 557 558 559
	ret = scsi_ioctl_block_when_processing_errors(sdev, cmd,
			(mode & FMODE_NDELAY) != 0);
	if (ret)
		goto out;

560 561 562 563 564 565 566
	/*
	 * Send SCSI addressing ioctls directly to mid level, send other
	 * ioctls to cdrom/block level.
	 */
	switch (cmd) {
	case SCSI_IOCTL_GET_IDLUN:
	case SCSI_IOCTL_GET_BUS_NUMBER:
567 568
		ret = scsi_ioctl(sdev, cmd, argp);
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
569
	}
570

Al Viro's avatar
Al Viro committed
571
	ret = cdrom_ioctl(&cd->cdi, bdev, mode, cmd, arg);
572
	if (ret != -ENOSYS)
573
		goto out;
574

575 576 577
	ret = scsi_ioctl(sdev, cmd, argp);

out:
578
	mutex_unlock(&sr_mutex);
579
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
580 581
}

Tejun Heo's avatar
Tejun Heo committed
582 583
static unsigned int sr_block_check_events(struct gendisk *disk,
					  unsigned int clearing)
Linus Torvalds's avatar
Linus Torvalds committed
584 585
{
	struct scsi_cd *cd = scsi_cd(disk);
Aaron Lu's avatar
Aaron Lu committed
586

587 588
	if (atomic_read(&cd->device->disk_events_disable_depth))
		return 0;
Aaron Lu's avatar
Aaron Lu committed
589

590
	return cdrom_check_events(&cd->cdi, clearing);
Tejun Heo's avatar
Tejun Heo committed
591 592 593 594 595 596 597 598 599
}

static int sr_block_revalidate_disk(struct gendisk *disk)
{
	struct scsi_cd *cd = scsi_cd(disk);
	struct scsi_sense_hdr sshdr;

	/* if the unit is not ready, nothing more to do */
	if (scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr))
Aaron Lu's avatar
Aaron Lu committed
600
		goto out;
Tejun Heo's avatar
Tejun Heo committed
601 602 603

	sr_cd_check(&cd->cdi);
	get_sectorsize(cd);
Aaron Lu's avatar
Aaron Lu committed
604
out:
Tejun Heo's avatar
Tejun Heo committed
605
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
606 607
}

608
static const struct block_device_operations sr_bdops =
Linus Torvalds's avatar
Linus Torvalds committed
609 610
{
	.owner		= THIS_MODULE,
Al Viro's avatar
Al Viro committed
611 612
	.open		= sr_block_open,
	.release	= sr_block_release,
613
	.ioctl		= sr_block_ioctl,
Tejun Heo's avatar
Tejun Heo committed
614 615
	.check_events	= sr_block_check_events,
	.revalidate_disk = sr_block_revalidate_disk,
Linus Torvalds's avatar
Linus Torvalds committed
616 617
	/* 
	 * No compat_ioctl for now because sr_block_ioctl never
Lucas De Marchi's avatar
Lucas De Marchi committed
618
	 * seems to pass arbitrary ioctls down to host drivers.
Linus Torvalds's avatar
Linus Torvalds committed
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
	 */
};

static int sr_open(struct cdrom_device_info *cdi, int purpose)
{
	struct scsi_cd *cd = cdi->handle;
	struct scsi_device *sdev = cd->device;
	int retval;

	/*
	 * If the device is in error recovery, wait until it is done.
	 * If the device is offline, then disallow any access to it.
	 */
	retval = -ENXIO;
	if (!scsi_block_when_processing_errors(sdev))
		goto error_out;

	return 0;

error_out:
	return retval;	
}

static void sr_release(struct cdrom_device_info *cdi)
{
	struct scsi_cd *cd = cdi->handle;

	if (cd->device->sector_size > 2048)
		sr_set_blocklength(cd, 2048);

}

static int sr_probe(struct device *dev)
{
	struct scsi_device *sdev = to_scsi_device(dev);
	struct gendisk *disk;
	struct scsi_cd *cd;
	int minor, error;

658
	scsi_autopm_get_device(sdev);
Linus Torvalds's avatar
Linus Torvalds committed
659 660 661 662 663
	error = -ENODEV;
	if (sdev->type != TYPE_ROM && sdev->type != TYPE_WORM)
		goto fail;

	error = -ENOMEM;
664
	cd = kzalloc(sizeof(*cd), GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
	if (!cd)
		goto fail;

	kref_init(&cd->kref);

	disk = alloc_disk(1);
	if (!disk)
		goto fail_free;

	spin_lock(&sr_index_lock);
	minor = find_first_zero_bit(sr_index_bits, SR_DISKS);
	if (minor == SR_DISKS) {
		spin_unlock(&sr_index_lock);
		error = -EBUSY;
		goto fail_put;
	}
	__set_bit(minor, sr_index_bits);
	spin_unlock(&sr_index_lock);

	disk->major = SCSI_CDROM_MAJOR;
	disk->first_minor = minor;
	sprintf(disk->disk_name, "sr%d", minor);
	disk->fops = &sr_bdops;
688
	disk->flags = GENHD_FL_CD | GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE;
Tejun Heo's avatar
Tejun Heo committed
689
	disk->events = DISK_EVENT_MEDIA_CHANGE | DISK_EVENT_EJECT_REQUEST;
Linus Torvalds's avatar
Linus Torvalds committed
690

691 692
	blk_queue_rq_timeout(sdev->request_queue, SR_TIMEOUT);

Linus Torvalds's avatar
Linus Torvalds committed
693 694 695 696 697 698
	cd->device = sdev;
	cd->disk = disk;
	cd->driver = &sr_template;
	cd->disk = disk;
	cd->capacity = 0x1fffff;
	cd->device->changed = 1;	/* force recheck CD type */
Tejun Heo's avatar
Tejun Heo committed
699
	cd->media_present = 1;
Linus Torvalds's avatar
Linus Torvalds committed
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
	cd->use = 1;
	cd->readcd_known = 0;
	cd->readcd_cdda = 0;

	cd->cdi.ops = &sr_dops;
	cd->cdi.handle = cd;
	cd->cdi.mask = 0;
	cd->cdi.capacity = 1;
	sprintf(cd->cdi.name, "sr%d", minor);

	sdev->sector_size = 2048;	/* A guess, just in case */

	/* FIXME: need to handle a get_capabilities failure properly ?? */
	get_capabilities(cd);
	sr_vendor_init(cd);

	set_capacity(disk, cd->capacity);
	disk->private_data = &cd->driver;
	disk->queue = sdev->request_queue;
	cd->cdi.disk = disk;

	if (register_cdrom(&cd->cdi))
		goto fail_put;

724 725 726 727 728 729
	/*
	 * Initialize block layer runtime PM stuffs before the
	 * periodic event checking request gets started in add_disk.
	 */
	blk_pm_runtime_init(sdev->request_queue, dev);

Linus Torvalds's avatar
Linus Torvalds committed
730 731
	dev_set_drvdata(dev, cd);
	disk->flags |= GENHD_FL_REMOVABLE;
732
	device_add_disk(&sdev->sdev_gendev, disk);
Linus Torvalds's avatar
Linus Torvalds committed
733

734 735
	sdev_printk(KERN_DEBUG, sdev,
		    "Attached scsi CD-ROM %s\n", cd->cdi.name);
Aaron Lu's avatar
Aaron Lu committed
736 737
	scsi_autopm_put_device(cd->device);

Linus Torvalds's avatar
Linus Torvalds committed
738 739 740 741 742 743 744
	return 0;

fail_put:
	put_disk(disk);
fail_free:
	kfree(cd);
fail:
745
	scsi_autopm_put_device(sdev);
Linus Torvalds's avatar
Linus Torvalds committed
746 747 748 749 750 751 752
	return error;
}


static void get_sectorsize(struct scsi_cd *cd)
{
	unsigned char cmd[10];
753
	unsigned char buffer[8];
Linus Torvalds's avatar
Linus Torvalds committed
754 755
	int the_result, retries = 3;
	int sector_size;
756
	struct request_queue *queue;
Linus Torvalds's avatar
Linus Torvalds committed
757 758 759 760

	do {
		cmd[0] = READ_CAPACITY;
		memset((void *) &cmd[1], 0, 9);
761
		memset(buffer, 0, sizeof(buffer));
Linus Torvalds's avatar
Linus Torvalds committed
762 763

		/* Do the command and wait.. */
764
		the_result = scsi_execute_req(cd->device, cmd, DMA_FROM_DEVICE,
765
					      buffer, sizeof(buffer), NULL,
766
					      SR_TIMEOUT, MAX_RETRIES, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
767 768 769 770 771 772 773 774 775 776

		retries--;

	} while (the_result && retries);


	if (the_result) {
		cd->capacity = 0x1fffff;
		sector_size = 2048;	/* A guess, just in case */
	} else {
777 778 779 780 781 782 783 784 785 786 787 788 789 790
		long last_written;

		cd->capacity = 1 + ((buffer[0] << 24) | (buffer[1] << 16) |
				    (buffer[2] << 8) | buffer[3]);
		/*
		 * READ_CAPACITY doesn't return the correct size on
		 * certain UDF media.  If last_written is larger, use
		 * it instead.
		 *
		 * http://bugzilla.kernel.org/show_bug.cgi?id=9668
		 */
		if (!cdrom_get_last_written(&cd->cdi, &last_written))
			cd->capacity = max_t(long, cd->capacity, last_written);

Linus Torvalds's avatar
Linus Torvalds committed
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
		sector_size = (buffer[4] << 24) |
		    (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
		switch (sector_size) {
			/*
			 * HP 4020i CD-Recorder reports 2340 byte sectors
			 * Philips CD-Writers report 2352 byte sectors
			 *
			 * Use 2k sectors for them..
			 */
		case 0:
		case 2340:
		case 2352:
			sector_size = 2048;
			/* fall through */
		case 2048:
			cd->capacity *= 4;
			/* fall through */
		case 512:
			break;
		default:
Hannes Reinecke's avatar
Hannes Reinecke committed
811 812
			sr_printk(KERN_INFO, cd,
				  "unsupported sector size %d.", sector_size);
Linus Torvalds's avatar
Linus Torvalds committed
813 814 815 816 817 818 819 820 821 822 823 824 825
			cd->capacity = 0;
		}

		cd->device->sector_size = sector_size;

		/*
		 * Add this so that we have the ability to correctly gauge
		 * what the device is capable of.
		 */
		set_capacity(cd->disk, cd->capacity);
	}

	queue = cd->device->request_queue;
826
	blk_queue_logical_block_size(queue, sector_size);
Linus Torvalds's avatar
Linus Torvalds committed
827

828
	return;
Linus Torvalds's avatar
Linus Torvalds committed
829 830 831 832 833 834
}

static void get_capabilities(struct scsi_cd *cd)
{
	unsigned char *buffer;
	struct scsi_mode_data data;
835
	struct scsi_sense_hdr sshdr;
836
	unsigned int ms_len = 128;
837
	int rc, n;
Linus Torvalds's avatar
Linus Torvalds committed
838

839
	static const char *loadmech[] =
Linus Torvalds's avatar
Linus Torvalds committed
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
	{
		"caddy",
		"tray",
		"pop-up",
		"",
		"changer",
		"cartridge changer",
		"",
		""
	};


	/* allocate transfer buffer */
	buffer = kmalloc(512, GFP_KERNEL | GFP_DMA);
	if (!buffer) {
Hannes Reinecke's avatar
Hannes Reinecke committed
855
		sr_printk(KERN_ERR, cd, "out of memory.\n");
Linus Torvalds's avatar
Linus Torvalds committed
856 857 858
		return;
	}

859
	/* eat unit attentions */
860
	scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr);
Linus Torvalds's avatar
Linus Torvalds committed
861 862

	/* ask for mode page 0x2a */
863
	rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, ms_len,
864
			     SR_TIMEOUT, 3, &data, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
865

866 867
	if (!scsi_status_is_good(rc) || data.length > ms_len ||
	    data.header_length + data.block_descriptor_length > data.length) {
Linus Torvalds's avatar
Linus Torvalds committed
868 869 870
		/* failed, drive doesn't have capabilities mode page */
		cd->cdi.speed = 1;
		cd->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R |
871 872 873
				 CDC_DVD | CDC_DVD_RAM |
				 CDC_SELECT_DISC | CDC_SELECT_SPEED |
				 CDC_MRW | CDC_MRW_W | CDC_RAM);
Linus Torvalds's avatar
Linus Torvalds committed
874
		kfree(buffer);
Hannes Reinecke's avatar
Hannes Reinecke committed
875
		sr_printk(KERN_INFO, cd, "scsi-1 drive");
Linus Torvalds's avatar
Linus Torvalds committed
876 877 878 879 880 881 882 883
		return;
	}

	n = data.header_length + data.block_descriptor_length;
	cd->cdi.speed = ((buffer[n + 8] << 8) + buffer[n + 9]) / 176;
	cd->readcd_known = 1;
	cd->readcd_cdda = buffer[n + 5] & 0x01;
	/* print some capability bits */
Hannes Reinecke's avatar
Hannes Reinecke committed
884 885 886 887 888 889 890 891 892 893
	sr_printk(KERN_INFO, cd,
		  "scsi3-mmc drive: %dx/%dx %s%s%s%s%s%s\n",
		  ((buffer[n + 14] << 8) + buffer[n + 15]) / 176,
		  cd->cdi.speed,
		  buffer[n + 3] & 0x01 ? "writer " : "", /* CD Writer */
		  buffer[n + 3] & 0x20 ? "dvd-ram " : "",
		  buffer[n + 2] & 0x02 ? "cd/rw " : "", /* can read rewriteable */
		  buffer[n + 4] & 0x20 ? "xa/form2 " : "",	/* can read xa/from2 */
		  buffer[n + 5] & 0x01 ? "cdda " : "", /* can read audio data */
		  loadmech[buffer[n + 6] >> 5]);
Linus Torvalds's avatar
Linus Torvalds committed
894 895 896 897 898 899
	if ((buffer[n + 6] >> 5) == 0)
		/* caddy drives can't close tray... */
		cd->cdi.mask |= CDC_CLOSE_TRAY;
	if ((buffer[n + 2] & 0x8) == 0)
		/* not a DVD drive */
		cd->cdi.mask |= CDC_DVD;
Hannes Reinecke's avatar
Hannes Reinecke committed
900
	if ((buffer[n + 3] & 0x20) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930
		/* can't write DVD-RAM media */
		cd->cdi.mask |= CDC_DVD_RAM;
	if ((buffer[n + 3] & 0x10) == 0)
		/* can't write DVD-R media */
		cd->cdi.mask |= CDC_DVD_R;
	if ((buffer[n + 3] & 0x2) == 0)
		/* can't write CD-RW media */
		cd->cdi.mask |= CDC_CD_RW;
	if ((buffer[n + 3] & 0x1) == 0)
		/* can't write CD-R media */
		cd->cdi.mask |= CDC_CD_R;
	if ((buffer[n + 6] & 0x8) == 0)
		/* can't eject */
		cd->cdi.mask |= CDC_OPEN_TRAY;

	if ((buffer[n + 6] >> 5) == mechtype_individual_changer ||
	    (buffer[n + 6] >> 5) == mechtype_cartridge_changer)
		cd->cdi.capacity =
		    cdrom_number_of_slots(&cd->cdi);
	if (cd->cdi.capacity <= 1)
		/* not a changer */
		cd->cdi.mask |= CDC_SELECT_DISC;
	/*else    I don't think it can close its tray
		cd->cdi.mask |= CDC_CLOSE_TRAY; */

	/*
	 * if DVD-RAM, MRW-W or CD-RW, we are randomly writable
	 */
	if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) !=
			(CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) {
931
		cd->writeable = 1;
Linus Torvalds's avatar
Linus Torvalds committed
932 933 934 935 936 937 938
	}

	kfree(buffer);
}

/*
 * sr_packet() is the entry point for the generic commands generated
Hannes Reinecke's avatar
Hannes Reinecke committed
939
 * by the Uniform CD-ROM layer.
Linus Torvalds's avatar
Linus Torvalds committed
940 941 942 943
 */
static int sr_packet(struct cdrom_device_info *cdi,
		struct packet_command *cgc)
{
944 945 946 947 948 949
	struct scsi_cd *cd = cdi->handle;
	struct scsi_device *sdev = cd->device;

	if (cgc->cmd[0] == GPCMD_READ_DISC_INFO && sdev->no_read_disc_info)
		return -EDRIVE_CANT_DO_THIS;

Linus Torvalds's avatar
Linus Torvalds committed
950 951 952
	if (cgc->timeout <= 0)
		cgc->timeout = IOCTL_TIMEOUT;

953
	sr_do_ioctl(cd, cgc);
Linus Torvalds's avatar
Linus Torvalds committed
954 955 956 957 958 959 960 961

	return cgc->stat;
}

/**
 *	sr_kref_release - Called to free the scsi_cd structure
 *	@kref: pointer to embedded kref
 *
962
 *	sr_ref_mutex must be held entering this routine.  Because it is
Linus Torvalds's avatar
Linus Torvalds committed
963 964 965 966 967 968 969 970 971 972
 *	called on last put, you should always use the scsi_cd_get()
 *	scsi_cd_put() helpers which manipulate the semaphore directly
 *	and never do a direct kref_put().
 **/
static void sr_kref_release(struct kref *kref)
{
	struct scsi_cd *cd = container_of(kref, struct scsi_cd, kref);
	struct gendisk *disk = cd->disk;

	spin_lock(&sr_index_lock);
973
	clear_bit(MINOR(disk_devt(disk)), sr_index_bits);
Linus Torvalds's avatar
Linus Torvalds committed
974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
	spin_unlock(&sr_index_lock);

	unregister_cdrom(&cd->cdi);

	disk->private_data = NULL;

	put_disk(disk);

	kfree(cd);
}

static int sr_remove(struct device *dev)
{
	struct scsi_cd *cd = dev_get_drvdata(dev);

Aaron Lu's avatar
Aaron Lu committed
989 990
	scsi_autopm_get_device(cd->device);

Linus Torvalds's avatar
Linus Torvalds committed
991
	del_gendisk(cd->disk);
992
	dev_set_drvdata(dev, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
993

994
	mutex_lock(&sr_ref_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
995
	kref_put(&cd->kref, sr_kref_release);
996
	mutex_unlock(&sr_ref_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007

	return 0;
}

static int __init init_sr(void)
{
	int rc;

	rc = register_blkdev(SCSI_CDROM_MAJOR, "sr");
	if (rc)
		return rc;
1008 1009 1010 1011 1012
	rc = scsi_register_driver(&sr_template.gendrv);
	if (rc)
		unregister_blkdev(SCSI_CDROM_MAJOR, "sr");

	return rc;
Linus Torvalds's avatar
Linus Torvalds committed
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
}

static void __exit exit_sr(void)
{
	scsi_unregister_driver(&sr_template.gendrv);
	unregister_blkdev(SCSI_CDROM_MAJOR, "sr");
}

module_init(init_sr);
module_exit(exit_sr);
MODULE_LICENSE("GPL");