@@ -1,6 +1,7 @@
/*
* Copyright (C) 2011 Huawei Tech. Co., Ltd.
* Copyright (C) 2011 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2011 Hanjun Guo <guohanjun@huawei.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
@@ -27,6 +28,77 @@
#include <acpi/acpi_hotplug.h>
#include "../internal.h"
+int acpihp_dev_get_info(struct acpi_device *device,
+ struct acpihp_dev_info *info)
+{
+ int ret = -ENOSYS;
+
+ acpihp_dev_get_type(device->handle, &info->type);
+
+ mutex_lock(&device->dev.mutex);
+ if (device->driver && device->driver->ops.hp_ops &&
+ device->driver->ops.hp_ops->get_info)
+ ret = device->driver->ops.hp_ops->get_info(device, info);
+ else
+#if 0
+ /* Turn on this once all system devices have been converted
+ * to the new hotplug framework
+ */
+ info->status |= ACPIHP_DEV_STATUS_IRREMOVABLE;
+#else
+ ret = 0;
+#endif
+
+ if (device->driver)
+ info->status |= ACPIHP_DEV_STATUS_ATTACHED;
+ mutex_unlock(&device->dev.mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpihp_dev_get_info);
+
+#define ACPIHP_DEFINE_FUNC(method, def, err) \
+int acpihp_dev_##method(struct acpi_device *device) \
+{ \
+ int ret; \
+ BUG_ON(device == NULL); \
+ if (!device->driver || !device->driver->ops.hp_ops) \
+ ret = (err); \
+ else if (!device->driver->ops.hp_ops->method) \
+ ret = (def); \
+ else \
+ ret = device->driver->ops.hp_ops->method(device);\
+ return ret; \
+} \
+EXPORT_SYMBOL_GPL(acpihp_dev_##method)
+
+#define ACPIHP_DEFINE_FUNC1(method, def, err, type) \
+int acpihp_dev_##method(struct acpi_device *device, type val) \
+{ \
+ int ret; \
+ BUG_ON(device == NULL); \
+ if (!device->driver || !device->driver->ops.hp_ops) \
+ ret = (err); \
+ else if (!device->driver->ops.hp_ops->method) \
+ ret = (def); \
+ else \
+ ret = device->driver->ops.hp_ops->method(device, val); \
+ return ret; \
+} \
+EXPORT_SYMBOL_GPL(acpihp_dev_##method)
+
+ACPIHP_DEFINE_FUNC1(pre_configure, 0, 0, struct acpihp_cancel_context *);
+ACPIHP_DEFINE_FUNC1(configure, 0, -ENOSYS, struct acpihp_cancel_context *);
+ACPIHP_DEFINE_FUNC1(post_configure, 0, 0, enum acpihp_dev_post_cmd);
+
+ACPIHP_DEFINE_FUNC1(pre_release, 0, 0, struct acpihp_cancel_context *);
+ACPIHP_DEFINE_FUNC1(release, 0, 0, struct acpihp_cancel_context *);
+ACPIHP_DEFINE_FUNC1(post_release, 0, 0, enum acpihp_dev_post_cmd);
+
+ACPIHP_DEFINE_FUNC(pre_unconfigure, 0, 0);
+ACPIHP_DEFINE_FUNC(unconfigure, 0, -ENOSYS);
+ACPIHP_DEFINE_FUNC(post_unconfigure, 0, 0);
+
/*
* When creating ACPI devices for hot-added system devices connecting to slot,
* we shouldn't cross the slot boundary. Otherwise it will cause inconsistence
@@ -140,6 +140,9 @@ struct acpi_device_ops {
acpi_op_bind bind;
acpi_op_unbind unbind;
acpi_op_notify notify;
+#ifdef CONFIG_ACPI_HOTPLUG
+ struct acpihp_dev_ops *hp_ops;
+#endif /* CONFIG_ACPI_HOTPLUG */
};
#define ACPI_DRIVER_ALL_NOTIFY_EVENTS 0x1 /* system AND device events */
@@ -66,6 +66,51 @@ struct acpihp_dev_node {
struct klist_node node;
};
+/* Status of system devices. */
+#define ACPIHP_DEV_STATUS_ATTACHED 0x1 /* Device driver attached */
+#define ACPIHP_DEV_STATUS_STARTED 0x2 /* Device started */
+#define ACPIHP_DEV_STATUS_IRREMOVABLE 0x10000 /* Device can't be removed */
+#define ACPIHP_DEV_STATUS_FAULT 0x20000 /* Device in fault status */
+
+struct acpihp_dev_info {
+ enum acpihp_dev_type type;
+ uint32_t status;
+};
+
+/* Rollback or commit changes in post_{confiure|release} */
+enum acpihp_dev_post_cmd {
+ ACPIHP_DEV_POST_CMD_ROLLBACK,
+ ACPIHP_DEV_POST_CMD_COMMIT
+};
+
+/*
+ * ACPI system device drivers may check cancellations of hotplug operations
+ * by invoking the callback.
+ */
+struct acpihp_cancel_context {
+ int (*check_cancel)(struct acpihp_cancel_context *ctx);
+};
+
+/*
+ * Callback hooks provided by ACPI device drivers to support system device
+ * hotplug. To support hotplug, an ACPI system device driver must implement
+ * configure(), unconfigure() and get_info() at least.
+ */
+struct acpihp_dev_ops {
+ int (*get_info)(struct acpi_device *, struct acpihp_dev_info *info);
+ int (*pre_configure)(struct acpi_device *,
+ struct acpihp_cancel_context *);
+ int (*configure)(struct acpi_device *, struct acpihp_cancel_context *);
+ int (*post_configure)(struct acpi_device *, enum acpihp_dev_post_cmd);
+ int (*pre_release)(struct acpi_device *,
+ struct acpihp_cancel_context *);
+ int (*release)(struct acpi_device *, struct acpihp_cancel_context *);
+ int (*post_release)(struct acpi_device *, enum acpihp_dev_post_cmd);
+ int (*pre_unconfigure)(struct acpi_device *);
+ int (*unconfigure)(struct acpi_device *);
+ int (*post_unconfigure)(struct acpi_device *);
+};
+
/*
* ACPI hotplug slot is an abstraction of receptacles where a group of
* system devices could be attached, just like PCI slot in PCI hotplug.
@@ -165,6 +210,25 @@ extern int acpihp_register_class(void);
/* Unregister the ACPI hotplug slot class driver */
extern void acpihp_unregister_class(void);
+/* Interfaces to invoke the ACPI device driver's hotplug hooks. */
+extern int acpihp_dev_get_info(struct acpi_device *device,
+ struct acpihp_dev_info *info);
+extern int acpihp_dev_pre_configure(struct acpi_device *device,
+ struct acpihp_cancel_context *ctx);
+extern int acpihp_dev_configure(struct acpi_device *device,
+ struct acpihp_cancel_context *ctx);
+extern int acpihp_dev_post_configure(struct acpi_device *device,
+ enum acpihp_dev_post_cmd cmd);
+extern int acpihp_dev_pre_release(struct acpi_device *device,
+ struct acpihp_cancel_context *ctx);
+extern int acpihp_dev_release(struct acpi_device *device,
+ struct acpihp_cancel_context *ctx);
+extern int acpihp_dev_post_release(struct acpi_device *device,
+ enum acpihp_dev_post_cmd cmd);
+extern int acpihp_dev_pre_unconfigure(struct acpi_device *device);
+extern int acpihp_dev_unconfigure(struct acpi_device *device);
+extern int acpihp_dev_post_unconfigure(struct acpi_device *device);
+
/* Utility routines */
extern int acpihp_dev_get_type(acpi_handle handle, enum acpihp_dev_type *type);
extern bool acpihp_dev_match_ids(struct acpi_device_info *infop, char **ids);