LCOV - code coverage report
Current view: top level - drivers/input/mouse - psmouse-smbus.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 5 132 3.8 %
Date: 2023-03-27 20:00:47 Functions: 1 13 7.7 %

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

Generated by: LCOV version 1.14