diff options
author | Holger Macht <hmacht@suse.de> | 2008-06-03 14:27:59 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-06-04 06:28:50 -0400 |
commit | 664d080c41463570b95717b5ad86e79dc1be0877 (patch) | |
tree | 6843884af68ebc586200eb5e26f2799f3c5c893e /drivers/ata | |
parent | c3b25b32e8bef526cca748e1ba023c6bdd705a99 (diff) |
[libata] ACPI: Properly handle bay devices in dock stations
* Differentiate between bay devices in dock stations and others:
- When an ACPI_NOTIFY_EJECT_REQUEST appears, just signal uevent to
userspace (that is when the optional eject button on a bay device is
pressed/pulled) giving the possibility to unmount file systems and to
clean up. Also, only send uevent in case we get an EJECT_REQUEST
without doing anything else. In other cases, you'll get an add/remove
event because libata attaches/detaches the device.
- In case of a dock event, which in turn signals an
ACPI_NOTIFY_EJECT_REQUEST, immediately detach the device, because it
may already have been gone
* In case of an ACPI_NOTIFY_DEVICE/BUS_CHECK, evaluate _STA to check if
the device has been plugged or unplugged. If plugged, hotplug it, if
unplugged, just signal event to userspace
(initial patch by Matthew Garrett <mjg59@srcf.ucam.org>)
* Call ACPI _EJ0 for detached devices
Signed-off-by: Holger Macht <hmacht@suse.de>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/libata-acpi.c | 165 |
1 files changed, 114 insertions, 51 deletions
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index dbf6ca781f66..3ff8b14420d9 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c | |||
@@ -118,12 +118,62 @@ static void ata_acpi_associate_ide_port(struct ata_port *ap) | |||
118 | ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; | 118 | ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; |
119 | } | 119 | } |
120 | 120 | ||
121 | static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device | 121 | static void ata_acpi_eject_device(acpi_handle handle) |
122 | *dev, u32 event) | 122 | { |
123 | struct acpi_object_list arg_list; | ||
124 | union acpi_object arg; | ||
125 | |||
126 | arg_list.count = 1; | ||
127 | arg_list.pointer = &arg; | ||
128 | arg.type = ACPI_TYPE_INTEGER; | ||
129 | arg.integer.value = 1; | ||
130 | |||
131 | if (ACPI_FAILURE(acpi_evaluate_object(handle, "_EJ0", | ||
132 | &arg_list, NULL))) | ||
133 | printk(KERN_ERR "Failed to evaluate _EJ0!\n"); | ||
134 | } | ||
135 | |||
136 | /* @ap and @dev are the same as ata_acpi_handle_hotplug() */ | ||
137 | static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev) | ||
138 | { | ||
139 | if (dev) | ||
140 | dev->flags |= ATA_DFLAG_DETACH; | ||
141 | else { | ||
142 | struct ata_link *tlink; | ||
143 | struct ata_device *tdev; | ||
144 | |||
145 | ata_port_for_each_link(tlink, ap) | ||
146 | ata_link_for_each_dev(tdev, tlink) | ||
147 | tdev->flags |= ATA_DFLAG_DETACH; | ||
148 | } | ||
149 | |||
150 | ata_port_schedule_eh(ap); | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * ata_acpi_handle_hotplug - ACPI event handler backend | ||
155 | * @ap: ATA port ACPI event occurred | ||
156 | * @dev: ATA device ACPI event occurred (can be NULL) | ||
157 | * @event: ACPI event which occurred | ||
158 | * @is_dock_event: boolean indicating whether the event was a dock one | ||
159 | * | ||
160 | * All ACPI bay / device realted events end up in this function. If | ||
161 | * the event is port-wide @dev is NULL. If the event is specific to a | ||
162 | * device, @dev points to it. | ||
163 | * | ||
164 | * Hotplug (as opposed to unplug) notification is always handled as | ||
165 | * port-wide while unplug only kills the target device on device-wide | ||
166 | * event. | ||
167 | * | ||
168 | * LOCKING: | ||
169 | * ACPI notify handler context. May sleep. | ||
170 | */ | ||
171 | static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, | ||
172 | u32 event, int is_dock_event) | ||
123 | { | 173 | { |
124 | char event_string[12]; | 174 | char event_string[12]; |
125 | char *envp[] = { event_string, NULL }; | 175 | char *envp[] = { event_string, NULL }; |
126 | struct ata_eh_info *ehi; | 176 | struct ata_eh_info *ehi = &ap->link.eh_info; |
127 | struct kobject *kobj = NULL; | 177 | struct kobject *kobj = NULL; |
128 | int wait = 0; | 178 | int wait = 0; |
129 | unsigned long flags; | 179 | unsigned long flags; |
@@ -131,87 +181,100 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device | |||
131 | unsigned long sta; | 181 | unsigned long sta; |
132 | acpi_status status; | 182 | acpi_status status; |
133 | 183 | ||
134 | if (!ap) | 184 | if (dev) { |
135 | ap = dev->link->ap; | 185 | if (dev->sdev) |
136 | ehi = &ap->link.eh_info; | 186 | kobj = &dev->sdev->sdev_gendev.kobj; |
137 | |||
138 | spin_lock_irqsave(ap->lock, flags); | ||
139 | |||
140 | if (dev) | ||
141 | handle = dev->acpi_handle; | 187 | handle = dev->acpi_handle; |
142 | else | 188 | } else { |
189 | kobj = &ap->dev->kobj; | ||
143 | handle = ap->acpi_handle; | 190 | handle = ap->acpi_handle; |
191 | } | ||
144 | 192 | ||
145 | status = acpi_get_handle(handle, "_EJ0", &tmphandle); | 193 | status = acpi_get_handle(handle, "_EJ0", &tmphandle); |
146 | if (ACPI_FAILURE(status)) { | 194 | if (ACPI_FAILURE(status)) |
147 | /* This device is not ejectable */ | 195 | /* This device does not support hotplug */ |
148 | spin_unlock_irqrestore(ap->lock, flags); | ||
149 | return; | 196 | return; |
150 | } | ||
151 | 197 | ||
152 | status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); | 198 | spin_lock_irqsave(ap->lock, flags); |
153 | if (ACPI_FAILURE(status)) { | ||
154 | printk ("Unable to determine bay status\n"); | ||
155 | spin_unlock_irqrestore(ap->lock, flags); | ||
156 | return; | ||
157 | } | ||
158 | 199 | ||
159 | switch (event) { | 200 | switch (event) { |
160 | case ACPI_NOTIFY_BUS_CHECK: | 201 | case ACPI_NOTIFY_BUS_CHECK: |
161 | case ACPI_NOTIFY_DEVICE_CHECK: | 202 | case ACPI_NOTIFY_DEVICE_CHECK: |
162 | ata_ehi_push_desc(ehi, "ACPI event"); | 203 | ata_ehi_push_desc(ehi, "ACPI event"); |
163 | if (!sta) { | 204 | |
164 | /* Device has been unplugged */ | 205 | status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); |
165 | if (dev) | 206 | if (ACPI_FAILURE(status)) { |
166 | dev->flags |= ATA_DFLAG_DETACH; | 207 | ata_port_printk(ap, KERN_ERR, |
167 | else { | 208 | "acpi: failed to determine bay status (0x%x)\n", |
168 | struct ata_link *tlink; | 209 | status); |
169 | struct ata_device *tdev; | 210 | break; |
170 | 211 | } | |
171 | ata_port_for_each_link(tlink, ap) { | 212 | |
172 | ata_link_for_each_dev(tdev, tlink) { | 213 | if (sta) { |
173 | tdev->flags |= | ||
174 | ATA_DFLAG_DETACH; | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | ata_port_schedule_eh(ap); | ||
179 | wait = 1; | ||
180 | } else { | ||
181 | ata_ehi_hotplugged(ehi); | 214 | ata_ehi_hotplugged(ehi); |
182 | ata_port_freeze(ap); | 215 | ata_port_freeze(ap); |
216 | } else { | ||
217 | /* The device has gone - unplug it */ | ||
218 | ata_acpi_detach_device(ap, dev); | ||
219 | wait = 1; | ||
183 | } | 220 | } |
221 | break; | ||
222 | case ACPI_NOTIFY_EJECT_REQUEST: | ||
223 | ata_ehi_push_desc(ehi, "ACPI event"); | ||
224 | |||
225 | if (!is_dock_event) | ||
226 | break; | ||
227 | |||
228 | /* undock event - immediate unplug */ | ||
229 | ata_acpi_detach_device(ap, dev); | ||
230 | wait = 1; | ||
231 | break; | ||
184 | } | 232 | } |
185 | 233 | ||
234 | /* make sure kobj doesn't go away while ap->lock is released */ | ||
235 | kobject_get(kobj); | ||
236 | |||
186 | spin_unlock_irqrestore(ap->lock, flags); | 237 | spin_unlock_irqrestore(ap->lock, flags); |
187 | 238 | ||
188 | if (wait) | 239 | if (wait) { |
189 | ata_port_wait_eh(ap); | 240 | ata_port_wait_eh(ap); |
241 | ata_acpi_eject_device(handle); | ||
242 | } | ||
190 | 243 | ||
191 | if (dev) { | 244 | if (kobj && !is_dock_event) { |
192 | if (dev->sdev) | ||
193 | kobj = &dev->sdev->sdev_gendev.kobj; | ||
194 | } else | ||
195 | kobj = &ap->dev->kobj; | ||
196 | |||
197 | if (kobj) { | ||
198 | sprintf(event_string, "BAY_EVENT=%d", event); | 245 | sprintf(event_string, "BAY_EVENT=%d", event); |
199 | kobject_uevent_env(kobj, KOBJ_CHANGE, envp); | 246 | kobject_uevent_env(kobj, KOBJ_CHANGE, envp); |
200 | } | 247 | } |
248 | |||
249 | kobject_put(kobj); | ||
250 | } | ||
251 | |||
252 | static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data) | ||
253 | { | ||
254 | struct ata_device *dev = data; | ||
255 | |||
256 | ata_acpi_handle_hotplug(dev->link->ap, dev, event, 1); | ||
257 | } | ||
258 | |||
259 | static void ata_acpi_ap_notify_dock(acpi_handle handle, u32 event, void *data) | ||
260 | { | ||
261 | struct ata_port *ap = data; | ||
262 | |||
263 | ata_acpi_handle_hotplug(ap, NULL, event, 1); | ||
201 | } | 264 | } |
202 | 265 | ||
203 | static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data) | 266 | static void ata_acpi_dev_notify(acpi_handle handle, u32 event, void *data) |
204 | { | 267 | { |
205 | struct ata_device *dev = data; | 268 | struct ata_device *dev = data; |
206 | 269 | ||
207 | ata_acpi_handle_hotplug(NULL, dev, event); | 270 | ata_acpi_handle_hotplug(dev->link->ap, dev, event, 0); |
208 | } | 271 | } |
209 | 272 | ||
210 | static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data) | 273 | static void ata_acpi_ap_notify(acpi_handle handle, u32 event, void *data) |
211 | { | 274 | { |
212 | struct ata_port *ap = data; | 275 | struct ata_port *ap = data; |
213 | 276 | ||
214 | ata_acpi_handle_hotplug(ap, NULL, event); | 277 | ata_acpi_handle_hotplug(ap, NULL, event, 0); |
215 | } | 278 | } |
216 | 279 | ||
217 | /** | 280 | /** |
@@ -252,7 +315,7 @@ void ata_acpi_associate(struct ata_host *host) | |||
252 | ata_acpi_ap_notify, ap); | 315 | ata_acpi_ap_notify, ap); |
253 | /* we might be on a docking station */ | 316 | /* we might be on a docking station */ |
254 | register_hotplug_dock_device(ap->acpi_handle, | 317 | register_hotplug_dock_device(ap->acpi_handle, |
255 | ata_acpi_ap_notify, ap); | 318 | ata_acpi_ap_notify_dock, ap); |
256 | } | 319 | } |
257 | 320 | ||
258 | for (j = 0; j < ata_link_max_devices(&ap->link); j++) { | 321 | for (j = 0; j < ata_link_max_devices(&ap->link); j++) { |
@@ -264,7 +327,7 @@ void ata_acpi_associate(struct ata_host *host) | |||
264 | ata_acpi_dev_notify, dev); | 327 | ata_acpi_dev_notify, dev); |
265 | /* we might be on a docking station */ | 328 | /* we might be on a docking station */ |
266 | register_hotplug_dock_device(dev->acpi_handle, | 329 | register_hotplug_dock_device(dev->acpi_handle, |
267 | ata_acpi_dev_notify, dev); | 330 | ata_acpi_dev_notify_dock, dev); |
268 | } | 331 | } |
269 | } | 332 | } |
270 | } | 333 | } |