Commit 93aae17a authored by Tejun Heo's avatar Tejun Heo Committed by Jens Axboe

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: default avatarTejun Heo <tj@kernel.org>
Cc: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: default avatarJens Axboe <jaxboe@fusionio.com>
parent 9f8a2c23
...@@ -104,14 +104,15 @@ static void sr_release(struct cdrom_device_info *); ...@@ -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) ...@@ -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)
{
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 * This function checks to see if the media has been changed or eject
* CDROM drive. It is possible that we have already sensed a change, * button has been pressed. It is possible that we have already
* or the drive may have sensed one and not yet reported it. We must * sensed a change, or the drive may have sensed one and not yet
* be ready for either case. This function always reports the current * reported it. The past events are accumulated in sdev->changed and
* value of the changed bit. If flag is 0, then the changed bit is reset. * returned together with the current state.
* 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 unsigned int sr_check_events(struct cdrom_device_info *cdi,
static int sr_media_change(struct cdrom_device_info *cdi, int slot) unsigned int clearing, int slot)
{ {
struct scsi_cd *cd = cdi->handle; struct scsi_cd *cd = cdi->handle;
int retval; bool last_present;
struct scsi_sense_hdr *sshdr; struct scsi_sense_hdr sshdr;
unsigned int events;
int ret;
if (CDSL_CURRENT != slot) {
/* no changer support */ /* no changer support */
return -EINVAL; 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);
sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
retval = 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 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, ...@@ -512,10 +540,25 @@ static int sr_block_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
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 = ...@@ -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) ...@@ -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) ...@@ -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 { ...@@ -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; ...@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment