diff options
Diffstat (limited to 'drivers/scsi/sr.c')
| -rw-r--r-- | drivers/scsi/sr.c | 526 |
1 files changed, 332 insertions, 194 deletions
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 997f8e30509..93cbd36c990 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -35,7 +35,6 @@ #include <linux/module.h> #include <linux/fs.h> #include <linux/kernel.h> -#include <linux/sched.h> #include <linux/mm.h> #include <linux/bio.h> #include <linux/string.h> @@ -45,6 +44,8 @@ #include <linux/init.h> #include <linux/blkdev.h> #include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> #include <asm/uaccess.h> #include <scsi/scsi.h> @@ -60,20 +61,31 @@ #include "sr.h" +MODULE_DESCRIPTION("SCSI cdrom (sr) driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_CDROM_MAJOR); +MODULE_ALIAS_SCSI_DEVICE(TYPE_ROM); +MODULE_ALIAS_SCSI_DEVICE(TYPE_WORM); + #define SR_DISKS 256 -#define MAX_RETRIES 3 -#define SR_TIMEOUT (30 * HZ) #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| \ - CDC_PLAY_AUDIO|CDC_RESET|CDC_IOCTLS|CDC_DRIVE_STATUS| \ + CDC_PLAY_AUDIO|CDC_RESET|CDC_DRIVE_STATUS| \ CDC_CD_R|CDC_CD_RW|CDC_DVD|CDC_DVD_R|CDC_DVD_RAM|CDC_GENERIC_PACKET| \ CDC_MRW|CDC_MRW_W|CDC_RAM) +static DEFINE_MUTEX(sr_mutex); static int sr_probe(struct device *); static int sr_remove(struct device *); -static int sr_init_command(struct scsi_cmnd *); +static int sr_init_command(struct scsi_cmnd *SCpnt); +static int sr_done(struct scsi_cmnd *); +static int sr_runtime_suspend(struct device *dev); + +static struct dev_pm_ops sr_pm_ops = { + .runtime_suspend = sr_runtime_suspend, +}; static struct scsi_driver sr_template = { .owner = THIS_MODULE, @@ -81,8 +93,10 @@ static struct scsi_driver sr_template = { .name = "sr", .probe = sr_probe, .remove = sr_remove, + .pm = &sr_pm_ops, }, .init_command = sr_init_command, + .done = sr_done, }; static unsigned long sr_index_bits[SR_DISKS / BITS_PER_LONG]; @@ -99,14 +113,15 @@ static void sr_release(struct cdrom_device_info *); static void get_sectorsize(struct scsi_cd *); static void get_capabilities(struct scsi_cd *); -static int sr_media_change(struct cdrom_device_info *, int); +static unsigned int sr_check_events(struct cdrom_device_info *cdi, + unsigned int clearing, int slot); 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, - .media_changed = sr_media_change, + .check_events = sr_check_events, .tray_move = sr_tray_move, .lock_door = sr_lock_door, .select_speed = sr_select_speed, @@ -114,7 +129,6 @@ static struct cdrom_device_ops sr_dops = { .get_mcn = sr_get_mcn, .reset = sr_reset, .audio_ioctl = sr_audio_ioctl, - .dev_ioctl = sr_dev_ioctl, .capability = SR_CAPABILITIES, .generic_packet = sr_packet, }; @@ -126,6 +140,16 @@ static inline struct scsi_cd *scsi_cd(struct gendisk *disk) return container_of(disk->private_data, struct scsi_cd, driver); } +static int sr_runtime_suspend(struct device *dev) +{ + struct scsi_cd *cd = dev_get_drvdata(dev); + + if (cd->media_present) + return -EBUSY; + else + return 0; +} + /* * The get and put routines for the struct scsi_cd. Note this entity * has a scsi_device pointer and owns a reference to this. @@ -139,13 +163,10 @@ static inline struct scsi_cd *scsi_cd_get(struct gendisk *disk) goto out; cd = scsi_cd(disk); kref_get(&cd->kref); - if (scsi_device_get(cd->device)) - goto out_put; - goto out; - - out_put: - kref_put(&cd->kref, sr_kref_release); - cd = NULL; + if (scsi_device_get(cd->device)) { + kref_put(&cd->kref, sr_kref_release); + cd = NULL; + } out: mutex_unlock(&sr_ref_mutex); return cd; @@ -161,60 +182,140 @@ static void scsi_cd_put(struct scsi_cd *cd) mutex_unlock(&sr_ref_mutex); } +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; +} + /* - * This function checks to see if the media has been changed in the - * CDROM drive. It is possible that we have already sensed a change, - * or the drive may have sensed one and not yet reported it. We must - * be ready for either case. This function always reports the current - * value of the changed bit. If flag is 0, then the changed bit is reset. - * This function could be done as an ioctl, but we would need to have - * an inode for that to work, and we do not always have one. + * 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. */ - -int sr_media_change(struct cdrom_device_info *cdi, int slot) +static unsigned int sr_check_events(struct cdrom_device_info *cdi, + unsigned int clearing, int slot) { struct scsi_cd *cd = cdi->handle; - int retval; + bool last_present; + struct scsi_sense_hdr sshdr; + unsigned int events; + int ret; + + /* no changer support */ + if (CDSL_CURRENT != slot) + return 0; + + events = sr_get_events(cd->device); + 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; + } - if (CDSL_CURRENT != slot) { - /* no changer support */ - return -EINVAL; + /* + * 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. + */ + if (cd->device->changed) { + events |= DISK_EVENT_MEDIA_CHANGE; + cd->device->changed = 0; + cd->tur_changed = true; } - retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES); - if (retval) { - /* Unable to test, unit probably not ready. This usually - * means there is no disc in the drive. Mark as changed, - * and we will figure it out later once the drive is - * available again. */ + if (!(clearing & DISK_EVENT_MEDIA_CHANGE)) + return events; +do_tur: + /* 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); + + /* + * Media is considered to be present if TUR succeeds or fails with + * sense data indicating something other than media-not-present + * (ASC 0x3a). + */ + cd->media_present = scsi_status_is_good(ret) || + (scsi_sense_valid(&sshdr) && sshdr.asc != 0x3a); + + if (last_present != cd->media_present) cd->device->changed = 1; - return 1; /* This will force a flush, if called from - * check_disk_change */ - }; - retval = cd->device->changed; - cd->device->changed = 0; - /* If the disk changed, the capacity will now be different, - * so we force a re-read of this information */ - if (retval) { - /* check multisession offset etc */ - sr_cd_check(cdi); + if (cd->device->changed) { + events |= DISK_EVENT_MEDIA_CHANGE; + cd->device->changed = 0; + cd->tur_changed = true; + } - get_sectorsize(cd); + 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) { + sdev_printk(KERN_WARNING, cd->device, + "GET_EVENT and TUR disagree continuously, suppress GET_EVENT events\n"); + cd->ignore_get_event = true; + } + } else { + cd->tur_mismatch = 0; + } } - return retval; + cd->tur_changed = false; + cd->get_event_changed = false; + + return events; } - + /* - * rw_intr is the interrupt routine for the device driver. + * sr_done is the interrupt routine for the device driver. * - * It will be notified on the end of a SCSI read / write, and will take on + * It will be notified on the end of a SCSI read / write, and will take one * of several actions based on success or failure. */ -static void rw_intr(struct scsi_cmnd * SCpnt) +static int sr_done(struct scsi_cmnd *SCpnt) { int result = SCpnt->result; - int this_count = SCpnt->bufflen; + int this_count = scsi_bufflen(SCpnt); int good_bytes = (result == 0 ? this_count : 0); int block_sectors = 0; long error_sector; @@ -250,7 +351,8 @@ static void rw_intr(struct scsi_cmnd * SCpnt) if (cd->device->sector_size == 2048) error_sector <<= 2; error_sector &= ~(block_sectors - 1); - good_bytes = (error_sector - SCpnt->request->sector) << 9; + good_bytes = (error_sector - + blk_rq_pos(SCpnt->request)) << 9; if (good_bytes < 0 || good_bytes >= this_count) good_bytes = 0; /* @@ -267,15 +369,6 @@ static void rw_intr(struct scsi_cmnd * SCpnt) break; case RECOVERED_ERROR: - - /* - * An error occured, but it recovered. Inform the - * user, but make sure that it's not treated as a - * hard error. - */ - scsi_print_sense("sr", SCpnt); - SCpnt->result = 0; - SCpnt->sense_buffer[0] = 0x0; good_bytes = this_count; break; @@ -284,27 +377,35 @@ static void rw_intr(struct scsi_cmnd * SCpnt) } } - /* - * This calls the generic completion function, now that we know - * how many actual sectors finished, and how many sectors we need - * to say have failed. - */ - scsi_io_completion(SCpnt, good_bytes, block_sectors << 9); + return good_bytes; } -static int sr_init_command(struct scsi_cmnd * SCpnt) +static int sr_init_command(struct scsi_cmnd *SCpnt) { - int block=0, this_count, s_size, timeout = SR_TIMEOUT; - struct scsi_cd *cd = scsi_cd(SCpnt->request->rq_disk); + int block = 0, this_count, s_size; + struct scsi_cd *cd; + struct request *rq = SCpnt->request; + struct scsi_device *sdp = SCpnt->device; + int ret; + + ret = scsi_setup_fs_cmnd(sdp, rq); + 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; SCSI_LOG_HLQUEUE(1, printk("Doing sr request, dev = %s, block = %d\n", cd->disk->disk_name, block)); if (!cd->device || !scsi_device_online(cd->device)) { - SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n", - SCpnt->request->nr_sectors)); + SCSI_LOG_HLQUEUE(2, printk("Finishing %u sectors\n", + blk_rq_sectors(rq))); SCSI_LOG_HLQUEUE(2, printk("Retry with 0x%p\n", SCpnt)); - return 0; + goto out; } if (cd->device->changed) { @@ -312,7 +413,7 @@ static int sr_init_command(struct scsi_cmnd * SCpnt) * quietly refuse to do anything to a changed disc until the * changed bit has been reset */ - return 0; + goto out; } /* @@ -329,63 +430,63 @@ static int sr_init_command(struct scsi_cmnd * SCpnt) if (s_size != 512 && s_size != 1024 && s_size != 2048) { scmd_printk(KERN_ERR, SCpnt, "bad sector size %d\n", s_size); - return 0; + goto out; } - if (rq_data_dir(SCpnt->request) == WRITE) { + if (rq_data_dir(rq) == WRITE) { if (!cd->device->writeable) - return 0; + goto out; SCpnt->cmnd[0] = WRITE_10; SCpnt->sc_data_direction = DMA_TO_DEVICE; cd->cdi.media_written = 1; - } else if (rq_data_dir(SCpnt->request) == READ) { + } else if (rq_data_dir(rq) == READ) { SCpnt->cmnd[0] = READ_10; SCpnt->sc_data_direction = DMA_FROM_DEVICE; } else { - blk_dump_rq_flags(SCpnt->request, "Unknown sr command"); - return 0; + blk_dump_rq_flags(rq, "Unknown sr command"); + goto out; } { - struct scatterlist *sg = SCpnt->request_buffer; - int i, size = 0; - for (i = 0; i < SCpnt->use_sg; i++) - size += sg[i].length; + struct scatterlist *sg; + int i, size = 0, sg_count = scsi_sg_count(SCpnt); - if (size != SCpnt->request_bufflen && SCpnt->use_sg) { + scsi_for_each_sg(SCpnt, sg, sg_count, i) + size += sg->length; + + if (size != scsi_bufflen(SCpnt)) { scmd_printk(KERN_ERR, SCpnt, "mismatch count %d, bytes %d\n", - size, SCpnt->request_bufflen); - if (SCpnt->request_bufflen > size) - SCpnt->request_bufflen = SCpnt->bufflen = size; + size, scsi_bufflen(SCpnt)); + if (scsi_bufflen(SCpnt) > size) + SCpnt->sdb.length = size; } } /* * request doesn't start on hw block boundary, add scatter pads */ - if (((unsigned int)SCpnt->request->sector % (s_size >> 9)) || - (SCpnt->request_bufflen % s_size)) { + if (((unsigned int)blk_rq_pos(rq) % (s_size >> 9)) || + (scsi_bufflen(SCpnt) % s_size)) { scmd_printk(KERN_NOTICE, SCpnt, "unaligned transfer\n"); - return 0; + goto out; } - this_count = (SCpnt->request_bufflen >> 9) / (s_size >> 9); + this_count = (scsi_bufflen(SCpnt) >> 9) / (s_size >> 9); - SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%ld 512 byte blocks.\n", + SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%u 512 byte blocks.\n", cd->cdi.name, - (rq_data_dir(SCpnt->request) == WRITE) ? + (rq_data_dir(rq) == WRITE) ? "writing" : "reading", - this_count, SCpnt->request->nr_sectors)); + this_count, blk_rq_sectors(rq))); SCpnt->cmnd[1] = 0; - block = (unsigned int)SCpnt->request->sector / (s_size >> 9); + block = (unsigned int)blk_rq_pos(rq) / (s_size >> 9); if (this_count > 0xffff) { this_count = 0xffff; - SCpnt->request_bufflen = SCpnt->bufflen = - this_count * s_size; + SCpnt->sdb.length = this_count * s_size; } SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff; @@ -404,83 +505,120 @@ static int sr_init_command(struct scsi_cmnd * SCpnt) SCpnt->transfersize = cd->device->sector_size; SCpnt->underflow = this_count << 9; SCpnt->allowed = MAX_RETRIES; - SCpnt->timeout_per_command = timeout; - - /* - * This is the completion routine we use. This is matched in terms - * of capability to this function. - */ - SCpnt->done = rw_intr; /* * This indicates that the command is ready from our end to be * queued. */ - return 1; + ret = BLKPREP_OK; + out: + return ret; } -static int sr_block_open(struct inode *inode, struct file *file) +static int sr_block_open(struct block_device *bdev, fmode_t mode) { - struct gendisk *disk = inode->i_bdev->bd_disk; struct scsi_cd *cd; - int ret = 0; - - if(!(cd = scsi_cd_get(disk))) - return -ENXIO; - - if((ret = cdrom_open(&cd->cdi, inode, file)) != 0) - scsi_cd_put(cd); - + int ret = -ENXIO; + + mutex_lock(&sr_mutex); + cd = scsi_cd_get(bdev->bd_disk); + if (cd) { + ret = cdrom_open(&cd->cdi, bdev, mode); + if (ret) + scsi_cd_put(cd); + } + mutex_unlock(&sr_mutex); return ret; } -static int sr_block_release(struct inode *inode, struct file *file) +static void sr_block_release(struct gendisk *disk, fmode_t mode) { - int ret; - struct scsi_cd *cd = scsi_cd(inode->i_bdev->bd_disk); - ret = cdrom_release(&cd->cdi, file); - if(ret) - return ret; - + struct scsi_cd *cd = scsi_cd(disk); + mutex_lock(&sr_mutex); + cdrom_release(&cd->cdi, mode); scsi_cd_put(cd); - - return 0; + mutex_unlock(&sr_mutex); } -static int sr_block_ioctl(struct inode *inode, struct file *file, unsigned cmd, +static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, unsigned long arg) { - struct scsi_cd *cd = scsi_cd(inode->i_bdev->bd_disk); + struct scsi_cd *cd = scsi_cd(bdev->bd_disk); struct scsi_device *sdev = cd->device; + void __user *argp = (void __user *)arg; + int ret; + + mutex_lock(&sr_mutex); - /* - * 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: - return scsi_ioctl(sdev, cmd, (void __user *)arg); + /* + * 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: + ret = scsi_ioctl(sdev, cmd, argp); + goto out; } - return cdrom_ioctl(file, &cd->cdi, inode, cmd, arg); + + ret = cdrom_ioctl(&cd->cdi, bdev, mode, cmd, arg); + if (ret != -ENOSYS) + goto out; + + /* + * ENODEV means that we didn't recognise the ioctl, or that we + * cannot execute it in the current device state. In either + * case fall through to scsi_ioctl, which will return ENDOEV again + * if it doesn't recognise the ioctl + */ + ret = scsi_nonblockable_ioctl(sdev, cmd, argp, + (mode & FMODE_NDELAY) != 0); + if (ret != -ENODEV) + goto out; + ret = scsi_ioctl(sdev, cmd, argp); + +out: + mutex_unlock(&sr_mutex); + return ret; +} + +static unsigned int sr_block_check_events(struct gendisk *disk, + unsigned int clearing) +{ + struct scsi_cd *cd = scsi_cd(disk); + + if (atomic_read(&cd->device->disk_events_disable_depth)) + return 0; + + return cdrom_check_events(&cd->cdi, clearing); } -static int sr_block_media_changed(struct gendisk *disk) +static int sr_block_revalidate_disk(struct gendisk *disk) { struct scsi_cd *cd = scsi_cd(disk); - return cdrom_media_changed(&cd->cdi); + 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)) + goto out; + + sr_cd_check(&cd->cdi); + get_sectorsize(cd); +out: + return 0; } -static struct block_device_operations sr_bdops = +static const struct block_device_operations sr_bdops = { .owner = THIS_MODULE, .open = sr_block_open, .release = sr_block_release, .ioctl = sr_block_ioctl, - .media_changed = sr_block_media_changed, + .check_events = sr_block_check_events, + .revalidate_disk = sr_block_revalidate_disk, /* * No compat_ioctl for now because sr_block_ioctl never - * seems to pass arbitary ioctls down to host drivers. + * seems to pass arbitrary ioctls down to host drivers. */ }; @@ -525,10 +663,9 @@ static int sr_probe(struct device *dev) goto fail; error = -ENOMEM; - cd = kmalloc(sizeof(*cd), GFP_KERNEL); + cd = kzalloc(sizeof(*cd), GFP_KERNEL); if (!cd) goto fail; - memset(cd, 0, sizeof(*cd)); kref_init(&cd->kref); @@ -550,7 +687,10 @@ static int sr_probe(struct device *dev) disk->first_minor = minor; sprintf(disk->disk_name, "sr%d", minor); disk->fops = &sr_bdops; - disk->flags = GENHD_FL_CD; + disk->flags = GENHD_FL_CD | GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE; + disk->events = DISK_EVENT_MEDIA_CHANGE | DISK_EVENT_EJECT_REQUEST; + + blk_queue_rq_timeout(sdev->request_queue, SR_TIMEOUT); cd->device = sdev; cd->disk = disk; @@ -558,6 +698,7 @@ static int sr_probe(struct device *dev) cd->disk = disk; cd->capacity = 0x1fffff; cd->device->changed = 1; /* force recheck CD type */ + cd->media_present = 1; cd->use = 1; cd->readcd_known = 0; cd->readcd_cdda = 0; @@ -574,8 +715,6 @@ static int sr_probe(struct device *dev) get_capabilities(cd); sr_vendor_init(cd); - snprintf(disk->devfs_name, sizeof(disk->devfs_name), - "%s/cd", sdev->devfs_name); disk->driverfs_dev = &sdev->sdev_gendev; set_capacity(disk, cd->capacity); disk->private_data = &cd->driver; @@ -585,12 +724,20 @@ static int sr_probe(struct device *dev) if (register_cdrom(&cd->cdi)) goto fail_put; + /* + * 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); + dev_set_drvdata(dev, cd); disk->flags |= GENHD_FL_REMOVABLE; add_disk(disk); sdev_printk(KERN_DEBUG, sdev, "Attached scsi CD-ROM %s\n", cd->cdi.name); + scsi_autopm_put_device(cd->device); + return 0; fail_put: @@ -605,24 +752,20 @@ fail: static void get_sectorsize(struct scsi_cd *cd) { unsigned char cmd[10]; - unsigned char *buffer; + unsigned char buffer[8]; int the_result, retries = 3; int sector_size; - request_queue_t *queue; - - buffer = kmalloc(512, GFP_KERNEL | GFP_DMA); - if (!buffer) - goto Enomem; + struct request_queue *queue; do { cmd[0] = READ_CAPACITY; memset((void *) &cmd[1], 0, 9); - memset(buffer, 0, 8); + memset(buffer, 0, sizeof(buffer)); /* Do the command and wait.. */ the_result = scsi_execute_req(cd->device, cmd, DMA_FROM_DEVICE, - buffer, 8, NULL, SR_TIMEOUT, - MAX_RETRIES); + buffer, sizeof(buffer), NULL, + SR_TIMEOUT, MAX_RETRIES, NULL); retries--; @@ -633,14 +776,20 @@ static void get_sectorsize(struct scsi_cd *cd) cd->capacity = 0x1fffff; sector_size = 2048; /* A guess, just in case */ } else { -#if 0 - if (cdrom_get_last_written(&cd->cdi, - &cd->capacity)) -#endif - cd->capacity = 1 + ((buffer[0] << 24) | - (buffer[1] << 16) | - (buffer[2] << 8) | - buffer[3]); + 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); + sector_size = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; switch (sector_size) { @@ -676,25 +825,17 @@ static void get_sectorsize(struct scsi_cd *cd) } queue = cd->device->request_queue; - blk_queue_hardsect_size(queue, sector_size); -out: - kfree(buffer); - return; + blk_queue_logical_block_size(queue, sector_size); -Enomem: - cd->capacity = 0x1fffff; - cd->device->sector_size = 2048; /* A guess, just in case */ - goto out; + return; } static void get_capabilities(struct scsi_cd *cd) { unsigned char *buffer; struct scsi_mode_data data; - unsigned char cmd[MAX_COMMAND_SIZE]; struct scsi_sense_hdr sshdr; - unsigned int the_result; - int retries, rc, n; + int rc, n; static const char *loadmech[] = { @@ -716,23 +857,8 @@ static void get_capabilities(struct scsi_cd *cd) return; } - /* issue TEST_UNIT_READY until the initial startup UNIT_ATTENTION - * conditions are gone, or a timeout happens - */ - retries = 0; - do { - memset((void *)cmd, 0, MAX_COMMAND_SIZE); - cmd[0] = TEST_UNIT_READY; - - the_result = scsi_execute_req (cd->device, cmd, DMA_NONE, NULL, - 0, &sshdr, SR_TIMEOUT, - MAX_RETRIES); - - retries++; - } while (retries < 5 && - (!scsi_status_is_good(the_result) || - (scsi_sense_valid(&sshdr) && - sshdr.sense_key == UNIT_ATTENTION))); + /* eat unit attentions */ + scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr); /* ask for mode page 0x2a */ rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128, @@ -814,10 +940,16 @@ static void get_capabilities(struct scsi_cd *cd) static int sr_packet(struct cdrom_device_info *cdi, struct packet_command *cgc) { + 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; + if (cgc->timeout <= 0) cgc->timeout = IOCTL_TIMEOUT; - sr_do_ioctl(cdi->handle, cgc); + sr_do_ioctl(cd, cgc); return cgc->stat; } @@ -837,7 +969,7 @@ static void sr_kref_release(struct kref *kref) struct gendisk *disk = cd->disk; spin_lock(&sr_index_lock); - clear_bit(disk->first_minor, sr_index_bits); + clear_bit(MINOR(disk_devt(disk)), sr_index_bits); spin_unlock(&sr_index_lock); unregister_cdrom(&cd->cdi); @@ -853,6 +985,8 @@ static int sr_remove(struct device *dev) { struct scsi_cd *cd = dev_get_drvdata(dev); + scsi_autopm_get_device(cd->device); + del_gendisk(cd->disk); mutex_lock(&sr_ref_mutex); @@ -869,7 +1003,11 @@ static int __init init_sr(void) rc = register_blkdev(SCSI_CDROM_MAJOR, "sr"); if (rc) return rc; - return scsi_register_driver(&sr_template.gendrv); + rc = scsi_register_driver(&sr_template.gendrv); + if (rc) + unregister_blkdev(SCSI_CDROM_MAJOR, "sr"); + + return rc; } static void __exit exit_sr(void) |
