linux/drivers/acpi/dock.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  dock.c - ACPI dock station driver
   4 *
   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    0x00000001
  48#define DOCK_UNDOCKING  0x00000002
  49#define DOCK_IS_DOCK    0x00000010
  50#define DOCK_IS_ATA     0x00000020
  51#define DOCK_IS_BAT     0x00000040
  52#define DOCK_EVENT      3
  53#define UNDOCK_EVENT    2
  54
  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 station
  66 * @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
  92        acpi_lock_hp_context();
  93
  94        if (!adev->hp)
  95                goto out;
  96
  97        if (cb_type == DOCK_CALL_FIXUP) {
  98                void (*fixup)(struct acpi_device *);
  99
 100                fixup = adev->hp->fixup;
 101                if (fixup) {
 102                        acpi_unlock_hp_context();
 103                        fixup(adev);
 104                        return;
 105                }
 106        } else if (cb_type == DOCK_CALL_UEVENT) {
 107                void (*uevent)(struct acpi_device *, u32);
 108
 109                uevent = adev->hp->uevent;
 110                if (uevent) {
 111                        acpi_unlock_hp_context();
 112                        uevent(adev, event);
 113                        return;
 114                }
 115        } else {
 116                int (*notify)(struct acpi_device *, u32);
 117
 118                notify = adev->hp->notify;
 119                if (notify) {
 120                        acpi_unlock_hp_context();
 121                        notify(adev, event);
 122                        return;
 123                }
 124        }
 125
 126 out:
 127        acpi_unlock_hp_context();
 128}
 129
 130static struct dock_station *find_dock_station(acpi_handle handle)
 131{
 132        struct dock_station *ds;
 133
 134        list_for_each_entry(ds, &dock_stations, sibling)
 135                if (ds->handle == handle)
 136                        return ds;
 137
 138        return NULL;
 139}
 140
 141/**
 142 * find_dock_dependent_device - get a device dependent on this dock
 143 * @ds: the dock station
 144 * @adev: ACPI device object to find.
 145 *
 146 * iterate over the dependent device list for this dock.  If the
 147 * dependent device matches the handle, return.
 148 */
 149static struct dock_dependent_device *
 150find_dock_dependent_device(struct dock_station *ds, struct acpi_device *adev)
 151{
 152        struct dock_dependent_device *dd;
 153
 154        list_for_each_entry(dd, &ds->dependent_devices, list)
 155                if (adev == dd->adev)
 156                        return dd;
 157
 158        return NULL;
 159}
 160
 161void register_dock_dependent_device(struct acpi_device *adev,
 162                                    acpi_handle dshandle)
 163{
 164        struct dock_station *ds = find_dock_station(dshandle);
 165
 166        if (ds && !find_dock_dependent_device(ds, adev))
 167                add_dock_dependent_device(ds, adev);
 168}
 169
 170/*****************************************************************************
 171 *                         Dock functions                                    *
 172 *****************************************************************************/
 173
 174/**
 175 * is_dock_device - see if a device is on a dock station
 176 * @adev: ACPI device object to check.
 177 *
 178 * If this device is either the dock station itself,
 179 * or is a device dependent on the dock station, then it
 180 * is a dock device
 181 */
 182int is_dock_device(struct acpi_device *adev)
 183{
 184        struct dock_station *dock_station;
 185
 186        if (!dock_station_count)
 187                return 0;
 188
 189        if (acpi_dock_match(adev->handle))
 190                return 1;
 191
 192        list_for_each_entry(dock_station, &dock_stations, sibling)
 193                if (find_dock_dependent_device(dock_station, adev))
 194                        return 1;
 195
 196        return 0;
 197}
 198EXPORT_SYMBOL_GPL(is_dock_device);
 199
 200/**
 201 * dock_present - see if the dock station is present.
 202 * @ds: the dock station
 203 *
 204 * execute the _STA method.  note that present does not
 205 * imply that we are docked.
 206 */
 207static int dock_present(struct dock_station *ds)
 208{
 209        unsigned long long sta;
 210        acpi_status status;
 211
 212        if (ds) {
 213                status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
 214                if (ACPI_SUCCESS(status) && sta)
 215                        return 1;
 216        }
 217        return 0;
 218}
 219
 220/**
 221 * hot_remove_dock_devices - Remove dock station devices.
 222 * @ds: Dock station.
 223 */
 224static void hot_remove_dock_devices(struct dock_station *ds)
 225{
 226        struct dock_dependent_device *dd;
 227
 228        /*
 229         * Walk the list in reverse order so that devices that have been added
 230         * last are removed first (in case there are some indirect dependencies
 231         * between them).
 232         */
 233        list_for_each_entry_reverse(dd, &ds->dependent_devices, list)
 234                dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST,
 235                                   DOCK_CALL_HANDLER);
 236
 237        list_for_each_entry_reverse(dd, &ds->dependent_devices, list)
 238                acpi_bus_trim(dd->adev);
 239}
 240
 241/**
 242 * hotplug_dock_devices - Insert devices on a dock station.
 243 * @ds: the dock station
 244 * @event: either bus check or device check request
 245 *
 246 * Some devices on the dock station need to have drivers called
 247 * to perform hotplug operations after a dock event has occurred.
 248 * Traverse the list of dock devices that have registered a
 249 * hotplug handler, and call the handler.
 250 */
 251static void hotplug_dock_devices(struct dock_station *ds, u32 event)
 252{
 253        struct dock_dependent_device *dd;
 254
 255        /* Call driver specific post-dock fixups. */
 256        list_for_each_entry(dd, &ds->dependent_devices, list)
 257                dock_hotplug_event(dd, event, DOCK_CALL_FIXUP);
 258
 259        /* Call driver specific hotplug functions. */
 260        list_for_each_entry(dd, &ds->dependent_devices, list)
 261                dock_hotplug_event(dd, event, DOCK_CALL_HANDLER);
 262
 263        /*
 264         * Check if all devices have been enumerated already.  If not, run
 265         * acpi_bus_scan() for them and that will cause scan handlers to be
 266         * attached to device objects or acpi_drivers to be stopped/started if
 267         * they are present.
 268         */
 269        list_for_each_entry(dd, &ds->dependent_devices, list) {
 270                struct acpi_device *adev = dd->adev;
 271
 272                if (!acpi_device_enumerated(adev)) {
 273                        int ret = acpi_bus_scan(adev->handle);
 274
 275                        if (ret)
 276                                dev_dbg(&adev->dev, "scan error %d\n", -ret);
 277                }
 278        }
 279}
 280
 281static void dock_event(struct dock_station *ds, u32 event, int num)
 282{
 283        struct device *dev = &ds->dock_device->dev;
 284        char event_string[13];
 285        char *envp[] = { event_string, NULL };
 286        struct dock_dependent_device *dd;
 287
 288        if (num == UNDOCK_EVENT)
 289                sprintf(event_string, "EVENT=undock");
 290        else
 291                sprintf(event_string, "EVENT=dock");
 292
 293        /*
 294         * Indicate that the status of the dock station has
 295         * changed.
 296         */
 297        if (num == DOCK_EVENT)
 298                kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
 299
 300        list_for_each_entry(dd, &ds->dependent_devices, list)
 301                dock_hotplug_event(dd, event, DOCK_CALL_UEVENT);
 302
 303        if (num != DOCK_EVENT)
 304                kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
 305}
 306
 307/**
 308 * handle_dock - handle a dock event
 309 * @ds: the dock station
 310 * @dock: to dock, or undock - that is the question
 311 *
 312 * Execute the _DCK method in response to an acpi event
 313 */
 314static void handle_dock(struct dock_station *ds, int dock)
 315{
 316        acpi_status status;
 317        struct acpi_object_list arg_list;
 318        union acpi_object arg;
 319        unsigned long long value;
 320
 321        acpi_handle_info(ds->handle, "%s\n", dock ? "docking" : "undocking");
 322
 323        /* _DCK method has one argument */
 324        arg_list.count = 1;
 325        arg_list.pointer = &arg;
 326        arg.type = ACPI_TYPE_INTEGER;
 327        arg.integer.value = dock;
 328        status = acpi_evaluate_integer(ds->handle, "_DCK", &arg_list, &value);
 329        if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
 330                acpi_handle_err(ds->handle, "Failed to execute _DCK (0x%x)\n",
 331                                status);
 332}
 333
 334static inline void dock(struct dock_station *ds)
 335{
 336        handle_dock(ds, 1);
 337}
 338
 339static inline void undock(struct dock_station *ds)
 340{
 341        handle_dock(ds, 0);
 342}
 343
 344static inline void begin_dock(struct dock_station *ds)
 345{
 346        ds->flags |= DOCK_DOCKING;
 347}
 348
 349static inline void complete_dock(struct dock_station *ds)
 350{
 351        ds->flags &= ~(DOCK_DOCKING);
 352        ds->last_dock_time = jiffies;
 353}
 354
 355static inline void begin_undock(struct dock_station *ds)
 356{
 357        ds->flags |= DOCK_UNDOCKING;
 358}
 359
 360static inline void complete_undock(struct dock_station *ds)
 361{
 362        ds->flags &= ~(DOCK_UNDOCKING);
 363}
 364
 365/**
 366 * dock_in_progress - see if we are in the middle of handling a dock event
 367 * @ds: the dock station
 368 *
 369 * Sometimes while docking, false dock events can be sent to the driver
 370 * because good connections aren't made or some other reason.  Ignore these
 371 * if we are in the middle of doing something.
 372 */
 373static int dock_in_progress(struct dock_station *ds)
 374{
 375        if ((ds->flags & DOCK_DOCKING) ||
 376            time_before(jiffies, (ds->last_dock_time + HZ)))
 377                return 1;
 378        return 0;
 379}
 380
 381/**
 382 * handle_eject_request - handle an undock request checking for error conditions
 383 *
 384 * Check to make sure the dock device is still present, then undock and
 385 * hotremove all the devices that may need removing.
 386 */
 387static int handle_eject_request(struct dock_station *ds, u32 event)
 388{
 389        if (dock_in_progress(ds))
 390                return -EBUSY;
 391
 392        /*
 393         * here we need to generate the undock
 394         * event prior to actually doing the undock
 395         * so that the device struct still exists.
 396         * Also, even send the dock event if the
 397         * device is not present anymore
 398         */
 399        dock_event(ds, event, UNDOCK_EVENT);
 400
 401        hot_remove_dock_devices(ds);
 402        undock(ds);
 403        acpi_evaluate_lck(ds->handle, 0);
 404        acpi_evaluate_ej0(ds->handle);
 405        if (dock_present(ds)) {
 406                acpi_handle_err(ds->handle, "Unable to undock!\n");
 407                return -EBUSY;
 408        }
 409        complete_undock(ds);
 410        return 0;
 411}
 412
 413/**
 414 * dock_notify - Handle ACPI dock notification.
 415 * @adev: Dock station's ACPI device object.
 416 * @event: Event code.
 417 *
 418 * If we are notified to dock, then check to see if the dock is
 419 * present and then dock.  Notify all drivers of the dock event,
 420 * and then hotplug and devices that may need hotplugging.
 421 */
 422int dock_notify(struct acpi_device *adev, u32 event)
 423{
 424        acpi_handle handle = adev->handle;
 425        struct dock_station *ds = find_dock_station(handle);
 426        int surprise_removal = 0;
 427
 428        if (!ds)
 429                return -ENODEV;
 430
 431        /*
 432         * According to acpi spec 3.0a, if a DEVICE_CHECK notification
 433         * is sent and _DCK is present, it is assumed to mean an undock
 434         * request.
 435         */
 436        if ((ds->flags & DOCK_IS_DOCK) && event == ACPI_NOTIFY_DEVICE_CHECK)
 437                event = ACPI_NOTIFY_EJECT_REQUEST;
 438
 439        /*
 440         * dock station: BUS_CHECK - docked or surprise removal
 441         *               DEVICE_CHECK - undocked
 442         * other device: BUS_CHECK/DEVICE_CHECK - added or surprise removal
 443         *
 444         * To simplify event handling, dock dependent device handler always
 445         * get ACPI_NOTIFY_BUS_CHECK/ACPI_NOTIFY_DEVICE_CHECK for add and
 446         * ACPI_NOTIFY_EJECT_REQUEST for removal
 447         */
 448        switch (event) {
 449        case ACPI_NOTIFY_BUS_CHECK:
 450        case ACPI_NOTIFY_DEVICE_CHECK:
 451                if (!dock_in_progress(ds) && !acpi_device_enumerated(adev)) {
 452                        begin_dock(ds);
 453                        dock(ds);
 454                        if (!dock_present(ds)) {
 455                                acpi_handle_err(handle, "Unable to dock!\n");
 456                                complete_dock(ds);
 457                                break;
 458                        }
 459                        hotplug_dock_devices(ds, event);
 460                        complete_dock(ds);
 461                        dock_event(ds, event, DOCK_EVENT);
 462                        acpi_evaluate_lck(ds->handle, 1);
 463                        acpi_update_all_gpes();
 464                        break;
 465                }
 466                if (dock_present(ds) || dock_in_progress(ds))
 467                        break;
 468                /* This is a surprise removal */
 469                surprise_removal = 1;
 470                event = ACPI_NOTIFY_EJECT_REQUEST;
 471                /* Fall back */
 472                fallthrough;
 473        case ACPI_NOTIFY_EJECT_REQUEST:
 474                begin_undock(ds);
 475                if ((immediate_undock && !(ds->flags & DOCK_IS_ATA))
 476                   || surprise_removal)
 477                        handle_eject_request(ds, event);
 478                else
 479                        dock_event(ds, event, UNDOCK_EVENT);
 480                break;
 481        }
 482        return 0;
 483}
 484
 485/*
 486 * show_docked - read method for "docked" file in sysfs
 487 */
 488static ssize_t docked_show(struct device *dev,
 489                           struct device_attribute *attr, char *buf)
 490{
 491        struct dock_station *dock_station = dev->platform_data;
 492        struct acpi_device *adev = NULL;
 493
 494        acpi_bus_get_device(dock_station->handle, &adev);
 495        return snprintf(buf, PAGE_SIZE, "%u\n", acpi_device_enumerated(adev));
 496}
 497static DEVICE_ATTR_RO(docked);
 498
 499/*
 500 * show_flags - read method for flags file in sysfs
 501 */
 502static ssize_t flags_show(struct device *dev,
 503                          struct device_attribute *attr, char *buf)
 504{
 505        struct dock_station *dock_station = dev->platform_data;
 506
 507        return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
 508
 509}
 510static DEVICE_ATTR_RO(flags);
 511
 512/*
 513 * write_undock - write method for "undock" file in sysfs
 514 */
 515static ssize_t undock_store(struct device *dev, struct device_attribute *attr,
 516                            const char *buf, size_t count)
 517{
 518        int ret;
 519        struct dock_station *dock_station = dev->platform_data;
 520
 521        if (!count)
 522                return -EINVAL;
 523
 524        acpi_scan_lock_acquire();
 525        begin_undock(dock_station);
 526        ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);
 527        acpi_scan_lock_release();
 528        return ret ? ret : count;
 529}
 530static DEVICE_ATTR_WO(undock);
 531
 532/*
 533 * show_dock_uid - read method for "uid" file in sysfs
 534 */
 535static ssize_t uid_show(struct device *dev,
 536                        struct device_attribute *attr, char *buf)
 537{
 538        unsigned long long lbuf;
 539        struct dock_station *dock_station = dev->platform_data;
 540
 541        acpi_status status = acpi_evaluate_integer(dock_station->handle,
 542                                        "_UID", NULL, &lbuf);
 543        if (ACPI_FAILURE(status))
 544                return 0;
 545
 546        return snprintf(buf, PAGE_SIZE, "%llx\n", lbuf);
 547}
 548static DEVICE_ATTR_RO(uid);
 549
 550static ssize_t type_show(struct device *dev,
 551                         struct device_attribute *attr, char *buf)
 552{
 553        struct dock_station *dock_station = dev->platform_data;
 554        char *type;
 555
 556        if (dock_station->flags & DOCK_IS_DOCK)
 557                type = "dock_station";
 558        else if (dock_station->flags & DOCK_IS_ATA)
 559                type = "ata_bay";
 560        else if (dock_station->flags & DOCK_IS_BAT)
 561                type = "battery_bay";
 562        else
 563                type = "unknown";
 564
 565        return snprintf(buf, PAGE_SIZE, "%s\n", type);
 566}
 567static DEVICE_ATTR_RO(type);
 568
 569static struct attribute *dock_attributes[] = {
 570        &dev_attr_docked.attr,
 571        &dev_attr_flags.attr,
 572        &dev_attr_undock.attr,
 573        &dev_attr_uid.attr,
 574        &dev_attr_type.attr,
 575        NULL
 576};
 577
 578static const struct attribute_group dock_attribute_group = {
 579        .attrs = dock_attributes
 580};
 581
 582/**
 583 * acpi_dock_add - Add a new dock station
 584 * @adev: Dock station ACPI device object.
 585 *
 586 * allocated and initialize a new dock station device.
 587 */
 588void acpi_dock_add(struct acpi_device *adev)
 589{
 590        struct dock_station *dock_station, ds = { NULL, };
 591        struct platform_device_info pdevinfo;
 592        acpi_handle handle = adev->handle;
 593        struct platform_device *dd;
 594        int ret;
 595
 596        memset(&pdevinfo, 0, sizeof(pdevinfo));
 597        pdevinfo.name = "dock";
 598        pdevinfo.id = dock_station_count;
 599        pdevinfo.fwnode = acpi_fwnode_handle(adev);
 600        pdevinfo.data = &ds;
 601        pdevinfo.size_data = sizeof(ds);
 602        dd = platform_device_register_full(&pdevinfo);
 603        if (IS_ERR(dd))
 604                return;
 605
 606        dock_station = dd->dev.platform_data;
 607
 608        dock_station->handle = handle;
 609        dock_station->dock_device = dd;
 610        dock_station->last_dock_time = jiffies - HZ;
 611
 612        INIT_LIST_HEAD(&dock_station->sibling);
 613        INIT_LIST_HEAD(&dock_station->dependent_devices);
 614
 615        /* we want the dock device to send uevents */
 616        dev_set_uevent_suppress(&dd->dev, 0);
 617
 618        if (acpi_dock_match(handle))
 619                dock_station->flags |= DOCK_IS_DOCK;
 620        if (acpi_ata_match(handle))
 621                dock_station->flags |= DOCK_IS_ATA;
 622        if (acpi_device_is_battery(adev))
 623                dock_station->flags |= DOCK_IS_BAT;
 624
 625        ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);
 626        if (ret)
 627                goto err_unregister;
 628
 629        /* add the dock station as a device dependent on itself */
 630        ret = add_dock_dependent_device(dock_station, adev);
 631        if (ret)
 632                goto err_rmgroup;
 633
 634        dock_station_count++;
 635        list_add(&dock_station->sibling, &dock_stations);
 636        adev->flags.is_dock_station = true;
 637        dev_info(&adev->dev, "ACPI dock station (docks/bays count: %d)\n",
 638                 dock_station_count);
 639        return;
 640
 641err_rmgroup:
 642        sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group);
 643
 644err_unregister:
 645        platform_device_unregister(dd);
 646        acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret);
 647}
 648