]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * acpi_container.c - ACPI Generic Container Driver | |
3 | * ($Revision: ) | |
4 | * | |
5 | * Copyright (C) 2004 Anil S Keshavamurthy ([email protected]) | |
6 | * Copyright (C) 2004 Keiichiro Tokunaga ([email protected]) | |
7 | * Copyright (C) 2004 Motoyuki Ito ([email protected]) | |
8 | * Copyright (C) 2004 Intel Corp. | |
9 | * Copyright (C) 2004 FUJITSU LIMITED | |
10 | * | |
11 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify | |
14 | * it under the terms of the GNU General Public License as published by | |
15 | * the Free Software Foundation; either version 2 of the License, or (at | |
16 | * your option) any later version. | |
17 | * | |
18 | * This program is distributed in the hope that it will be useful, but | |
19 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
21 | * General Public License for more details. | |
22 | * | |
23 | * You should have received a copy of the GNU General Public License along | |
24 | * with this program; if not, write to the Free Software Foundation, Inc., | |
25 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
26 | * | |
27 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
28 | */ | |
29 | #include <linux/kernel.h> | |
30 | #include <linux/module.h> | |
31 | #include <linux/init.h> | |
5a0e3ad6 | 32 | #include <linux/slab.h> |
1da177e4 LT |
33 | #include <linux/types.h> |
34 | #include <linux/acpi.h> | |
35 | #include <acpi/acpi_bus.h> | |
36 | #include <acpi/acpi_drivers.h> | |
1da177e4 | 37 | |
a192a958 LB |
38 | #define PREFIX "ACPI: " |
39 | ||
1da177e4 | 40 | #define _COMPONENT ACPI_CONTAINER_COMPONENT |
f52fd66d | 41 | ACPI_MODULE_NAME("container"); |
1da177e4 | 42 | |
1ba90e3a TR |
43 | static const struct acpi_device_id container_device_ids[] = { |
44 | {"ACPI0004", 0}, | |
45 | {"PNP0A05", 0}, | |
46 | {"PNP0A06", 0}, | |
47 | {"", 0}, | |
48 | }; | |
1ba90e3a | 49 | |
737f1a9f RW |
50 | static int container_device_attach(struct acpi_device *device, |
51 | const struct acpi_device_id *not_used) | |
52 | { | |
53 | /* | |
54 | * FIXME: This is necessary, so that acpi_eject_store() doesn't return | |
55 | * -ENODEV for containers. | |
56 | */ | |
57 | return 1; | |
58 | } | |
59 | ||
60 | static struct acpi_scan_handler container_device_handler = { | |
1ba90e3a | 61 | .ids = container_device_ids, |
737f1a9f | 62 | .attach = container_device_attach, |
1da177e4 LT |
63 | }; |
64 | ||
4be44fcd | 65 | static int is_device_present(acpi_handle handle) |
1da177e4 | 66 | { |
4be44fcd LB |
67 | acpi_handle temp; |
68 | acpi_status status; | |
27663c58 | 69 | unsigned long long sta; |
1da177e4 | 70 | |
1da177e4 LT |
71 | |
72 | status = acpi_get_handle(handle, "_STA", &temp); | |
73 | if (ACPI_FAILURE(status)) | |
a0bd4ac4 | 74 | return 1; /* _STA not found, assume device present */ |
1da177e4 LT |
75 | |
76 | status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); | |
77 | if (ACPI_FAILURE(status)) | |
d550d98d | 78 | return 0; /* Firmware error */ |
1da177e4 | 79 | |
a0bd4ac4 | 80 | return ((sta & ACPI_STA_DEVICE_PRESENT) == ACPI_STA_DEVICE_PRESENT); |
1da177e4 LT |
81 | } |
82 | ||
4be44fcd | 83 | static void container_notify_cb(acpi_handle handle, u32 type, void *context) |
1da177e4 | 84 | { |
4be44fcd | 85 | struct acpi_device *device = NULL; |
1da177e4 LT |
86 | int result; |
87 | int present; | |
88 | acpi_status status; | |
0c67dc24 | 89 | u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ |
4be44fcd | 90 | |
3757b948 RW |
91 | acpi_scan_lock_acquire(); |
92 | ||
1da177e4 LT |
93 | switch (type) { |
94 | case ACPI_NOTIFY_BUS_CHECK: | |
95 | /* Fall through */ | |
96 | case ACPI_NOTIFY_DEVICE_CHECK: | |
3d78bd9e | 97 | pr_debug("Container driver received %s event\n", |
4be44fcd LB |
98 | (type == ACPI_NOTIFY_BUS_CHECK) ? |
99 | "ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK"); | |
0c67dc24 TK |
100 | |
101 | present = is_device_present(handle); | |
1da177e4 | 102 | status = acpi_bus_get_device(handle, &device); |
0c67dc24 | 103 | if (!present) { |
1da177e4 LT |
104 | if (ACPI_SUCCESS(status)) { |
105 | /* device exist and this is a remove request */ | |
0c67dc24 | 106 | device->flags.eject_pending = 1; |
f883d9db | 107 | kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); |
3757b948 | 108 | goto out; |
1da177e4 | 109 | } |
0c67dc24 TK |
110 | break; |
111 | } | |
112 | ||
113 | if (!ACPI_FAILURE(status) || device) | |
114 | break; | |
115 | ||
b8bd759a | 116 | result = acpi_bus_scan(handle); |
0c67dc24 | 117 | if (result) { |
3d78bd9e | 118 | acpi_handle_warn(handle, "Failed to add container\n"); |
0c67dc24 | 119 | break; |
1da177e4 | 120 | } |
0cd6ac52 RW |
121 | result = acpi_bus_get_device(handle, &device); |
122 | if (result) { | |
123 | acpi_handle_warn(handle, "Missing device object\n"); | |
124 | break; | |
125 | } | |
0c67dc24 TK |
126 | |
127 | kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); | |
128 | ost_code = ACPI_OST_SC_SUCCESS; | |
1da177e4 | 129 | break; |
0c67dc24 | 130 | |
1da177e4 LT |
131 | case ACPI_NOTIFY_EJECT_REQUEST: |
132 | if (!acpi_bus_get_device(handle, &device) && device) { | |
0c67dc24 | 133 | device->flags.eject_pending = 1; |
f883d9db | 134 | kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); |
3757b948 | 135 | goto out; |
1da177e4 LT |
136 | } |
137 | break; | |
0c67dc24 | 138 | |
1da177e4 | 139 | default: |
0c67dc24 | 140 | /* non-hotplug event; possibly handled by other handler */ |
3757b948 | 141 | goto out; |
1da177e4 | 142 | } |
0c67dc24 TK |
143 | |
144 | /* Inform firmware that the hotplug operation has completed */ | |
145 | (void) acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL); | |
3757b948 RW |
146 | |
147 | out: | |
148 | acpi_scan_lock_release(); | |
1da177e4 LT |
149 | } |
150 | ||
737f1a9f | 151 | static bool is_container(acpi_handle handle) |
1da177e4 | 152 | { |
4be44fcd | 153 | struct acpi_device_info *info; |
737f1a9f | 154 | bool ret = false; |
1da177e4 | 155 | |
737f1a9f RW |
156 | if (ACPI_FAILURE(acpi_get_object_info(handle, &info))) |
157 | return false; | |
1da177e4 | 158 | |
737f1a9f RW |
159 | if (info->valid & ACPI_VALID_HID) { |
160 | const struct acpi_device_id *id; | |
1da177e4 | 161 | |
737f1a9f RW |
162 | for (id = container_device_ids; id->id[0]; id++) { |
163 | ret = !strcmp((char *)id->id, info->hardware_id.string); | |
164 | if (ret) | |
165 | break; | |
166 | } | |
1da177e4 | 167 | } |
15b8dd53 | 168 | kfree(info); |
737f1a9f | 169 | return ret; |
1da177e4 LT |
170 | } |
171 | ||
737f1a9f RW |
172 | static acpi_status acpi_container_register_notify_handler(acpi_handle handle, |
173 | u32 lvl, void *ctxt, | |
174 | void **retv) | |
1da177e4 | 175 | { |
737f1a9f RW |
176 | if (is_container(handle)) |
177 | acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, | |
178 | container_notify_cb, NULL); | |
1da177e4 | 179 | |
737f1a9f | 180 | return AE_OK; |
1da177e4 LT |
181 | } |
182 | ||
737f1a9f | 183 | void __init acpi_container_init(void) |
1da177e4 | 184 | { |
737f1a9f RW |
185 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, |
186 | acpi_container_register_notify_handler, NULL, | |
187 | NULL, NULL); | |
1da177e4 | 188 | |
737f1a9f | 189 | acpi_scan_add_handler(&container_device_handler); |
1da177e4 | 190 | } |