diff options
author | Tejun Heo <tj@kernel.org> | 2010-12-16 11:52:17 -0500 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2010-12-16 11:53:39 -0500 |
commit | 93aae17af1172c40c6f74b7294e93a90c3cfaa5d (patch) | |
tree | 1ad359eba862b56c106bca78d412293f34e3500d /drivers/scsi/sr.c | |
parent | 9f8a2c23c6c1140f515f601265c4dff7522110b7 (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.c | 149 |
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 *); | |||
104 | static void get_sectorsize(struct scsi_cd *); | 104 | static void get_sectorsize(struct scsi_cd *); |
105 | static void get_capabilities(struct scsi_cd *); | 105 | static void get_capabilities(struct scsi_cd *); |
106 | 106 | ||
107 | static int sr_media_change(struct cdrom_device_info *, int); | 107 | static unsigned int sr_check_events(struct cdrom_device_info *cdi, |
108 | unsigned int clearing, int slot); | ||
108 | static int sr_packet(struct cdrom_device_info *, struct packet_command *); | 109 | static int sr_packet(struct cdrom_device_info *, struct packet_command *); |
109 | 110 | ||
110 | static struct cdrom_device_ops sr_dops = { | 111 | static 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 | ||
169 | static 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 | 210 | static unsigned int sr_check_events(struct cdrom_device_info *cdi, | |
178 | static 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, | 246 | skip_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 | ||
220 | out: | 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 | ||
515 | static int sr_block_media_changed(struct gendisk *disk) | 543 | static 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 | |||
550 | static 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 | ||
521 | static const struct block_device_operations sr_bdops = | 564 | static 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; |