635 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * dock.c - ACPI dock station driver4 *5 * Copyright (C) 2006, 2014, Intel Corp.6 * Author: Kristen Carlson Accardi <kristen.c.accardi@intel.com>7 * Rafael J. Wysocki <rafael.j.wysocki@intel.com>8 */9 10#include <linux/kernel.h>11#include <linux/moduleparam.h>12#include <linux/slab.h>13#include <linux/init.h>14#include <linux/types.h>15#include <linux/notifier.h>16#include <linux/platform_device.h>17#include <linux/jiffies.h>18#include <linux/stddef.h>19#include <linux/acpi.h>20 21#include "internal.h"22 23static bool immediate_undock = 1;24module_param(immediate_undock, bool, 0644);25MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "26 "undock immediately when the undock button is pressed, 0 will cause"27 " the driver to wait for userspace to write the undock sysfs file "28 " before undocking");29 30struct dock_station {31 acpi_handle handle;32 unsigned long last_dock_time;33 u32 flags;34 struct list_head dependent_devices;35 36 struct list_head sibling;37 struct platform_device *dock_device;38};39static LIST_HEAD(dock_stations);40static int dock_station_count;41 42struct dock_dependent_device {43 struct list_head list;44 struct acpi_device *adev;45};46 47#define DOCK_DOCKING 0x0000000148#define DOCK_UNDOCKING 0x0000000249#define DOCK_IS_DOCK 0x0000001050#define DOCK_IS_ATA 0x0000002051#define DOCK_IS_BAT 0x0000004052#define DOCK_EVENT 353#define UNDOCK_EVENT 254 55enum dock_callback_type {56 DOCK_CALL_HANDLER,57 DOCK_CALL_FIXUP,58 DOCK_CALL_UEVENT,59};60 61/*****************************************************************************62 * Dock Dependent device functions *63 *****************************************************************************/64/**65 * add_dock_dependent_device - associate a device with the dock station66 * @ds: Dock station.67 * @adev: Dependent ACPI device object.68 *69 * Add the dependent device to the dock's dependent device list.70 */71static int add_dock_dependent_device(struct dock_station *ds,72 struct acpi_device *adev)73{74 struct dock_dependent_device *dd;75 76 dd = kzalloc(sizeof(*dd), GFP_KERNEL);77 if (!dd)78 return -ENOMEM;79 80 dd->adev = adev;81 INIT_LIST_HEAD(&dd->list);82 list_add_tail(&dd->list, &ds->dependent_devices);83 84 return 0;85}86 87static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,88 enum dock_callback_type cb_type)89{90 struct acpi_device *adev = dd->adev;91 acpi_hp_fixup fixup = NULL;92 acpi_hp_uevent uevent = NULL;93 acpi_hp_notify notify = NULL;94 95 acpi_lock_hp_context();96 97 if (adev->hp) {98 if (cb_type == DOCK_CALL_FIXUP)99 fixup = adev->hp->fixup;100 else if (cb_type == DOCK_CALL_UEVENT)101 uevent = adev->hp->uevent;102 else103 notify = adev->hp->notify;104 }105 106 acpi_unlock_hp_context();107 108 if (fixup)109 fixup(adev);110 else if (uevent)111 uevent(adev, event);112 else if (notify)113 notify(adev, event);114}115 116static struct dock_station *find_dock_station(acpi_handle handle)117{118 struct dock_station *ds;119 120 list_for_each_entry(ds, &dock_stations, sibling)121 if (ds->handle == handle)122 return ds;123 124 return NULL;125}126 127/**128 * find_dock_dependent_device - get a device dependent on this dock129 * @ds: the dock station130 * @adev: ACPI device object to find.131 *132 * iterate over the dependent device list for this dock. If the133 * dependent device matches the handle, return.134 */135static struct dock_dependent_device *136find_dock_dependent_device(struct dock_station *ds, struct acpi_device *adev)137{138 struct dock_dependent_device *dd;139 140 list_for_each_entry(dd, &ds->dependent_devices, list)141 if (adev == dd->adev)142 return dd;143 144 return NULL;145}146 147void register_dock_dependent_device(struct acpi_device *adev,148 acpi_handle dshandle)149{150 struct dock_station *ds = find_dock_station(dshandle);151 152 if (ds && !find_dock_dependent_device(ds, adev))153 add_dock_dependent_device(ds, adev);154}155 156/*****************************************************************************157 * Dock functions *158 *****************************************************************************/159 160/**161 * is_dock_device - see if a device is on a dock station162 * @adev: ACPI device object to check.163 *164 * If this device is either the dock station itself,165 * or is a device dependent on the dock station, then it166 * is a dock device167 */168int is_dock_device(struct acpi_device *adev)169{170 struct dock_station *dock_station;171 172 if (!dock_station_count)173 return 0;174 175 if (acpi_dock_match(adev->handle))176 return 1;177 178 list_for_each_entry(dock_station, &dock_stations, sibling)179 if (find_dock_dependent_device(dock_station, adev))180 return 1;181 182 return 0;183}184EXPORT_SYMBOL_GPL(is_dock_device);185 186/**187 * dock_present - see if the dock station is present.188 * @ds: the dock station189 *190 * execute the _STA method. note that present does not191 * imply that we are docked.192 */193static int dock_present(struct dock_station *ds)194{195 unsigned long long sta;196 acpi_status status;197 198 if (ds) {199 status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);200 if (ACPI_SUCCESS(status) && sta)201 return 1;202 }203 return 0;204}205 206/**207 * hot_remove_dock_devices - Remove dock station devices.208 * @ds: Dock station.209 */210static void hot_remove_dock_devices(struct dock_station *ds)211{212 struct dock_dependent_device *dd;213 214 /*215 * Walk the list in reverse order so that devices that have been added216 * last are removed first (in case there are some indirect dependencies217 * between them).218 */219 list_for_each_entry_reverse(dd, &ds->dependent_devices, list)220 dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST,221 DOCK_CALL_HANDLER);222 223 list_for_each_entry_reverse(dd, &ds->dependent_devices, list)224 acpi_bus_trim(dd->adev);225}226 227/**228 * hotplug_dock_devices - Insert devices on a dock station.229 * @ds: the dock station230 * @event: either bus check or device check request231 *232 * Some devices on the dock station need to have drivers called233 * to perform hotplug operations after a dock event has occurred.234 * Traverse the list of dock devices that have registered a235 * hotplug handler, and call the handler.236 */237static void hotplug_dock_devices(struct dock_station *ds, u32 event)238{239 struct dock_dependent_device *dd;240 241 /* Call driver specific post-dock fixups. */242 list_for_each_entry(dd, &ds->dependent_devices, list)243 dock_hotplug_event(dd, event, DOCK_CALL_FIXUP);244 245 /* Call driver specific hotplug functions. */246 list_for_each_entry(dd, &ds->dependent_devices, list)247 dock_hotplug_event(dd, event, DOCK_CALL_HANDLER);248 249 /*250 * Check if all devices have been enumerated already. If not, run251 * acpi_bus_scan() for them and that will cause scan handlers to be252 * attached to device objects or acpi_drivers to be stopped/started if253 * they are present.254 */255 list_for_each_entry(dd, &ds->dependent_devices, list) {256 struct acpi_device *adev = dd->adev;257 258 if (!acpi_device_enumerated(adev)) {259 int ret = acpi_bus_scan(adev->handle);260 261 if (ret)262 dev_dbg(&adev->dev, "scan error %d\n", -ret);263 }264 }265}266 267static void dock_event(struct dock_station *ds, u32 event, int num)268{269 struct device *dev = &ds->dock_device->dev;270 char event_string[13];271 char *envp[] = { event_string, NULL };272 struct dock_dependent_device *dd;273 274 if (num == UNDOCK_EVENT)275 sprintf(event_string, "EVENT=undock");276 else277 sprintf(event_string, "EVENT=dock");278 279 /*280 * Indicate that the status of the dock station has281 * changed.282 */283 if (num == DOCK_EVENT)284 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);285 286 list_for_each_entry(dd, &ds->dependent_devices, list)287 dock_hotplug_event(dd, event, DOCK_CALL_UEVENT);288 289 if (num != DOCK_EVENT)290 kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);291}292 293/**294 * handle_dock - handle a dock event295 * @ds: the dock station296 * @dock: to dock, or undock - that is the question297 *298 * Execute the _DCK method in response to an acpi event299 */300static void handle_dock(struct dock_station *ds, int dock)301{302 acpi_status status;303 struct acpi_object_list arg_list;304 union acpi_object arg;305 unsigned long long value;306 307 acpi_handle_info(ds->handle, "%s\n", dock ? "docking" : "undocking");308 309 /* _DCK method has one argument */310 arg_list.count = 1;311 arg_list.pointer = &arg;312 arg.type = ACPI_TYPE_INTEGER;313 arg.integer.value = dock;314 status = acpi_evaluate_integer(ds->handle, "_DCK", &arg_list, &value);315 if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)316 acpi_handle_err(ds->handle, "Failed to execute _DCK (0x%x)\n",317 status);318}319 320static inline void dock(struct dock_station *ds)321{322 handle_dock(ds, 1);323}324 325static inline void undock(struct dock_station *ds)326{327 handle_dock(ds, 0);328}329 330static inline void begin_dock(struct dock_station *ds)331{332 ds->flags |= DOCK_DOCKING;333}334 335static inline void complete_dock(struct dock_station *ds)336{337 ds->flags &= ~(DOCK_DOCKING);338 ds->last_dock_time = jiffies;339}340 341static inline void begin_undock(struct dock_station *ds)342{343 ds->flags |= DOCK_UNDOCKING;344}345 346static inline void complete_undock(struct dock_station *ds)347{348 ds->flags &= ~(DOCK_UNDOCKING);349}350 351/**352 * dock_in_progress - see if we are in the middle of handling a dock event353 * @ds: the dock station354 *355 * Sometimes while docking, false dock events can be sent to the driver356 * because good connections aren't made or some other reason. Ignore these357 * if we are in the middle of doing something.358 */359static int dock_in_progress(struct dock_station *ds)360{361 if ((ds->flags & DOCK_DOCKING) ||362 time_before(jiffies, (ds->last_dock_time + HZ)))363 return 1;364 return 0;365}366 367/**368 * handle_eject_request - handle an undock request checking for error conditions369 * @ds: The dock station to undock.370 * @event: The ACPI event number associated with the undock request.371 *372 * Check to make sure the dock device is still present, then undock and373 * hotremove all the devices that may need removing.374 */375static int handle_eject_request(struct dock_station *ds, u32 event)376{377 if (dock_in_progress(ds))378 return -EBUSY;379 380 /*381 * here we need to generate the undock382 * event prior to actually doing the undock383 * so that the device struct still exists.384 * Also, even send the dock event if the385 * device is not present anymore386 */387 dock_event(ds, event, UNDOCK_EVENT);388 389 hot_remove_dock_devices(ds);390 undock(ds);391 acpi_evaluate_lck(ds->handle, 0);392 acpi_evaluate_ej0(ds->handle);393 if (dock_present(ds)) {394 acpi_handle_err(ds->handle, "Unable to undock!\n");395 return -EBUSY;396 }397 complete_undock(ds);398 return 0;399}400 401/**402 * dock_notify - Handle ACPI dock notification.403 * @adev: Dock station's ACPI device object.404 * @event: Event code.405 *406 * If we are notified to dock, then check to see if the dock is407 * present and then dock. Notify all drivers of the dock event,408 * and then hotplug and devices that may need hotplugging.409 */410int dock_notify(struct acpi_device *adev, u32 event)411{412 acpi_handle handle = adev->handle;413 struct dock_station *ds = find_dock_station(handle);414 int surprise_removal = 0;415 416 if (!ds)417 return -ENODEV;418 419 /*420 * According to acpi spec 3.0a, if a DEVICE_CHECK notification421 * is sent and _DCK is present, it is assumed to mean an undock422 * request.423 */424 if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK)425 event = ACPI_NOTIFY_EJECT_REQUEST;426 427 /*428 * dock station: BUS_CHECK - docked or surprise removal429 * DEVICE_CHECK - undocked430 * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal431 *432 * To simplify event handling, dock dependent device handler always433 * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and434 * ACPI_NOTIFY_EJECT_REQUEST for removal435 */436 switch (event) {437 case ACPI_NOTIFY_BUS_CHECK:438 case ACPI_NOTIFY_DEVICE_CHECK:439 if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) {440 begin_dock(ds);441 dock(ds);442 if (!dock_present(ds)) {443 acpi_handle_err(handle, "Unable to dock!\n");444 complete_dock(ds);445 break;446 }447 hotplug_dock_devices(ds, event);448 complete_dock(ds);449 dock_event(ds, event, DOCK_EVENT);450 acpi_evaluate_lck(ds->handle, 1);451 acpi_update_all_gpes();452 break;453 }454 if (dock_present(ds) || dock_in_progress(ds))455 break;456 /* This is a surprise removal */457 surprise_removal = 1;458 event = ACPI_NOTIFY_EJECT_REQUEST;459 /* Fall back */460 fallthrough;461 case ACPI_NOTIFY_EJECT_REQUEST:462 begin_undock(ds);463 if ((immediate_undock && !(ds->flags & DOCK_IS_ATA))464 || surprise_removal)465 handle_eject_request(ds, event);466 else467 dock_event(ds, event, UNDOCK_EVENT);468 break;469 }470 return 0;471}472 473/*474 * show_docked - read method for "docked" file in sysfs475 */476static ssize_t docked_show(struct device *dev,477 struct device_attribute *attr, char *buf)478{479 struct dock_station *dock_station = dev->platform_data;480 struct acpi_device *adev = acpi_fetch_acpi_dev(dock_station->handle);481 482 return sysfs_emit(buf, "%u\n", acpi_device_enumerated(adev));483}484static DEVICE_ATTR_RO(docked);485 486/*487 * show_flags - read method for flags file in sysfs488 */489static ssize_t flags_show(struct device *dev,490 struct device_attribute *attr, char *buf)491{492 struct dock_station *dock_station = dev->platform_data;493 494 return sysfs_emit(buf, "%d\n", dock_station->flags);495 496}497static DEVICE_ATTR_RO(flags);498 499/*500 * write_undock - write method for "undock" file in sysfs501 */502static ssize_t undock_store(struct device *dev, struct device_attribute *attr,503 const char *buf, size_t count)504{505 int ret;506 struct dock_station *dock_station = dev->platform_data;507 508 if (!count)509 return -EINVAL;510 511 acpi_scan_lock_acquire();512 begin_undock(dock_station);513 ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);514 acpi_scan_lock_release();515 return ret ? ret : count;516}517static DEVICE_ATTR_WO(undock);518 519/*520 * show_dock_uid - read method for "uid" file in sysfs521 */522static ssize_t uid_show(struct device *dev,523 struct device_attribute *attr, char *buf)524{525 unsigned long long lbuf;526 struct dock_station *dock_station = dev->platform_data;527 528 acpi_status status = acpi_evaluate_integer(dock_station->handle,529 "_UID", NULL, &lbuf);530 if (ACPI_FAILURE(status))531 return 0;532 533 return sysfs_emit(buf, "%llx\n", lbuf);534}535static DEVICE_ATTR_RO(uid);536 537static ssize_t type_show(struct device *dev,538 struct device_attribute *attr, char *buf)539{540 struct dock_station *dock_station = dev->platform_data;541 char *type;542 543 if (dock_station->flags & DOCK_IS_DOCK)544 type = "dock_station";545 else if (dock_station->flags & DOCK_IS_ATA)546 type = "ata_bay";547 else if (dock_station->flags & DOCK_IS_BAT)548 type = "battery_bay";549 else550 type = "unknown";551 552 return sysfs_emit(buf, "%s\n", type);553}554static DEVICE_ATTR_RO(type);555 556static struct attribute *dock_attributes[] = {557 &dev_attr_docked.attr,558 &dev_attr_flags.attr,559 &dev_attr_undock.attr,560 &dev_attr_uid.attr,561 &dev_attr_type.attr,562 NULL563};564 565static const struct attribute_group dock_attribute_group = {566 .attrs = dock_attributes567};568 569/**570 * acpi_dock_add - Add a new dock station571 * @adev: Dock station ACPI device object.572 *573 * allocated and initialize a new dock station device.574 */575void acpi_dock_add(struct acpi_device *adev)576{577 struct dock_station *dock_station, ds = { NULL, };578 struct platform_device_info pdevinfo;579 acpi_handle handle = adev->handle;580 struct platform_device *dd;581 int ret;582 583 memset(&pdevinfo, 0, sizeof(pdevinfo));584 pdevinfo.name = "dock";585 pdevinfo.id = dock_station_count;586 pdevinfo.fwnode = acpi_fwnode_handle(adev);587 pdevinfo.data = &ds;588 pdevinfo.size_data = sizeof(ds);589 dd = platform_device_register_full(&pdevinfo);590 if (IS_ERR(dd))591 return;592 593 dock_station = dd->dev.platform_data;594 595 dock_station->handle = handle;596 dock_station->dock_device = dd;597 dock_station->last_dock_time = jiffies - HZ;598 599 INIT_LIST_HEAD(&dock_station->sibling);600 INIT_LIST_HEAD(&dock_station->dependent_devices);601 602 /* we want the dock device to send uevents */603 dev_set_uevent_suppress(&dd->dev, 0);604 605 if (acpi_dock_match(handle))606 dock_station->flags |= DOCK_IS_DOCK;607 if (acpi_ata_match(handle))608 dock_station->flags |= DOCK_IS_ATA;609 if (acpi_device_is_battery(adev))610 dock_station->flags |= DOCK_IS_BAT;611 612 ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);613 if (ret)614 goto err_unregister;615 616 /* add the dock station as a device dependent on itself */617 ret = add_dock_dependent_device(dock_station, adev);618 if (ret)619 goto err_rmgroup;620 621 dock_station_count++;622 list_add(&dock_station->sibling, &dock_stations);623 adev->flags.is_dock_station = true;624 dev_info(&adev->dev, "ACPI dock station (docks/bays count: %d)\n",625 dock_station_count);626 return;627 628err_rmgroup:629 sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group);630 631err_unregister:632 platform_device_unregister(dd);633 acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret);634}635