]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Shaohua Li <shaohua.li@intel.com> |
2 | Subject: makeing dock driver supports bay and battery hotplug | |
3 | Patch-mainline: submitted 2008-08-28 | |
4 | References: fate#304731,bnc#401740 | |
5 | ||
6 | Making dock driver supports bay and battery hotplug. They are all | |
7 | regarded as dock, and unified handled. | |
8 | ||
9 | Signed-off-by: Shaohua Li <shaohua.li@intel.com> | |
10 | Signed-off-by: Holger Macht <hmacht@suse.de> | |
11 | --- | |
12 | ||
13 | --- | |
14 | drivers/acpi/dock.c | 221 ++++++++++++++++++++++++++++++++++++++++------------ | |
15 | 1 file changed, 173 insertions(+), 48 deletions(-) | |
16 | ||
17 | --- a/drivers/acpi/dock.c | |
18 | +++ b/drivers/acpi/dock.c | |
19 | @@ -48,7 +48,6 @@ MODULE_PARM_DESC(immediate_undock, "1 (d | |
20 | " before undocking"); | |
21 | ||
22 | static struct atomic_notifier_head dock_notifier_list; | |
23 | -static struct platform_device *dock_device; | |
24 | static char dock_device_name[] = "dock"; | |
25 | ||
26 | static const struct acpi_device_id dock_device_ids[] = { | |
27 | @@ -65,7 +64,12 @@ struct dock_station { | |
28 | struct mutex hp_lock; | |
29 | struct list_head dependent_devices; | |
30 | struct list_head hotplug_devices; | |
31 | + | |
32 | + struct list_head sibiling; | |
33 | + struct platform_device *dock_device; | |
34 | }; | |
35 | +static LIST_HEAD(dock_stations); | |
36 | +static int dock_station_count; | |
37 | ||
38 | struct dock_dependent_device { | |
39 | struct list_head list; | |
40 | @@ -77,11 +81,12 @@ struct dock_dependent_device { | |
41 | ||
42 | #define DOCK_DOCKING 0x00000001 | |
43 | #define DOCK_UNDOCKING 0x00000002 | |
44 | +#define DOCK_IS_DOCK 0x00000010 | |
45 | +#define DOCK_IS_ATA 0x00000020 | |
46 | +#define DOCK_IS_BAT 0x00000040 | |
47 | #define DOCK_EVENT 3 | |
48 | #define UNDOCK_EVENT 2 | |
49 | ||
50 | -static struct dock_station *dock_station; | |
51 | - | |
52 | /***************************************************************************** | |
53 | * Dock Dependent device functions * | |
54 | *****************************************************************************/ | |
55 | @@ -199,6 +204,60 @@ static int is_dock(acpi_handle handle) | |
56 | return 1; | |
57 | } | |
58 | ||
59 | +static int is_ejectable(acpi_handle handle) | |
60 | +{ | |
61 | + acpi_status status; | |
62 | + acpi_handle tmp; | |
63 | + | |
64 | + status = acpi_get_handle(handle, "_EJ0", &tmp); | |
65 | + if (ACPI_FAILURE(status)) | |
66 | + return 0; | |
67 | + return 1; | |
68 | +} | |
69 | + | |
70 | +static int is_ata(acpi_handle handle) | |
71 | +{ | |
72 | + acpi_handle tmp; | |
73 | + | |
74 | + if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) || | |
75 | + (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) || | |
76 | + (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) || | |
77 | + (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp)))) | |
78 | + return 1; | |
79 | + | |
80 | + return 0; | |
81 | +} | |
82 | + | |
83 | +static int is_battery(acpi_handle handle) | |
84 | +{ | |
85 | + struct acpi_device_info *info; | |
86 | + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | |
87 | + int ret = 1; | |
88 | + | |
89 | + if (!ACPI_SUCCESS(acpi_get_object_info(handle, &buffer))) | |
90 | + return 0; | |
91 | + info = buffer.pointer; | |
92 | + if (!(info->valid & ACPI_VALID_HID)) | |
93 | + ret = 0; | |
94 | + else | |
95 | + ret = !strcmp("PNP0C0A", info->hardware_id.value); | |
96 | + | |
97 | + kfree(buffer.pointer); | |
98 | + return ret; | |
99 | +} | |
100 | + | |
101 | +static int is_ejectable_bay(acpi_handle handle) | |
102 | +{ | |
103 | + acpi_handle phandle; | |
104 | + if (!is_ejectable(handle)) | |
105 | + return 0; | |
106 | + if (is_battery(handle) || is_ata(handle)) | |
107 | + return 1; | |
108 | + if (!acpi_get_parent(handle, &phandle) && is_ata(phandle)) | |
109 | + return 1; | |
110 | + return 0; | |
111 | +} | |
112 | + | |
113 | /** | |
114 | * is_dock_device - see if a device is on a dock station | |
115 | * @handle: acpi handle of the device | |
116 | @@ -209,11 +268,17 @@ static int is_dock(acpi_handle handle) | |
117 | */ | |
118 | int is_dock_device(acpi_handle handle) | |
119 | { | |
120 | - if (!dock_station) | |
121 | + struct dock_station *dock_station; | |
122 | + | |
123 | + if (!dock_station_count) | |
124 | return 0; | |
125 | ||
126 | - if (is_dock(handle) || find_dock_dependent_device(dock_station, handle)) | |
127 | + if (is_dock(handle)) | |
128 | return 1; | |
129 | + list_for_each_entry(dock_station, &dock_stations, sibiling) { | |
130 | + if (find_dock_dependent_device(dock_station, handle)) | |
131 | + return 1; | |
132 | + } | |
133 | ||
134 | return 0; | |
135 | } | |
136 | @@ -341,7 +406,7 @@ static void hotplug_dock_devices(struct | |
137 | ||
138 | static void dock_event(struct dock_station *ds, u32 event, int num) | |
139 | { | |
140 | - struct device *dev = &dock_device->dev; | |
141 | + struct device *dev = &ds->dock_device->dev; | |
142 | char event_string[13]; | |
143 | char *envp[] = { event_string, NULL }; | |
144 | ||
145 | @@ -414,7 +479,7 @@ static void handle_dock(struct dock_stat | |
146 | arg.type = ACPI_TYPE_INTEGER; | |
147 | arg.integer.value = dock; | |
148 | status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer); | |
149 | - if (ACPI_FAILURE(status)) | |
150 | + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) | |
151 | printk(KERN_ERR PREFIX "%s - failed to execute _DCK\n", | |
152 | (char *)name_buffer.pointer); | |
153 | kfree(buffer.pointer); | |
154 | @@ -498,7 +563,7 @@ static int dock_in_progress(struct dock_ | |
155 | */ | |
156 | int register_dock_notifier(struct notifier_block *nb) | |
157 | { | |
158 | - if (!dock_station) | |
159 | + if (!dock_station_count) | |
160 | return -ENODEV; | |
161 | ||
162 | return atomic_notifier_chain_register(&dock_notifier_list, nb); | |
163 | @@ -512,7 +577,7 @@ EXPORT_SYMBOL_GPL(register_dock_notifier | |
164 | */ | |
165 | void unregister_dock_notifier(struct notifier_block *nb) | |
166 | { | |
167 | - if (!dock_station) | |
168 | + if (!dock_station_count) | |
169 | return; | |
170 | ||
171 | atomic_notifier_chain_unregister(&dock_notifier_list, nb); | |
172 | @@ -535,20 +600,23 @@ register_hotplug_dock_device(acpi_handle | |
173 | void *context) | |
174 | { | |
175 | struct dock_dependent_device *dd; | |
176 | + struct dock_station *dock_station; | |
177 | ||
178 | - if (!dock_station) | |
179 | + if (!dock_station_count) | |
180 | return -ENODEV; | |
181 | ||
182 | /* | |
183 | * make sure this handle is for a device dependent on the dock, | |
184 | * this would include the dock station itself | |
185 | */ | |
186 | - dd = find_dock_dependent_device(dock_station, handle); | |
187 | - if (dd) { | |
188 | - dd->handler = handler; | |
189 | - dd->context = context; | |
190 | - dock_add_hotplug_device(dock_station, dd); | |
191 | - return 0; | |
192 | + list_for_each_entry(dock_station, &dock_stations, sibiling) { | |
193 | + dd = find_dock_dependent_device(dock_station, handle); | |
194 | + if (dd) { | |
195 | + dd->handler = handler; | |
196 | + dd->context = context; | |
197 | + dock_add_hotplug_device(dock_station, dd); | |
198 | + return 0; | |
199 | + } | |
200 | } | |
201 | ||
202 | return -EINVAL; | |
203 | @@ -563,13 +631,16 @@ EXPORT_SYMBOL_GPL(register_hotplug_dock_ | |
204 | void unregister_hotplug_dock_device(acpi_handle handle) | |
205 | { | |
206 | struct dock_dependent_device *dd; | |
207 | + struct dock_station *dock_station; | |
208 | ||
209 | - if (!dock_station) | |
210 | + if (!dock_station_count) | |
211 | return; | |
212 | ||
213 | - dd = find_dock_dependent_device(dock_station, handle); | |
214 | - if (dd) | |
215 | - dock_del_hotplug_device(dock_station, dd); | |
216 | + list_for_each_entry(dock_station, &dock_stations, sibiling) { | |
217 | + dd = find_dock_dependent_device(dock_station, handle); | |
218 | + if (dd) | |
219 | + dock_del_hotplug_device(dock_station, dd); | |
220 | + } | |
221 | } | |
222 | ||
223 | EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); | |
224 | @@ -620,9 +691,28 @@ static void dock_notify(acpi_handle hand | |
225 | { | |
226 | struct dock_station *ds = data; | |
227 | struct acpi_device *tmp; | |
228 | + int surprise_removal = 0; | |
229 | ||
230 | + /* | |
231 | + * According to acpi spec 3.0a, if a DEVICE_CHECK notification | |
232 | + * is sent and _DCK is present, it is assumed to mean an undock | |
233 | + * request. | |
234 | + */ | |
235 | + if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK) | |
236 | + event = ACPI_NOTIFY_EJECT_REQUEST; | |
237 | + | |
238 | + /* | |
239 | + * dock station: BUS_CHECK - docked or surprise removal | |
240 | + * DEVICE_CHECK - undocked | |
241 | + * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal | |
242 | + * | |
243 | + * To simplify event handling, dock dependent device handler always | |
244 | + * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and | |
245 | + * ACPI_NOTIFY_EJECT_REQUEST for removal | |
246 | + */ | |
247 | switch (event) { | |
248 | case ACPI_NOTIFY_BUS_CHECK: | |
249 | + case ACPI_NOTIFY_DEVICE_CHECK: | |
250 | if (!dock_in_progress(ds) && acpi_bus_get_device(ds->handle, | |
251 | &tmp)) { | |
252 | begin_dock(ds); | |
253 | @@ -638,20 +728,17 @@ static void dock_notify(acpi_handle hand | |
254 | complete_dock(ds); | |
255 | dock_event(ds, event, DOCK_EVENT); | |
256 | dock_lock(ds, 1); | |
257 | + break; | |
258 | } | |
259 | - break; | |
260 | - case ACPI_NOTIFY_DEVICE_CHECK: | |
261 | - /* | |
262 | - * According to acpi spec 3.0a, if a DEVICE_CHECK notification | |
263 | - * is sent and _DCK is present, it is assumed to mean an | |
264 | - * undock request. This notify routine will only be called | |
265 | - * for objects defining _DCK, so we will fall through to eject | |
266 | - * request here. However, we will pass an eject request through | |
267 | - * to the driver who wish to hotplug. | |
268 | - */ | |
269 | + if (dock_present(ds) || dock_in_progress(ds)) | |
270 | + break; | |
271 | + /* This is a surprise removal */ | |
272 | + surprise_removal = 1; | |
273 | + event = ACPI_NOTIFY_EJECT_REQUEST; | |
274 | + /* Fall back */ | |
275 | case ACPI_NOTIFY_EJECT_REQUEST: | |
276 | begin_undock(ds); | |
277 | - if (immediate_undock) | |
278 | + if (immediate_undock || surprise_removal) | |
279 | handle_eject_request(ds, event); | |
280 | else | |
281 | dock_event(ds, event, UNDOCK_EVENT); | |
282 | @@ -718,6 +805,8 @@ static DEVICE_ATTR(docked, S_IRUGO, show | |
283 | static ssize_t show_flags(struct device *dev, | |
284 | struct device_attribute *attr, char *buf) | |
285 | { | |
286 | + struct dock_station *dock_station = *((struct dock_station **) | |
287 | + dev->platform_data); | |
288 | return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags); | |
289 | ||
290 | } | |
291 | @@ -730,6 +819,8 @@ static ssize_t write_undock(struct devic | |
292 | const char *buf, size_t count) | |
293 | { | |
294 | int ret; | |
295 | + struct dock_station *dock_station = *((struct dock_station **) | |
296 | + dev->platform_data); | |
297 | ||
298 | if (!count) | |
299 | return -EINVAL; | |
300 | @@ -747,6 +838,8 @@ static ssize_t show_dock_uid(struct devi | |
301 | struct device_attribute *attr, char *buf) | |
302 | { | |
303 | unsigned long long lbuf; | |
304 | + struct dock_station *dock_station = *((struct dock_station **) | |
305 | + dev->platform_data); | |
306 | acpi_status status = acpi_evaluate_integer(dock_station->handle, | |
307 | "_UID", NULL, &lbuf); | |
308 | if (ACPI_FAILURE(status)) | |
309 | @@ -768,6 +861,8 @@ static int dock_add(acpi_handle handle) | |
310 | int ret; | |
311 | acpi_status status; | |
312 | struct dock_dependent_device *dd; | |
313 | + struct dock_station *dock_station; | |
314 | + struct platform_device *dock_device; | |
315 | ||
316 | /* allocate & initialize the dock_station private data */ | |
317 | dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL); | |
318 | @@ -777,22 +872,34 @@ static int dock_add(acpi_handle handle) | |
319 | dock_station->last_dock_time = jiffies - HZ; | |
320 | INIT_LIST_HEAD(&dock_station->dependent_devices); | |
321 | INIT_LIST_HEAD(&dock_station->hotplug_devices); | |
322 | + INIT_LIST_HEAD(&dock_station->sibiling); | |
323 | spin_lock_init(&dock_station->dd_lock); | |
324 | mutex_init(&dock_station->hp_lock); | |
325 | ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); | |
326 | ||
327 | /* initialize platform device stuff */ | |
328 | - dock_device = | |
329 | - platform_device_register_simple(dock_device_name, 0, NULL, 0); | |
330 | + dock_station->dock_device = | |
331 | + platform_device_register_simple(dock_device_name, | |
332 | + dock_station_count, NULL, 0); | |
333 | + dock_device = dock_station->dock_device; | |
334 | if (IS_ERR(dock_device)) { | |
335 | kfree(dock_station); | |
336 | dock_station = NULL; | |
337 | return PTR_ERR(dock_device); | |
338 | } | |
339 | + platform_device_add_data(dock_device, &dock_station, | |
340 | + sizeof(struct dock_station *)); | |
341 | ||
342 | /* we want the dock device to send uevents */ | |
343 | dock_device->dev.uevent_suppress = 0; | |
344 | ||
345 | + if (is_dock(handle)) | |
346 | + dock_station->flags |= DOCK_IS_DOCK; | |
347 | + if (is_ata(handle)) | |
348 | + dock_station->flags |= DOCK_IS_ATA; | |
349 | + if (is_battery(handle)) | |
350 | + dock_station->flags |= DOCK_IS_BAT; | |
351 | + | |
352 | ret = device_create_file(&dock_device->dev, &dev_attr_docked); | |
353 | if (ret) { | |
354 | printk("Error %d adding sysfs file\n", ret); | |
355 | @@ -858,8 +965,8 @@ static int dock_add(acpi_handle handle) | |
356 | goto dock_add_err; | |
357 | } | |
358 | ||
359 | - printk(KERN_INFO PREFIX "%s\n", ACPI_DOCK_DRIVER_DESCRIPTION); | |
360 | - | |
361 | + dock_station_count++; | |
362 | + list_add(&dock_station->sibiling, &dock_stations); | |
363 | return 0; | |
364 | ||
365 | dock_add_err: | |
366 | @@ -878,12 +985,13 @@ dock_add_err_unregister: | |
367 | /** | |
368 | * dock_remove - free up resources related to the dock station | |
369 | */ | |
370 | -static int dock_remove(void) | |
371 | +static int dock_remove(struct dock_station *dock_station) | |
372 | { | |
373 | struct dock_dependent_device *dd, *tmp; | |
374 | acpi_status status; | |
375 | + struct platform_device *dock_device = dock_station->dock_device; | |
376 | ||
377 | - if (!dock_station) | |
378 | + if (!dock_station_count) | |
379 | return 0; | |
380 | ||
381 | /* remove dependent devices */ | |
382 | @@ -923,41 +1031,58 @@ static int dock_remove(void) | |
383 | static acpi_status | |
384 | find_dock(acpi_handle handle, u32 lvl, void *context, void **rv) | |
385 | { | |
386 | - int *count = context; | |
387 | acpi_status status = AE_OK; | |
388 | ||
389 | if (is_dock(handle)) { | |
390 | if (dock_add(handle) >= 0) { | |
391 | - (*count)++; | |
392 | status = AE_CTRL_TERMINATE; | |
393 | } | |
394 | } | |
395 | return status; | |
396 | } | |
397 | ||
398 | -static int __init dock_init(void) | |
399 | +static acpi_status | |
400 | +find_bay(acpi_handle handle, u32 lvl, void *context, void **rv) | |
401 | { | |
402 | - int num = 0; | |
403 | - | |
404 | - dock_station = NULL; | |
405 | + /* If bay is in a dock, it's already handled */ | |
406 | + if (is_ejectable_bay(handle) && !is_dock_device(handle)) | |
407 | + dock_add(handle); | |
408 | + return AE_OK; | |
409 | +} | |
410 | ||
411 | +static int __init dock_init(void) | |
412 | +{ | |
413 | if (acpi_disabled) | |
414 | return 0; | |
415 | ||
416 | /* look for a dock station */ | |
417 | acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | |
418 | - ACPI_UINT32_MAX, find_dock, &num, NULL); | |
419 | + ACPI_UINT32_MAX, find_dock, NULL, NULL); | |
420 | ||
421 | - if (!num) | |
422 | - printk(KERN_INFO "No dock devices found.\n"); | |
423 | + /* look for bay */ | |
424 | + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, | |
425 | + ACPI_UINT32_MAX, find_bay, NULL, NULL); | |
426 | + if (!dock_station_count) { | |
427 | + printk(KERN_INFO PREFIX "No dock devices found.\n"); | |
428 | + return 0; | |
429 | + } | |
430 | ||
431 | + printk(KERN_INFO PREFIX "%s: %d docks/bays found\n", | |
432 | + ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count); | |
433 | return 0; | |
434 | } | |
435 | ||
436 | static void __exit dock_exit(void) | |
437 | { | |
438 | - dock_remove(); | |
439 | + struct dock_station *dock_station; | |
440 | + | |
441 | + list_for_each_entry(dock_station, &dock_stations, sibiling) | |
442 | + dock_remove(dock_station); | |
443 | } | |
444 | ||
445 | -postcore_initcall(dock_init); | |
446 | +/* | |
447 | + * Must be called before drivers of devices in dock, otherwise we can't know | |
448 | + * which devices are in a dock | |
449 | + */ | |
450 | +subsys_initcall(dock_init); | |
451 | module_exit(dock_exit); |