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