@@ -177,6 +177,7 @@ sd_mod-objs := sd.o
sd_mod-$(CONFIG_BLK_DEV_INTEGRITY) += sd_dif.o
sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o
+sr_mod-$(CONFIG_ACPI) += sr_acpi.o
ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \
:= -DCONFIG_NCR53C8XX_PREFETCH -DSCSI_NCR_BIG_ENDIAN \
-DCONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS
@@ -734,6 +734,7 @@ static int sr_probe(struct device *dev)
sdev_printk(KERN_DEBUG, sdev,
"Attached scsi CD-ROM %s\n", cd->cdi.name);
+ sr_acpi_add_pm_notifier(dev);
scsi_autopm_put_device(cd->device);
return 0;
@@ -984,6 +985,7 @@ static int sr_remove(struct device *dev)
struct scsi_cd *cd = dev_get_drvdata(dev);
scsi_autopm_get_device(cd->device);
+ sr_acpi_remove_pm_notifier(dev);
blk_queue_prep_rq(cd->device->request_queue, scsi_prep_fn);
del_gendisk(cd->disk);
@@ -48,6 +48,8 @@ typedef struct scsi_cd {
bool get_event_changed:1; /* changed according to GET_EVENT */
bool ignore_get_event:1; /* GET_EVENT is unreliable, use TUR */
+ bool need_eject:1; /* User wakes up the ODD, need eject the tray */
+
struct cdrom_device_info cdi;
/* We hold gendisk and scsi_device references on probe and use
* the refs on this kref to decide when to release them */
@@ -74,4 +76,13 @@ void sr_vendor_init(Scsi_CD *);
int sr_cd_check(struct cdrom_device_info *);
int sr_set_blocklength(Scsi_CD *, int blocklength);
+/* sr_acpi.c */
+#ifdef CONFIG_ACPI
+extern void sr_acpi_add_pm_notifier(struct device *);
+extern void sr_acpi_remove_pm_notifier(struct device *);
+#else
+static inline void sr_acpi_add_pm_notifier(struct device *dev) {}
+static inline void sr_acpi_remove_pm_notifier(struct device *dev) {}
+#endif
+
#endif
new file mode 100644
@@ -0,0 +1,64 @@
+#include <linux/cdrom.h>
+#include <linux/pm_runtime.h>
+#include <scsi/scsi_device.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+#include "sr.h"
+
+static void sr_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
+{
+ struct device *dev = context;
+ struct scsi_cd *cd = dev_get_drvdata(dev);
+
+ if (event == ACPI_NOTIFY_DEVICE_WAKE && pm_runtime_suspended(dev)) {
+ cd->need_eject = 1;
+ pm_runtime_resume(dev);
+ }
+}
+
+void sr_acpi_add_pm_notifier(struct device *dev)
+{
+ struct acpi_device *acpi_dev;
+ acpi_handle handle;
+ acpi_status status;
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ if (!sdev->can_power_off)
+ return;
+
+ handle = DEVICE_ACPI_HANDLE(dev);
+ if (!handle)
+ return;
+
+ status = acpi_bus_get_device(handle, &acpi_dev);
+ if (ACPI_FAILURE(status))
+ return;
+
+ acpi_power_resource_register_device(dev, handle);
+ acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ sr_acpi_wake_dev, dev);
+ device_set_run_wake(dev, true);
+}
+
+void sr_acpi_remove_pm_notifier(struct device *dev)
+{
+ struct acpi_device *acpi_dev;
+ acpi_handle handle;
+ acpi_status status;
+ struct scsi_device *sdev = to_scsi_device(dev);
+
+ if (!sdev->can_power_off)
+ return;
+
+ handle = DEVICE_ACPI_HANDLE(dev);
+ if (!handle)
+ return;
+
+ status = acpi_bus_get_device(handle, &acpi_dev);
+ if (ACPI_FAILURE(status))
+ return;
+
+ acpi_power_resource_unregister_device(dev, handle);
+ device_set_run_wake(dev, false);
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, sr_acpi_wake_dev);
+}