|
@@ -1348,7 +1348,10 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot)
|
|
|
if (!CDROM_CAN(CDC_SELECT_DISC))
|
|
|
return -EDRIVE_CANT_DO_THIS;
|
|
|
|
|
|
- (void) cdi->ops->media_changed(cdi, slot);
|
|
|
+ if (cdi->ops->check_events)
|
|
|
+ cdi->ops->check_events(cdi, 0, slot);
|
|
|
+ else
|
|
|
+ cdi->ops->media_changed(cdi, slot);
|
|
|
|
|
|
if (slot == CDSL_NONE) {
|
|
|
/* set media changed bits, on both queues */
|
|
@@ -1392,6 +1395,41 @@ static int cdrom_select_disc(struct cdrom_device_info *cdi, int slot)
|
|
|
return slot;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * As cdrom implements an extra ioctl consumer for media changed
|
|
|
+ * event, it needs to buffer ->check_events() output, such that event
|
|
|
+ * is not lost for both the usual VFS and ioctl paths.
|
|
|
+ * cdi->{vfs|ioctl}_events are used to buffer pending events for each
|
|
|
+ * path.
|
|
|
+ *
|
|
|
+ * XXX: Locking is non-existent. cdi->ops->check_events() can be
|
|
|
+ * called in parallel and buffering fields are accessed without any
|
|
|
+ * exclusion. The original media_changed code had the same problem.
|
|
|
+ * It might be better to simply deprecate CDROM_MEDIA_CHANGED ioctl
|
|
|
+ * and remove this cruft altogether. It doesn't have much usefulness
|
|
|
+ * at this point.
|
|
|
+ */
|
|
|
+static void cdrom_update_events(struct cdrom_device_info *cdi,
|
|
|
+ unsigned int clearing)
|
|
|
+{
|
|
|
+ unsigned int events;
|
|
|
+
|
|
|
+ events = cdi->ops->check_events(cdi, clearing, CDSL_CURRENT);
|
|
|
+ cdi->vfs_events |= events;
|
|
|
+ cdi->ioctl_events |= events;
|
|
|
+}
|
|
|
+
|
|
|
+unsigned int cdrom_check_events(struct cdrom_device_info *cdi,
|
|
|
+ unsigned int clearing)
|
|
|
+{
|
|
|
+ unsigned int events;
|
|
|
+
|
|
|
+ cdrom_update_events(cdi, clearing);
|
|
|
+ events = cdi->vfs_events;
|
|
|
+ cdi->vfs_events = 0;
|
|
|
+ return events;
|
|
|
+}
|
|
|
+
|
|
|
/* We want to make media_changed accessible to the user through an
|
|
|
* ioctl. The main problem now is that we must double-buffer the
|
|
|
* low-level implementation, to assure that the VFS and the user both
|
|
@@ -1403,15 +1441,26 @@ int media_changed(struct cdrom_device_info *cdi, int queue)
|
|
|
{
|
|
|
unsigned int mask = (1 << (queue & 1));
|
|
|
int ret = !!(cdi->mc_flags & mask);
|
|
|
+ bool changed;
|
|
|
|
|
|
if (!CDROM_CAN(CDC_MEDIA_CHANGED))
|
|
|
- return ret;
|
|
|
+ return ret;
|
|
|
+
|
|
|
/* changed since last call? */
|
|
|
- if (cdi->ops->media_changed(cdi, CDSL_CURRENT)) {
|
|
|
+ if (cdi->ops->check_events) {
|
|
|
+ BUG_ON(!queue); /* shouldn't be called from VFS path */
|
|
|
+ cdrom_update_events(cdi, DISK_EVENT_MEDIA_CHANGE);
|
|
|
+ changed = cdi->ioctl_events & DISK_EVENT_MEDIA_CHANGE;
|
|
|
+ cdi->ioctl_events = 0;
|
|
|
+ } else
|
|
|
+ changed = cdi->ops->media_changed(cdi, CDSL_CURRENT);
|
|
|
+
|
|
|
+ if (changed) {
|
|
|
cdi->mc_flags = 0x3; /* set bit on both queues */
|
|
|
ret |= 1;
|
|
|
cdi->media_written = 0;
|
|
|
}
|
|
|
+
|
|
|
cdi->mc_flags &= ~mask; /* clear bit */
|
|
|
return ret;
|
|
|
}
|