libata-zpodd.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #include <linux/libata.h>
  2. #include <linux/cdrom.h>
  3. #include <linux/pm_runtime.h>
  4. #include <scsi/scsi_device.h>
  5. #include "libata.h"
  6. enum odd_mech_type {
  7. ODD_MECH_TYPE_SLOT,
  8. ODD_MECH_TYPE_DRAWER,
  9. ODD_MECH_TYPE_UNSUPPORTED,
  10. };
  11. struct zpodd {
  12. enum odd_mech_type mech_type; /* init during probe, RO afterwards */
  13. struct ata_device *dev;
  14. /* The following fields are synchronized by PM core. */
  15. bool from_notify; /* resumed as a result of
  16. * acpi wake notification */
  17. };
  18. /* Per the spec, only slot type and drawer type ODD can be supported */
  19. static enum odd_mech_type zpodd_get_mech_type(struct ata_device *dev)
  20. {
  21. char buf[16];
  22. unsigned int ret;
  23. struct rm_feature_desc *desc = (void *)(buf + 8);
  24. struct ata_taskfile tf = {};
  25. char cdb[] = { GPCMD_GET_CONFIGURATION,
  26. 2, /* only 1 feature descriptor requested */
  27. 0, 3, /* 3, removable medium feature */
  28. 0, 0, 0,/* reserved */
  29. 0, sizeof(buf),
  30. 0, 0, 0,
  31. };
  32. tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;
  33. tf.command = ATA_CMD_PACKET;
  34. tf.protocol = ATAPI_PROT_PIO;
  35. tf.lbam = sizeof(buf);
  36. ret = ata_exec_internal(dev, &tf, cdb, DMA_FROM_DEVICE,
  37. buf, sizeof(buf), 0);
  38. if (ret)
  39. return ODD_MECH_TYPE_UNSUPPORTED;
  40. if (be16_to_cpu(desc->feature_code) != 3)
  41. return ODD_MECH_TYPE_UNSUPPORTED;
  42. if (desc->mech_type == 0 && desc->load == 0 && desc->eject == 1)
  43. return ODD_MECH_TYPE_SLOT;
  44. else if (desc->mech_type == 1 && desc->load == 0 && desc->eject == 1)
  45. return ODD_MECH_TYPE_DRAWER;
  46. else
  47. return ODD_MECH_TYPE_UNSUPPORTED;
  48. }
  49. static bool odd_can_poweroff(struct ata_device *ata_dev)
  50. {
  51. acpi_handle handle;
  52. acpi_status status;
  53. struct acpi_device *acpi_dev;
  54. handle = ata_dev_acpi_handle(ata_dev);
  55. if (!handle)
  56. return false;
  57. status = acpi_bus_get_device(handle, &acpi_dev);
  58. if (ACPI_FAILURE(status))
  59. return false;
  60. return acpi_device_can_poweroff(acpi_dev);
  61. }
  62. static void zpodd_wake_dev(acpi_handle handle, u32 event, void *context)
  63. {
  64. struct ata_device *ata_dev = context;
  65. struct zpodd *zpodd = ata_dev->zpodd;
  66. struct device *dev = &ata_dev->sdev->sdev_gendev;
  67. if (event == ACPI_NOTIFY_DEVICE_WAKE && ata_dev &&
  68. pm_runtime_suspended(dev)) {
  69. zpodd->from_notify = true;
  70. pm_runtime_resume(dev);
  71. }
  72. }
  73. static void ata_acpi_add_pm_notifier(struct ata_device *dev)
  74. {
  75. acpi_handle handle = ata_dev_acpi_handle(dev);
  76. acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
  77. zpodd_wake_dev, dev);
  78. }
  79. static void ata_acpi_remove_pm_notifier(struct ata_device *dev)
  80. {
  81. acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->sdev->sdev_gendev);
  82. acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, zpodd_wake_dev);
  83. }
  84. void zpodd_init(struct ata_device *dev)
  85. {
  86. enum odd_mech_type mech_type;
  87. struct zpodd *zpodd;
  88. if (dev->zpodd)
  89. return;
  90. if (!odd_can_poweroff(dev))
  91. return;
  92. mech_type = zpodd_get_mech_type(dev);
  93. if (mech_type == ODD_MECH_TYPE_UNSUPPORTED)
  94. return;
  95. zpodd = kzalloc(sizeof(struct zpodd), GFP_KERNEL);
  96. if (!zpodd)
  97. return;
  98. zpodd->mech_type = mech_type;
  99. ata_acpi_add_pm_notifier(dev);
  100. zpodd->dev = dev;
  101. dev->zpodd = zpodd;
  102. }
  103. void zpodd_exit(struct ata_device *dev)
  104. {
  105. ata_acpi_remove_pm_notifier(dev);
  106. kfree(dev->zpodd);
  107. dev->zpodd = NULL;
  108. }