sr: implement sr_check_events()
Replace sr_media_change() with sr_check_events(). It normally only uses GET_EVENT_STATUS_NOTIFICATION to check both media change and eject request. If @clearing includes DISK_EVENT_MEDIA_CHANGE, it issues TUR and compares whether media presence has changed. The SCSI specific media change uevent is kept for compatibility. sr_media_change() was doing both media change check and revalidation. The revalidation part is split into sr_block_revalidate_disk(). Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Kay Sievers <kay.sievers@vrfy.org> Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
This commit is contained in:
parent
9f8a2c23c6
commit
93aae17af1
|
@ -104,14 +104,15 @@ static void sr_release(struct cdrom_device_info *);
|
||||||
static void get_sectorsize(struct scsi_cd *);
|
static void get_sectorsize(struct scsi_cd *);
|
||||||
static void get_capabilities(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 int sr_packet(struct cdrom_device_info *, struct packet_command *);
|
||||||
|
|
||||||
static struct cdrom_device_ops sr_dops = {
|
static struct cdrom_device_ops sr_dops = {
|
||||||
.open = sr_open,
|
.open = sr_open,
|
||||||
.release = sr_release,
|
.release = sr_release,
|
||||||
.drive_status = sr_drive_status,
|
.drive_status = sr_drive_status,
|
||||||
.media_changed = sr_media_change,
|
.check_events = sr_check_events,
|
||||||
.tray_move = sr_tray_move,
|
.tray_move = sr_tray_move,
|
||||||
.lock_door = sr_lock_door,
|
.lock_door = sr_lock_door,
|
||||||
.select_speed = sr_select_speed,
|
.select_speed = sr_select_speed,
|
||||||
|
@ -165,67 +166,94 @@ static void scsi_cd_put(struct scsi_cd *cd)
|
||||||
mutex_unlock(&sr_ref_mutex);
|
mutex_unlock(&sr_ref_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static unsigned int sr_get_events(struct scsi_device *sdev)
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int sr_media_change(struct cdrom_device_info *cdi, int slot)
|
|
||||||
{
|
{
|
||||||
struct scsi_cd *cd = cdi->handle;
|
u8 buf[8];
|
||||||
int retval;
|
u8 cmd[] = { GET_EVENT_STATUS_NOTIFICATION,
|
||||||
struct scsi_sense_hdr *sshdr;
|
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;
|
||||||
|
|
||||||
if (CDSL_CURRENT != slot) {
|
result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, sizeof(buf),
|
||||||
/* no changer support */
|
&sshdr, SR_TIMEOUT, MAX_RETRIES, NULL);
|
||||||
return -EINVAL;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
|
/*
|
||||||
retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES,
|
* This function checks to see if the media has been changed or eject
|
||||||
sshdr);
|
* 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.
|
||||||
|
*/
|
||||||
|
static unsigned int sr_check_events(struct cdrom_device_info *cdi,
|
||||||
|
unsigned int clearing, int slot)
|
||||||
|
{
|
||||||
|
struct scsi_cd *cd = cdi->handle;
|
||||||
|
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);
|
||||||
|
/*
|
||||||
|
* 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 (!(clearing & DISK_EVENT_MEDIA_CHANGE))
|
||||||
|
goto skip_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
|
* Media is considered to be present if TUR succeeds or fails with
|
||||||
* sense data indicating something other than media-not-present
|
* sense data indicating something other than media-not-present
|
||||||
* (ASC 0x3a).
|
* (ASC 0x3a).
|
||||||
*/
|
*/
|
||||||
if (!scsi_status_is_good(retval) &&
|
cd->media_present = scsi_status_is_good(ret) ||
|
||||||
(!scsi_sense_valid(sshdr) || sshdr->asc == 0x3a)) {
|
(scsi_sense_valid(&sshdr) && sshdr.asc != 0x3a);
|
||||||
/*
|
|
||||||
* Probably no media in the device. Mark as changed, and
|
|
||||||
* we will figure it out later once the drive is available
|
|
||||||
* again.
|
|
||||||
*/
|
|
||||||
cd->device->changed = 1;
|
|
||||||
/* This will force a flush, if called from check_disk_change */
|
|
||||||
retval = 1;
|
|
||||||
goto out;
|
|
||||||
};
|
|
||||||
|
|
||||||
retval = cd->device->changed;
|
if (last_present != cd->media_present)
|
||||||
|
events |= DISK_EVENT_MEDIA_CHANGE;
|
||||||
|
skip_tur:
|
||||||
|
if (cd->device->changed) {
|
||||||
|
events |= DISK_EVENT_MEDIA_CHANGE;
|
||||||
cd->device->changed = 0;
|
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);
|
|
||||||
get_sectorsize(cd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
/* for backward compatibility */
|
||||||
/* Notify userspace, that media has changed. */
|
if (events & DISK_EVENT_MEDIA_CHANGE)
|
||||||
if (retval != cd->previous_state)
|
|
||||||
sdev_evt_send_simple(cd->device, SDEV_EVT_MEDIA_CHANGE,
|
sdev_evt_send_simple(cd->device, SDEV_EVT_MEDIA_CHANGE,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
cd->previous_state = retval;
|
return events;
|
||||||
kfree(sshdr);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -512,10 +540,25 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sr_block_media_changed(struct gendisk *disk)
|
static unsigned int sr_block_check_events(struct gendisk *disk,
|
||||||
|
unsigned int clearing)
|
||||||
{
|
{
|
||||||
struct scsi_cd *cd = scsi_cd(disk);
|
struct scsi_cd *cd = scsi_cd(disk);
|
||||||
return cdrom_media_changed(&cd->cdi);
|
return cdrom_check_events(&cd->cdi, clearing);
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
sr_cd_check(&cd->cdi);
|
||||||
|
get_sectorsize(cd);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct block_device_operations sr_bdops =
|
static const struct block_device_operations sr_bdops =
|
||||||
|
@ -524,7 +567,8 @@ static const struct block_device_operations sr_bdops =
|
||||||
.open = sr_block_open,
|
.open = sr_block_open,
|
||||||
.release = sr_block_release,
|
.release = sr_block_release,
|
||||||
.ioctl = sr_block_ioctl,
|
.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
|
* No compat_ioctl for now because sr_block_ioctl never
|
||||||
* seems to pass arbitary ioctls down to host drivers.
|
* seems to pass arbitary ioctls down to host drivers.
|
||||||
|
@ -597,6 +641,7 @@ static int sr_probe(struct device *dev)
|
||||||
sprintf(disk->disk_name, "sr%d", minor);
|
sprintf(disk->disk_name, "sr%d", minor);
|
||||||
disk->fops = &sr_bdops;
|
disk->fops = &sr_bdops;
|
||||||
disk->flags = GENHD_FL_CD;
|
disk->flags = GENHD_FL_CD;
|
||||||
|
disk->events = DISK_EVENT_MEDIA_CHANGE | DISK_EVENT_EJECT_REQUEST;
|
||||||
|
|
||||||
blk_queue_rq_timeout(sdev->request_queue, SR_TIMEOUT);
|
blk_queue_rq_timeout(sdev->request_queue, SR_TIMEOUT);
|
||||||
|
|
||||||
|
@ -606,7 +651,7 @@ static int sr_probe(struct device *dev)
|
||||||
cd->disk = disk;
|
cd->disk = disk;
|
||||||
cd->capacity = 0x1fffff;
|
cd->capacity = 0x1fffff;
|
||||||
cd->device->changed = 1; /* force recheck CD type */
|
cd->device->changed = 1; /* force recheck CD type */
|
||||||
cd->previous_state = 1;
|
cd->media_present = 1;
|
||||||
cd->use = 1;
|
cd->use = 1;
|
||||||
cd->readcd_known = 0;
|
cd->readcd_known = 0;
|
||||||
cd->readcd_cdda = 0;
|
cd->readcd_cdda = 0;
|
||||||
|
|
|
@ -40,7 +40,7 @@ typedef struct scsi_cd {
|
||||||
unsigned xa_flag:1; /* CD has XA sectors ? */
|
unsigned xa_flag:1; /* CD has XA sectors ? */
|
||||||
unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */
|
unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */
|
||||||
unsigned readcd_cdda:1; /* reading audio data using READ_CD */
|
unsigned readcd_cdda:1; /* reading audio data using READ_CD */
|
||||||
unsigned previous_state:1; /* media has changed */
|
unsigned media_present:1; /* media is present */
|
||||||
struct cdrom_device_info cdi;
|
struct cdrom_device_info cdi;
|
||||||
/* We hold gendisk and scsi_device references on probe and use
|
/* We hold gendisk and scsi_device references on probe and use
|
||||||
* the refs on this kref to decide when to release them */
|
* the refs on this kref to decide when to release them */
|
||||||
|
|
|
@ -104,6 +104,7 @@ struct scsi_cmnd;
|
||||||
#define UNMAP 0x42
|
#define UNMAP 0x42
|
||||||
#define READ_TOC 0x43
|
#define READ_TOC 0x43
|
||||||
#define READ_HEADER 0x44
|
#define READ_HEADER 0x44
|
||||||
|
#define GET_EVENT_STATUS_NOTIFICATION 0x4a
|
||||||
#define LOG_SELECT 0x4c
|
#define LOG_SELECT 0x4c
|
||||||
#define LOG_SENSE 0x4d
|
#define LOG_SENSE 0x4d
|
||||||
#define XDWRITEREAD_10 0x53
|
#define XDWRITEREAD_10 0x53
|
||||||
|
|
Loading…
Reference in New Issue