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
  23ACPI_MODULE_NAME("dock");
  24
  25static bool immediate_undock = 1;
  26module_param(immediate_undock, bool, 0644);
  27MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
  28        "undock immediately when the undock button is pressed, 0 will cause"
  29        " the driver to wait for userspace to write the undock sysfs file "
  30        " before undocking");
  31
  32struct dock_station {
  33        acpi_handle handle;
  34        unsigned long last_dock_time;
  35        u32 flags;
  36        struct list_head dependent_devices;
  37
  38        struct list_head sibling;
  39        struct platform_device *dock_device;
  40};
  41static LIST_HEAD(dock_stations);
  42static int dock_station_count;
  43
  44struct dock_dependent_device {
  45        struct list_head list;
  46        struct acpi_device *adev;
  47};
  48
  49#define DOCK_DOCKING    0x00000001
  50#define DOCK_UNDOCKING  0x00000002
  51#define DOCK_IS_DOCK    0x00000010
  52#define DOCK_IS_ATA     0x00000020
  53#define DOCK_IS_BAT     0x00000040
  54#define DOCK_EVENT      3
  55#define UNDOCK_EVENT    2
  56
  57enum dock_callback_type {
  58        DOCK_CALL_HANDLER,
  59        DOCK_CALL_FIXUP,
  60        DOCK_CALL_UEVENT,
  61};
  62
  63/*****************************************************************************
  64 *                         Dock Dependent device functions                   *
  65 *****************************************************************************/
  66/**
  67 * add_dock_dependent_device - associate a device with the dock station
  68 * @ds: Dock station.
  69 * @adev: Dependent ACPI device object.
  70 *
  71 * Add the dependent device to the dock's dependent device list.
  72 */
  73static int add_dock_dependent_device(struct dock_station *ds,
  74                                     struct acpi_device *adev)
  75{
  76        struct dock_dependent_device *dd;
  77
  78        dd = kzalloc(sizeof(*dd), GFP_KERNEL);
  79        if (!dd)
  80                return -ENOMEM;
  81
  82        dd->adev = adev;
  83        INIT_LIST_HEAD(&dd->list);
  84        list_add_tail(&dd->list, &ds->dependent_devices);
  85
  86        return 0;
  87}
  88
  89static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event,
  90                               enum dock_callback_type cb_type)
  91{
  92        struct acpi_device *adev = dd->adev;
  93
  94        acpi_lock_hp_context();
  95
  96        if (!adev->hp)
  97                goto out;
  98
  99        if (cb_type == DOCK_CALL_FIXUP) {
 100                void (*fixup)(struct acpi_device *);
 101
 102                fixup = adev->hp->fixup;
 103                if (fixup) {
 104                        acpi_unlock_hp_context();
 105                        fixup(adev);
 106                        return;
 107                }
 108        } else if (cb_type == DOCK_CALL_UEVENT) {
 109                void (*uevent)(struct acpi_device *, u32);
 110
 111                uevent = adev->hp->uevent;
 112                if (uevent) {
 113                        acpi_unlock_hp_context();
 114                        uevent(adev, event);
 115                        return;
 116                }
 117        } else {
 118                int (*notify)(struct acpi_device *, u32);
 119
 120                notify = adev->hp->notify;
 121                if (notify) {
 122                        acpi_unlock_hp_context();
 123                        notify(adev, event);
 124                        return;
 125                }
 126        }
 127
 128 out:
 129        acpi_unlock_hp_context();
 130}
 131
 132static struct dock_station *find_dock_station(acpi_handle handle)
 133{
 134        struct dock_station *ds;
 135
 136        list_for_each_entry(ds, &dock_stations, sibling)
 137                if (ds->handle == handle)
 138                        return ds;
 139
 140        return NULL;
 141}
 142
 143/**
 144 * find_dock_dependent_device - get a device dependent on this dock
 145 * @ds: the dock station
 146 * @adev: ACPI device object to find.
 147 *
 148 * iterate over the dependent device list for this dock.  If the
 149 * dependent device matches the handle, return.
 150 */
 151static struct dock_dependent_device *
 152find_dock_dependent_device(struct dock_station *ds, struct acpi_device *adev)
 153{
 154        struct dock_dependent_device *dd;
 155
 156        list_for_each_entry(dd, &ds->dependent_devices, list)
 157                if (adev == dd->adev)
 158                        return dd;
 159
 160        return NULL;
 161}
 162
 163void register_dock_dependent_device(struct acpi_device *adev,
 164                                    acpi_handle dshandle)
 165{
 166        struct dock_station *ds = find_dock_station(dshandle);
 167
 168        if (ds && !find_dock_dependent_device(ds, adev))
 169                add_dock_dependent_device(ds, adev);
 170}
 171
 172/*****************************************************************************
 173 *                         Dock functions                                    *
 174 *****************************************************************************/
 175
 176/**
 177 * is_dock_device - see if a device is on a dock station
 178 * @adev: ACPI device object to check.
 179 *
 180 * If this device is either the dock station itself,
 181 * or is a device dependent on the dock station, then it
 182 * is a dock device
 183 */
 184int is_dock_device(struct acpi_device *adev)
 185{
 186        struct dock_station *dock_station;
 187
 188        if (!dock_station_count)
 189                return 0;
 190
 191        if (acpi_dock_match(adev->handle))
 192                return 1;
 193
 194        list_for_each_entry(dock_station, &dock_stations, sibling)
 195                if (find_dock_dependent_device(dock_station, adev))
 196                        return 1;
 197
 198        return 0;
 199}
 200EXPORT_SYMBOL_GPL(is_dock_device);
 201
 202/**
 203 * dock_present - see if the dock station is present.
 204 * @ds: the dock station
 205 *
 206 * execute the _STA method.  note that present does not
 207 * imply that we are docked.
 208 */
 209static int dock_present(struct dock_station *ds)
 210{
 211        unsigned long long sta;
 212        acpi_status status;
 213
 214        if (ds) {
 215                status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta);
 216                if (ACPI_SUCCESS(status) && sta)
 217                        return 1;
 218        }
 219        return 0;
 220}
 221
 222/**
 223 * hot_remove_dock_devices - Remove dock station devices.
 224 * @ds: Dock station.
 225 */
 226static void hot_remove_dock_devices(struct dock_station *ds)
 227{
 228        struct dock_dependent_device *dd;
 229
 230        /*
 231         * Walk the list in reverse order so that devices that have been added
 232         * last are removed first (in case there are some indirect dependencies
 233         * between them).
 234         */
 235        list_for_each_entry_reverse(dd, &ds->dependent_devices, list)
 236                dock_hotplug_event(dd, ACPI_NOTIFY_EJECT_REQUEST, false);
 237
 238        list_for_each_entry_reverse(dd, &ds->dependent_devices, list)
 239                acpi_bus_trim(dd->adev);
 240}
 241
 242/**
 243 * hotplug_dock_devices - Insert devices on a dock station.
 244 * @ds: the dock station
 245 * @event: either bus check or device check request
 246 *
 247 * Some devices on the dock station need to have drivers called
 248 * to perform hotplug operations after a dock event has occurred.
 249 * Traverse the list of dock devices that have registered a
 250 * hotplug handler, and call the handler.
 251 */
 252static void hotplug_dock_devices(struct dock_station *ds, u32 event)
 253{
 254        struct dock_dependent_device *dd;
 255
 256        /* Call driver specific post-dock fixups. */
 257        list_for_each_entry(dd, &ds->dependent_devices, list)
 258                dock_hotplug_event(dd, event, DOCK_CALL_FIXUP);
 259
 260        /* Call driver specific hotplug functions. */
 261        list_for_each_entry(dd, &ds->dependent_devices, list)
 262                dock_hotplug_event(dd, event, DOCK_CALL_HANDLER);
 263
 264        /*
 265         * Check if all devices have been enumerated already.  If not, run
 266         * acpi_bus_scan() for them and that will cause scan handlers to be
 267         * attached to device objects or acpi_drivers to be stopped/started if
 268         * they are present.
 269         */
 270        list_for_each_entry(dd, &ds->dependent_devices, list) {
 271                struct acpi_device *adev = dd->adev;
 272
 273                if (!acpi_device_enumerated(adev)) {
 274                        int ret = acpi_bus_scan(adev->handle);
 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                /* fall through */
 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 show_docked(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(docked, S_IRUGO, show_docked, NULL);
 498
 499/*
 500 * show_flags - read method for flags file in sysfs
 501 */
 502static ssize_t show_flags(struct device *dev,
 503                          struct device_attribute *attr, char *buf)
 504{
 505        struct dock_station *dock_station = dev->platform_data;
 506        return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
 507
 508}
 509static DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL);
 510
 511/*
 512 * write_undock - write method for "undock" file in sysfs
 513 */
 514static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
 515                           const char *buf, size_t count)
 516{
 517        int ret;
 518        struct dock_station *dock_station = dev->platform_data;
 519
 520        if (!count)
 521                return -EINVAL;
 522
 523        acpi_scan_lock_acquire();
 524        begin_undock(dock_station);
 525        ret = handle_eject_request(dock_station, ACPI_NOTIFY_EJECT_REQUEST);
 526        acpi_scan_lock_release();
 527        return ret ? ret: count;
 528}
 529static DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock);
 530
 531/*
 532 * show_dock_uid - read method for "uid" file in sysfs
 533 */
 534static ssize_t show_dock_uid(struct device *dev,
 535                             struct device_attribute *attr, char *buf)
 536{
 537        unsigned long long lbuf;
 538        struct dock_station *dock_station = dev->platform_data;
 539        acpi_status status = acpi_evaluate_integer(dock_station->handle,
 540                                        "_UID", NULL, &lbuf);
 541        if (ACPI_FAILURE(status))
 542            return 0;
 543
 544        return snprintf(buf, PAGE_SIZE, "%llx\n", lbuf);
 545}
 546static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
 547
 548static ssize_t show_dock_type(struct device *dev,
 549                struct device_attribute *attr, char *buf)
 550{
 551        struct dock_station *dock_station = dev->platform_data;
 552        char *type;
 553
 554        if (dock_station->flags & DOCK_IS_DOCK)
 555                type = "dock_station";
 556        else if (dock_station->flags & DOCK_IS_ATA)
 557                type = "ata_bay";
 558        else if (dock_station->flags & DOCK_IS_BAT)
 559                type = "battery_bay";
 560        else
 561                type = "unknown";
 562
 563        return snprintf(buf, PAGE_SIZE, "%s\n", type);
 564}
 565static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL);
 566
 567static struct attribute *dock_attributes[] = {
 568        &dev_attr_docked.attr,
 569        &dev_attr_flags.attr,
 570        &dev_attr_undock.attr,
 571        &dev_attr_uid.attr,
 572        &dev_attr_type.attr,
 573        NULL
 574};
 575
 576static const struct attribute_group dock_attribute_group = {
 577        .attrs = dock_attributes
 578};
 579
 580/**
 581 * acpi_dock_add - Add a new dock station
 582 * @adev: Dock station ACPI device object.
 583 *
 584 * allocated and initialize a new dock station device.
 585 */
 586void acpi_dock_add(struct acpi_device *adev)
 587{
 588        struct dock_station *dock_station, ds = { NULL, };
 589        struct platform_device_info pdevinfo;
 590        acpi_handle handle = adev->handle;
 591        struct platform_device *dd;
 592        int ret;
 593
 594        memset(&pdevinfo, 0, sizeof(pdevinfo));
 595        pdevinfo.name = "dock";
 596        pdevinfo.id = dock_station_count;
 597        pdevinfo.fwnode = acpi_fwnode_handle(adev);
 598        pdevinfo.data = &ds;
 599        pdevinfo.size_data = sizeof(ds);
 600        dd = platform_device_register_full(&pdevinfo);
 601        if (IS_ERR(dd))
 602                return;
 603
 604        dock_station = dd->dev.platform_data;
 605
 606        dock_station->handle = handle;
 607        dock_station->dock_device = dd;
 608        dock_station->last_dock_time = jiffies - HZ;
 609
 610        INIT_LIST_HEAD(&dock_station->sibling);
 611        INIT_LIST_HEAD(&dock_station->dependent_devices);
 612
 613        /* we want the dock device to send uevents */
 614        dev_set_uevent_suppress(&dd->dev, 0);
 615
 616        if (acpi_dock_match(handle))
 617                dock_station->flags |= DOCK_IS_DOCK;
 618        if (acpi_ata_match(handle))
 619                dock_station->flags |= DOCK_IS_ATA;
 620        if (acpi_device_is_battery(adev))
 621                dock_station->flags |= DOCK_IS_BAT;
 622
 623        ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);
 624        if (ret)
 625                goto err_unregister;
 626
 627        /* add the dock station as a device dependent on itself */
 628        ret = add_dock_dependent_device(dock_station, adev);
 629        if (ret)
 630                goto err_rmgroup;
 631
 632        dock_station_count++;
 633        list_add(&dock_station->sibling, &dock_stations);
 634        adev->flags.is_dock_station = true;
 635        dev_info(&adev->dev, "ACPI dock station (docks/bays count: %d)\n",
 636                 dock_station_count);
 637        return;
 638
 639err_rmgroup:
 640        sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group);
 641
 642err_unregister:
 643        platform_device_unregister(dd);
 644        acpi_handle_err(handle, "%s encountered error %d\n", __func__, ret);
 645}
 646