linux/drivers/input/mouse/psmouse-smbus.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2017 Red Hat, Inc
   4 */
   5
   6#define pr_fmt(fmt)             KBUILD_MODNAME ": " fmt
   7
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <linux/libps2.h>
  11#include <linux/i2c.h>
  12#include <linux/serio.h>
  13#include <linux/slab.h>
  14#include <linux/workqueue.h>
  15#include "psmouse.h"
  16
  17struct psmouse_smbus_dev {
  18        struct i2c_board_info board;
  19        struct psmouse *psmouse;
  20        struct i2c_client *client;
  21        struct list_head node;
  22        bool dead;
  23        bool need_deactivate;
  24};
  25
  26static LIST_HEAD(psmouse_smbus_list);
  27static DEFINE_MUTEX(psmouse_smbus_mutex);
  28
  29static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
  30{
  31        struct psmouse_smbus_dev *smbdev;
  32
  33        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
  34                return;
  35
  36        mutex_lock(&psmouse_smbus_mutex);
  37
  38        list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
  39                if (smbdev->dead)
  40                        continue;
  41
  42                if (smbdev->client)
  43                        continue;
  44
  45                /*
  46                 * Here would be a good place to check if device is actually
  47                 * present, but it seems that SMBus will not respond unless we
  48                 * fully reset PS/2 connection.  So cross our fingers, and try
  49                 * to switch over, hopefully our system will not have too many
  50                 * "host notify" I2C adapters.
  51                 */
  52                psmouse_dbg(smbdev->psmouse,
  53                            "SMBus candidate adapter appeared, triggering rescan\n");
  54                serio_rescan(smbdev->psmouse->ps2dev.serio);
  55        }
  56
  57        mutex_unlock(&psmouse_smbus_mutex);
  58}
  59
  60static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
  61{
  62        struct psmouse_smbus_dev *smbdev, *tmp;
  63
  64        mutex_lock(&psmouse_smbus_mutex);
  65
  66        list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
  67                if (smbdev->client != client)
  68                        continue;
  69
  70                kfree(client->dev.platform_data);
  71                client->dev.platform_data = NULL;
  72
  73                if (!smbdev->dead) {
  74                        psmouse_dbg(smbdev->psmouse,
  75                                    "Marking SMBus companion %s as gone\n",
  76                                    dev_name(&smbdev->client->dev));
  77                        smbdev->dead = true;
  78                        serio_rescan(smbdev->psmouse->ps2dev.serio);
  79                } else {
  80                        list_del(&smbdev->node);
  81                        kfree(smbdev);
  82                }
  83        }
  84
  85        mutex_unlock(&psmouse_smbus_mutex);
  86}
  87
  88static int psmouse_smbus_notifier_call(struct notifier_block *nb,
  89                                       unsigned long action, void *data)
  90{
  91        struct device *dev = data;
  92
  93        switch (action) {
  94        case BUS_NOTIFY_ADD_DEVICE:
  95                if (dev->type == &i2c_adapter_type)
  96                        psmouse_smbus_check_adapter(to_i2c_adapter(dev));
  97                break;
  98
  99        case BUS_NOTIFY_REMOVED_DEVICE:
 100                if (dev->type == &i2c_client_type)
 101                        psmouse_smbus_detach_i2c_client(to_i2c_client(dev));
 102                break;
 103        }
 104
 105        return 0;
 106}
 107
 108static struct notifier_block psmouse_smbus_notifier = {
 109        .notifier_call = psmouse_smbus_notifier_call,
 110};
 111
 112static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
 113{
 114        return PSMOUSE_FULL_PACKET;
 115}
 116
 117static int psmouse_smbus_reconnect(struct psmouse *psmouse)
 118{
 119        struct psmouse_smbus_dev *smbdev = psmouse->private;
 120
 121        if (smbdev->need_deactivate)
 122                psmouse_deactivate(psmouse);
 123
 124        return 0;
 125}
 126
 127struct psmouse_smbus_removal_work {
 128        struct work_struct work;
 129        struct i2c_client *client;
 130};
 131
 132static void psmouse_smbus_remove_i2c_device(struct work_struct *work)
 133{
 134        struct psmouse_smbus_removal_work *rwork =
 135                container_of(work, struct psmouse_smbus_removal_work, work);
 136
 137        dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n");
 138        i2c_unregister_device(rwork->client);
 139
 140        kfree(rwork);
 141}
 142
 143/*
 144 * This schedules removal of SMBus companion device. We have to do
 145 * it in a separate tread to avoid deadlocking on psmouse_mutex in
 146 * case the device has a trackstick (which is also driven by psmouse).
 147 *
 148 * Note that this may be racing with i2c adapter removal, but we
 149 * can't do anything about that: i2c automatically destroys clients
 150 * attached to an adapter that is being removed. This has to be
 151 * fixed in i2c core.
 152 */
 153static void psmouse_smbus_schedule_remove(struct i2c_client *client)
 154{
 155        struct psmouse_smbus_removal_work *rwork;
 156
 157        rwork = kzalloc(sizeof(*rwork), GFP_KERNEL);
 158        if (rwork) {
 159                INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device);
 160                rwork->client = client;
 161
 162                schedule_work(&rwork->work);
 163        }
 164}
 165
 166static void psmouse_smbus_disconnect(struct psmouse *psmouse)
 167{
 168        struct psmouse_smbus_dev *smbdev = psmouse->private;
 169
 170        mutex_lock(&psmouse_smbus_mutex);
 171
 172        if (smbdev->dead) {
 173                list_del(&smbdev->node);
 174                kfree(smbdev);
 175        } else {
 176                smbdev->dead = true;
 177                psmouse_dbg(smbdev->psmouse,
 178                            "posting removal request for SMBus companion %s\n",
 179                            dev_name(&smbdev->client->dev));
 180                psmouse_smbus_schedule_remove(smbdev->client);
 181        }
 182
 183        mutex_unlock(&psmouse_smbus_mutex);
 184
 185        psmouse->private = NULL;
 186}
 187
 188static int psmouse_smbus_create_companion(struct device *dev, void *data)
 189{
 190        struct psmouse_smbus_dev *smbdev = data;
 191        unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END };
 192        struct i2c_adapter *adapter;
 193        struct i2c_client *client;
 194
 195        adapter = i2c_verify_adapter(dev);
 196        if (!adapter)
 197                return 0;
 198
 199        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
 200                return 0;
 201
 202        client = i2c_new_scanned_device(adapter, &smbdev->board,
 203                                        addr_list, NULL);
 204        if (IS_ERR(client))
 205                return 0;
 206
 207        /* We have our(?) device, stop iterating i2c bus. */
 208        smbdev->client = client;
 209        return 1;
 210}
 211
 212void psmouse_smbus_cleanup(struct psmouse *psmouse)
 213{
 214        struct psmouse_smbus_dev *smbdev, *tmp;
 215
 216        mutex_lock(&psmouse_smbus_mutex);
 217
 218        list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
 219                if (psmouse == smbdev->psmouse) {
 220                        list_del(&smbdev->node);
 221                        kfree(smbdev);
 222                }
 223        }
 224
 225        mutex_unlock(&psmouse_smbus_mutex);
 226}
 227
 228int psmouse_smbus_init(struct psmouse *psmouse,
 229                       const struct i2c_board_info *board,
 230                       const void *pdata, size_t pdata_size,
 231                       bool need_deactivate,
 232                       bool leave_breadcrumbs)
 233{
 234        struct psmouse_smbus_dev *smbdev;
 235        int error;
 236
 237        smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL);
 238        if (!smbdev)
 239                return -ENOMEM;
 240
 241        smbdev->psmouse = psmouse;
 242        smbdev->board = *board;
 243        smbdev->need_deactivate = need_deactivate;
 244
 245        if (pdata) {
 246                smbdev->board.platform_data = kmemdup(pdata, pdata_size,
 247                                                      GFP_KERNEL);
 248                if (!smbdev->board.platform_data) {
 249                        kfree(smbdev);
 250                        return -ENOMEM;
 251                }
 252        }
 253
 254        if (need_deactivate)
 255                psmouse_deactivate(psmouse);
 256
 257        psmouse->private = smbdev;
 258        psmouse->protocol_handler = psmouse_smbus_process_byte;
 259        psmouse->reconnect = psmouse_smbus_reconnect;
 260        psmouse->fast_reconnect = psmouse_smbus_reconnect;
 261        psmouse->disconnect = psmouse_smbus_disconnect;
 262        psmouse->resync_time = 0;
 263
 264        mutex_lock(&psmouse_smbus_mutex);
 265        list_add_tail(&smbdev->node, &psmouse_smbus_list);
 266        mutex_unlock(&psmouse_smbus_mutex);
 267
 268        /* Bind to already existing adapters right away */
 269        error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion);
 270
 271        if (smbdev->client) {
 272                /* We have our companion device */
 273                return 0;
 274        }
 275
 276        /*
 277         * If we did not create i2c device we will not need platform
 278         * data even if we are leaving breadcrumbs.
 279         */
 280        kfree(smbdev->board.platform_data);
 281        smbdev->board.platform_data = NULL;
 282
 283        if (error < 0 || !leave_breadcrumbs) {
 284                mutex_lock(&psmouse_smbus_mutex);
 285                list_del(&smbdev->node);
 286                mutex_unlock(&psmouse_smbus_mutex);
 287
 288                kfree(smbdev);
 289        }
 290
 291        return error < 0 ? error : -EAGAIN;
 292}
 293
 294int __init psmouse_smbus_module_init(void)
 295{
 296        int error;
 297
 298        error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
 299        if (error) {
 300                pr_err("failed to register i2c bus notifier: %d\n", error);
 301                return error;
 302        }
 303
 304        return 0;
 305}
 306
 307void psmouse_smbus_module_exit(void)
 308{
 309        bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
 310        flush_scheduled_work();
 311}
 312