aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/sr.c
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2010-12-16 11:52:17 -0500
committerJens Axboe <jaxboe@fusionio.com>2010-12-16 11:53:39 -0500
commit93aae17af1172c40c6f74b7294e93a90c3cfaa5d (patch)
tree1ad359eba862b56c106bca78d412293f34e3500d /drivers/scsi/sr.c
parent9f8a2c23c6c1140f515f601265c4dff7522110b7 (diff)
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>
Diffstat (limited to 'drivers/scsi/sr.c')
-rw-r--r--drivers/scsi/sr.c149
1 files changed, 97 insertions, 52 deletions
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 310b3fac6313..be6baf8ad704 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -104,14 +104,15 @@ static void sr_release(struct cdrom_device_info *);
104static void get_sectorsize(struct scsi_cd *); 104static void get_sectorsize(struct scsi_cd *);
105static void get_capabilities(struct scsi_cd *); 105static void get_capabilities(struct scsi_cd *);
106 106
107static int sr_media_change(struct cdrom_device_info *, int); 107static unsigned int sr_check_events(struct cdrom_device_info *cdi,
108 unsigned int clearing, int slot);
108static int sr_packet(struct cdrom_device_info *, struct packet_command *); 109static int sr_packet(struct cdrom_device_info *, struct packet_command *);
109 110
110static struct cdrom_device_ops sr_dops = { 111static struct cdrom_device_ops sr_dops = {
111 .open = sr_open, 112 .open = sr_open,
112 .release = sr_release, 113 .release = sr_release,
113 .drive_status = sr_drive_status, 114 .drive_status = sr_drive_status,
114 .media_changed = sr_media_change, 115 .check_events = sr_check_events,
115 .tray_move = sr_tray_move, 116 .tray_move = sr_tray_move,
116 .lock_door = sr_lock_door, 117 .lock_door = sr_lock_door,
117 .select_speed = sr_select_speed, 118 .select_speed = sr_select_speed,
@@ -165,69 +166,96 @@ static void scsi_cd_put(struct scsi_cd *cd)
165 mutex_unlock(&sr_ref_mutex); 166 mutex_unlock(&sr_ref_mutex);
166} 167}
167 168
169static unsigned int sr_get_events(struct scsi_device *sdev)
170{
171 u8 buf[8];
172 u8 cmd[] = { GET_EVENT_STATUS_NOTIFICATION,
173 1, /* polled */
174 0, 0, /* reserved */
175 1 << 4, /* notification class: media */
176 0, 0, /* reserved */
177 0, sizeof(buf), /* allocation length */
178 0, /* control */
179 };
180 struct event_header *eh = (void *)buf;
181 struct media_event_desc *med = (void *)(buf + 4);
182 struct scsi_sense_hdr sshdr;
183 int result;
184
185 result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, sizeof(buf),
186 &sshdr, SR_TIMEOUT, MAX_RETRIES, NULL);
187 if (scsi_sense_valid(&sshdr) && sshdr.sense_key == UNIT_ATTENTION)
188 return DISK_EVENT_MEDIA_CHANGE;
189
190 if (result || be16_to_cpu(eh->data_len) < sizeof(*med))
191 return 0;
192
193 if (eh->nea || eh->notification_class != 0x4)
194 return 0;
195
196 if (med->media_event_code == 1)
197 return DISK_EVENT_EJECT_REQUEST;
198 else if (med->media_event_code == 2)
199 return DISK_EVENT_MEDIA_CHANGE;
200 return 0;
201}
202
168/* 203/*
169 * This function checks to see if the media has been changed in the 204 * This function checks to see if the media has been changed or eject
170 * CDROM drive. It is possible that we have already sensed a change, 205 * button has been pressed. It is possible that we have already
171 * or the drive may have sensed one and not yet reported it. We must 206 * sensed a change, or the drive may have sensed one and not yet
172 * be ready for either case. This function always reports the current 207 * reported it. The past events are accumulated in sdev->changed and
173 * value of the changed bit. If flag is 0, then the changed bit is reset. 208 * returned together with the current state.
174 * This function could be done as an ioctl, but we would need to have
175 * an inode for that to work, and we do not always have one.
176 */ 209 */
177 210static unsigned int sr_check_events(struct cdrom_device_info *cdi,
178static int sr_media_change(struct cdrom_device_info *cdi, int slot) 211 unsigned int clearing, int slot)
179{ 212{
180 struct scsi_cd *cd = cdi->handle; 213 struct scsi_cd *cd = cdi->handle;
181 int retval; 214 bool last_present;
182 struct scsi_sense_hdr *sshdr; 215 struct scsi_sense_hdr sshdr;
216 unsigned int events;
217 int ret;
183 218
184 if (CDSL_CURRENT != slot) { 219 /* no changer support */
185 /* no changer support */ 220 if (CDSL_CURRENT != slot)
186 return -EINVAL; 221 return 0;
187 } 222
223 events = sr_get_events(cd->device);
224 /*
225 * GET_EVENT_STATUS_NOTIFICATION is enough unless MEDIA_CHANGE
226 * is being cleared. Note that there are devices which hang
227 * if asked to execute TUR repeatedly.
228 */
229 if (!(clearing & DISK_EVENT_MEDIA_CHANGE))
230 goto skip_tur;
231
232 /* let's see whether the media is there with TUR */
233 last_present = cd->media_present;
234 ret = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr);
188 235
189 sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
190 retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES,
191 sshdr);
192 /* 236 /*
193 * Media is considered to be present if TUR succeeds or fails with 237 * Media is considered to be present if TUR succeeds or fails with
194 * sense data indicating something other than media-not-present 238 * sense data indicating something other than media-not-present
195 * (ASC 0x3a). 239 * (ASC 0x3a).
196 */ 240 */
197 if (!scsi_status_is_good(retval) && 241 cd->media_present = scsi_status_is_good(ret) ||
198 (!scsi_sense_valid(sshdr) || sshdr->asc == 0x3a)) { 242 (scsi_sense_valid(&sshdr) && sshdr.asc != 0x3a);
199 /*
200 * Probably no media in the device. Mark as changed, and
201 * we will figure it out later once the drive is available
202 * again.
203 */
204 cd->device->changed = 1;
205 /* This will force a flush, if called from check_disk_change */
206 retval = 1;
207 goto out;
208 };
209 243
210 retval = cd->device->changed; 244 if (last_present != cd->media_present)
211 cd->device->changed = 0; 245 events |= DISK_EVENT_MEDIA_CHANGE;
212 /* If the disk changed, the capacity will now be different, 246skip_tur:
213 * so we force a re-read of this information */ 247 if (cd->device->changed) {
214 if (retval) { 248 events |= DISK_EVENT_MEDIA_CHANGE;
215 /* check multisession offset etc */ 249 cd->device->changed = 0;
216 sr_cd_check(cdi);
217 get_sectorsize(cd);
218 } 250 }
219 251
220out: 252 /* for backward compatibility */
221 /* Notify userspace, that media has changed. */ 253 if (events & DISK_EVENT_MEDIA_CHANGE)
222 if (retval != cd->previous_state)
223 sdev_evt_send_simple(cd->device, SDEV_EVT_MEDIA_CHANGE, 254 sdev_evt_send_simple(cd->device, SDEV_EVT_MEDIA_CHANGE,
224 GFP_KERNEL); 255 GFP_KERNEL);
225 cd->previous_state = retval; 256 return events;
226 kfree(sshdr);
227
228 return retval;
229} 257}
230 258
231/* 259/*
232 * sr_done is the interrupt routine for the device driver. 260 * sr_done is the interrupt routine for the device driver.
233 * 261 *
@@ -512,10 +540,25 @@ out:
512 return ret; 540 return ret;
513} 541}
514 542
515static int sr_block_media_changed(struct gendisk *disk) 543static unsigned int sr_block_check_events(struct gendisk *disk,
544 unsigned int clearing)
516{ 545{
517 struct scsi_cd *cd = scsi_cd(disk); 546 struct scsi_cd *cd = scsi_cd(disk);
518 return cdrom_media_changed(&cd->cdi); 547 return cdrom_check_events(&cd->cdi, clearing);
548}
549
550static int sr_block_revalidate_disk(struct gendisk *disk)
551{
552 struct scsi_cd *cd = scsi_cd(disk);
553 struct scsi_sense_hdr sshdr;
554
555 /* if the unit is not ready, nothing more to do */
556 if (scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr))
557 return 0;
558
559 sr_cd_check(&cd->cdi);
560 get_sectorsize(cd);
561 return 0;
519} 562}
520 563
521static const struct block_device_operations sr_bdops = 564static const struct block_device_operations sr_bdops =
@@ -524,7 +567,8 @@ static const struct block_device_operations sr_bdops =
524 .open = sr_block_open, 567 .open = sr_block_open,
525 .release = sr_block_release, 568 .release = sr_block_release,
526 .ioctl = sr_block_ioctl, 569 .ioctl = sr_block_ioctl,
527 .media_changed = sr_block_media_changed, 570 .check_events = sr_block_check_events,
571 .revalidate_disk = sr_block_revalidate_disk,
528 /* 572 /*
529 * No compat_ioctl for now because sr_block_ioctl never 573 * No compat_ioctl for now because sr_block_ioctl never
530 * seems to pass arbitary ioctls down to host drivers. 574 * seems to pass arbitary ioctls down to host drivers.
@@ -597,6 +641,7 @@ static int sr_probe(struct device *dev)
597 sprintf(disk->disk_name, "sr%d", minor); 641 sprintf(disk->disk_name, "sr%d", minor);
598 disk->fops = &sr_bdops; 642 disk->fops = &sr_bdops;
599 disk->flags = GENHD_FL_CD; 643 disk->flags = GENHD_FL_CD;
644 disk->events = DISK_EVENT_MEDIA_CHANGE | DISK_EVENT_EJECT_REQUEST;
600 645
601 blk_queue_rq_timeout(sdev->request_queue, SR_TIMEOUT); 646 blk_queue_rq_timeout(sdev->request_queue, SR_TIMEOUT);
602 647
@@ -606,7 +651,7 @@ static int sr_probe(struct device *dev)
606 cd->disk = disk; 651 cd->disk = disk;
607 cd->capacity = 0x1fffff; 652 cd->capacity = 0x1fffff;
608 cd->device->changed = 1; /* force recheck CD type */ 653 cd->device->changed = 1; /* force recheck CD type */
609 cd->previous_state = 1; 654 cd->media_present = 1;
610 cd->use = 1; 655 cd->use = 1;
611 cd->readcd_known = 0; 656 cd->readcd_known = 0;
612 cd->readcd_cdda = 0; 657 cd->readcd_cdda = 0;