| /* | |
| * container.c - ACPI Generic Container Driver | |
| * | |
| * Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com) | |
| * Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com) | |
| * Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com) | |
| * Copyright (C) 2004 FUJITSU LIMITED | |
| * Copyright (C) 2004, 2013 Intel Corp. | |
| * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> | |
| * | |
| * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; either version 2 of the License, or (at | |
| * your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, but | |
| * WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * General Public License for more details. | |
| * | |
| * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| */ | |
| #include <linux/acpi.h> | |
| #include <linux/container.h> | |
| #include "internal.h" | |
| #define _COMPONENT ACPI_CONTAINER_COMPONENT | |
| ACPI_MODULE_NAME("container"); | |
| static const struct acpi_device_id container_device_ids[] = { | |
| {"ACPI0004", 0}, | |
| {"PNP0A05", 0}, | |
| {"PNP0A06", 0}, | |
| {"", 0}, | |
| }; | |
| #ifdef CONFIG_ACPI_CONTAINER | |
| static int acpi_container_offline(struct container_dev *cdev) | |
| { | |
| struct acpi_device *adev = ACPI_COMPANION(&cdev->dev); | |
| struct acpi_device *child; | |
| /* Check all of the dependent devices' physical companions. */ | |
| list_for_each_entry(child, &adev->children, node) | |
| if (!acpi_scan_is_offline(child, false)) | |
| return -EBUSY; | |
| return 0; | |
| } | |
| static void acpi_container_release(struct device *dev) | |
| { | |
| kfree(to_container_dev(dev)); | |
| } | |
| static int container_device_attach(struct acpi_device *adev, | |
| const struct acpi_device_id *not_used) | |
| { | |
| struct container_dev *cdev; | |
| struct device *dev; | |
| int ret; | |
| if (adev->flags.is_dock_station) | |
| return 0; | |
| cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); | |
| if (!cdev) | |
| return -ENOMEM; | |
| cdev->offline = acpi_container_offline; | |
| dev = &cdev->dev; | |
| dev->bus = &container_subsys; | |
| dev_set_name(dev, "%s", dev_name(&adev->dev)); | |
| ACPI_COMPANION_SET(dev, adev); | |
| dev->release = acpi_container_release; | |
| ret = device_register(dev); | |
| if (ret) { | |
| put_device(dev); | |
| return ret; | |
| } | |
| adev->driver_data = dev; | |
| return 1; | |
| } | |
| static void container_device_detach(struct acpi_device *adev) | |
| { | |
| struct device *dev = acpi_driver_data(adev); | |
| adev->driver_data = NULL; | |
| if (dev) | |
| device_unregister(dev); | |
| } | |
| static void container_device_online(struct acpi_device *adev) | |
| { | |
| struct device *dev = acpi_driver_data(adev); | |
| kobject_uevent(&dev->kobj, KOBJ_ONLINE); | |
| } | |
| static struct acpi_scan_handler container_handler = { | |
| .ids = container_device_ids, | |
| .attach = container_device_attach, | |
| .detach = container_device_detach, | |
| .hotplug = { | |
| .enabled = true, | |
| .demand_offline = true, | |
| .notify_online = container_device_online, | |
| }, | |
| }; | |
| void __init acpi_container_init(void) | |
| { | |
| acpi_scan_add_handler(&container_handler); | |
| } | |
| #else | |
| static struct acpi_scan_handler container_handler = { | |
| .ids = container_device_ids, | |
| }; | |
| void __init acpi_container_init(void) | |
| { | |
| acpi_scan_add_handler_with_hotplug(&container_handler, "container"); | |
| } | |
| #endif /* CONFIG_ACPI_CONTAINER */ |