LCOV - code coverage report
Current view: top level - drivers/input/mouse - psmouse-smbus.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 4 137 2.9 %
Date: 2022-03-28 15:32:58 Functions: 1 13 7.7 %
Branches: 1 53 1.9 %

           Branch data     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                 :          0 : static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
      30                 :            : {
      31                 :          0 :         struct psmouse_smbus_dev *smbdev;
      32                 :            : 
      33         [ #  # ]:          0 :         if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
      34                 :            :                 return;
      35                 :            : 
      36                 :          0 :         mutex_lock(&psmouse_smbus_mutex);
      37                 :            : 
      38         [ #  # ]:          0 :         list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
      39         [ #  # ]:          0 :                 if (smbdev->dead)
      40                 :          0 :                         continue;
      41                 :            : 
      42         [ #  # ]:          0 :                 if (smbdev->client)
      43                 :          0 :                         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                 :          0 :                 psmouse_dbg(smbdev->psmouse,
      53                 :            :                             "SMBus candidate adapter appeared, triggering rescan\n");
      54                 :          0 :                 serio_rescan(smbdev->psmouse->ps2dev.serio);
      55                 :            :         }
      56                 :            : 
      57                 :          0 :         mutex_unlock(&psmouse_smbus_mutex);
      58                 :            : }
      59                 :            : 
      60                 :          0 : static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
      61                 :            : {
      62                 :          0 :         struct psmouse_smbus_dev *smbdev, *tmp;
      63                 :            : 
      64                 :          0 :         mutex_lock(&psmouse_smbus_mutex);
      65                 :            : 
      66         [ #  # ]:          0 :         list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
      67         [ #  # ]:          0 :                 if (smbdev->client != client)
      68                 :          0 :                         continue;
      69                 :            : 
      70                 :          0 :                 kfree(client->dev.platform_data);
      71                 :          0 :                 client->dev.platform_data = NULL;
      72                 :            : 
      73         [ #  # ]:          0 :                 if (!smbdev->dead) {
      74                 :          0 :                         psmouse_dbg(smbdev->psmouse,
      75                 :            :                                     "Marking SMBus companion %s as gone\n",
      76                 :            :                                     dev_name(&smbdev->client->dev));
      77                 :          0 :                         smbdev->dead = true;
      78                 :          0 :                         serio_rescan(smbdev->psmouse->ps2dev.serio);
      79                 :            :                 } else {
      80                 :          0 :                         list_del(&smbdev->node);
      81                 :          0 :                         kfree(smbdev);
      82                 :            :                 }
      83                 :            :         }
      84                 :            : 
      85                 :          0 :         mutex_unlock(&psmouse_smbus_mutex);
      86                 :          0 : }
      87                 :            : 
      88                 :          0 : static int psmouse_smbus_notifier_call(struct notifier_block *nb,
      89                 :            :                                        unsigned long action, void *data)
      90                 :            : {
      91                 :          0 :         struct device *dev = data;
      92                 :            : 
      93      [ #  #  # ]:          0 :         switch (action) {
      94                 :          0 :         case BUS_NOTIFY_ADD_DEVICE:
      95         [ #  # ]:          0 :                 if (dev->type == &i2c_adapter_type)
      96                 :          0 :                         psmouse_smbus_check_adapter(to_i2c_adapter(dev));
      97                 :            :                 break;
      98                 :            : 
      99                 :          0 :         case BUS_NOTIFY_REMOVED_DEVICE:
     100         [ #  # ]:          0 :                 if (dev->type == &i2c_client_type)
     101                 :          0 :                         psmouse_smbus_detach_i2c_client(to_i2c_client(dev));
     102                 :            :                 break;
     103                 :            :         }
     104                 :            : 
     105                 :          0 :         return 0;
     106                 :            : }
     107                 :            : 
     108                 :            : static struct notifier_block psmouse_smbus_notifier = {
     109                 :            :         .notifier_call = psmouse_smbus_notifier_call,
     110                 :            : };
     111                 :            : 
     112                 :          0 : static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
     113                 :            : {
     114                 :          0 :         return PSMOUSE_FULL_PACKET;
     115                 :            : }
     116                 :            : 
     117                 :          0 : static int psmouse_smbus_reconnect(struct psmouse *psmouse)
     118                 :            : {
     119                 :          0 :         struct psmouse_smbus_dev *smbdev = psmouse->private;
     120                 :            : 
     121         [ #  # ]:          0 :         if (smbdev->need_deactivate)
     122                 :          0 :                 psmouse_deactivate(psmouse);
     123                 :            : 
     124                 :          0 :         return 0;
     125                 :            : }
     126                 :            : 
     127                 :            : struct psmouse_smbus_removal_work {
     128                 :            :         struct work_struct work;
     129                 :            :         struct i2c_client *client;
     130                 :            : };
     131                 :            : 
     132                 :          0 : static void psmouse_smbus_remove_i2c_device(struct work_struct *work)
     133                 :            : {
     134                 :          0 :         struct psmouse_smbus_removal_work *rwork =
     135                 :          0 :                 container_of(work, struct psmouse_smbus_removal_work, work);
     136                 :            : 
     137                 :          0 :         dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n");
     138                 :          0 :         i2c_unregister_device(rwork->client);
     139                 :            : 
     140                 :          0 :         kfree(rwork);
     141                 :          0 : }
     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                 :            :  */
     153                 :          0 : static void psmouse_smbus_schedule_remove(struct i2c_client *client)
     154                 :            : {
     155                 :          0 :         struct psmouse_smbus_removal_work *rwork;
     156                 :            : 
     157                 :          0 :         rwork = kzalloc(sizeof(*rwork), GFP_KERNEL);
     158         [ #  # ]:          0 :         if (rwork) {
     159                 :          0 :                 INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device);
     160                 :          0 :                 rwork->client = client;
     161                 :            : 
     162                 :          0 :                 schedule_work(&rwork->work);
     163                 :            :         }
     164                 :          0 : }
     165                 :            : 
     166                 :          0 : static void psmouse_smbus_disconnect(struct psmouse *psmouse)
     167                 :            : {
     168                 :          0 :         struct psmouse_smbus_dev *smbdev = psmouse->private;
     169                 :            : 
     170                 :          0 :         mutex_lock(&psmouse_smbus_mutex);
     171                 :            : 
     172         [ #  # ]:          0 :         if (smbdev->dead) {
     173                 :          0 :                 list_del(&smbdev->node);
     174                 :          0 :                 kfree(smbdev);
     175                 :            :         } else {
     176                 :          0 :                 smbdev->dead = true;
     177                 :          0 :                 psmouse_dbg(smbdev->psmouse,
     178                 :            :                             "posting removal request for SMBus companion %s\n",
     179                 :            :                             dev_name(&smbdev->client->dev));
     180                 :          0 :                 psmouse_smbus_schedule_remove(smbdev->client);
     181                 :            :         }
     182                 :            : 
     183                 :          0 :         mutex_unlock(&psmouse_smbus_mutex);
     184                 :            : 
     185                 :          0 :         psmouse->private = NULL;
     186                 :          0 : }
     187                 :            : 
     188                 :          0 : static int psmouse_smbus_create_companion(struct device *dev, void *data)
     189                 :            : {
     190                 :          0 :         struct psmouse_smbus_dev *smbdev = data;
     191                 :          0 :         unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END };
     192                 :          0 :         struct i2c_adapter *adapter;
     193                 :          0 :         struct i2c_client *client;
     194                 :            : 
     195                 :          0 :         adapter = i2c_verify_adapter(dev);
     196         [ #  # ]:          0 :         if (!adapter)
     197                 :            :                 return 0;
     198                 :            : 
     199         [ #  # ]:          0 :         if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
     200                 :            :                 return 0;
     201                 :            : 
     202                 :          0 :         client = i2c_new_scanned_device(adapter, &smbdev->board,
     203                 :            :                                         addr_list, NULL);
     204         [ #  # ]:          0 :         if (IS_ERR(client))
     205                 :            :                 return 0;
     206                 :            : 
     207                 :            :         /* We have our(?) device, stop iterating i2c bus. */
     208                 :          0 :         smbdev->client = client;
     209                 :          0 :         return 1;
     210                 :            : }
     211                 :            : 
     212                 :          0 : void psmouse_smbus_cleanup(struct psmouse *psmouse)
     213                 :            : {
     214                 :          0 :         struct psmouse_smbus_dev *smbdev, *tmp;
     215                 :            : 
     216                 :          0 :         mutex_lock(&psmouse_smbus_mutex);
     217                 :            : 
     218         [ #  # ]:          0 :         list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
     219         [ #  # ]:          0 :                 if (psmouse == smbdev->psmouse) {
     220                 :          0 :                         list_del(&smbdev->node);
     221                 :          0 :                         kfree(smbdev);
     222                 :            :                 }
     223                 :            :         }
     224                 :            : 
     225                 :          0 :         mutex_unlock(&psmouse_smbus_mutex);
     226                 :          0 : }
     227                 :            : 
     228                 :          0 : int 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                 :          0 :         struct psmouse_smbus_dev *smbdev;
     235                 :          0 :         int error;
     236                 :            : 
     237                 :          0 :         smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL);
     238         [ #  # ]:          0 :         if (!smbdev)
     239                 :            :                 return -ENOMEM;
     240                 :            : 
     241                 :          0 :         smbdev->psmouse = psmouse;
     242                 :          0 :         smbdev->board = *board;
     243                 :          0 :         smbdev->need_deactivate = need_deactivate;
     244                 :            : 
     245         [ #  # ]:          0 :         if (pdata) {
     246                 :          0 :                 smbdev->board.platform_data = kmemdup(pdata, pdata_size,
     247                 :            :                                                       GFP_KERNEL);
     248         [ #  # ]:          0 :                 if (!smbdev->board.platform_data) {
     249                 :          0 :                         kfree(smbdev);
     250                 :          0 :                         return -ENOMEM;
     251                 :            :                 }
     252                 :            :         }
     253                 :            : 
     254         [ #  # ]:          0 :         if (need_deactivate)
     255                 :          0 :                 psmouse_deactivate(psmouse);
     256                 :            : 
     257                 :          0 :         psmouse->private = smbdev;
     258                 :          0 :         psmouse->protocol_handler = psmouse_smbus_process_byte;
     259                 :          0 :         psmouse->reconnect = psmouse_smbus_reconnect;
     260                 :          0 :         psmouse->fast_reconnect = psmouse_smbus_reconnect;
     261                 :          0 :         psmouse->disconnect = psmouse_smbus_disconnect;
     262                 :          0 :         psmouse->resync_time = 0;
     263                 :            : 
     264                 :          0 :         mutex_lock(&psmouse_smbus_mutex);
     265                 :          0 :         list_add_tail(&smbdev->node, &psmouse_smbus_list);
     266                 :          0 :         mutex_unlock(&psmouse_smbus_mutex);
     267                 :            : 
     268                 :            :         /* Bind to already existing adapters right away */
     269                 :          0 :         error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion);
     270                 :            : 
     271         [ #  # ]:          0 :         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                 :          0 :         kfree(smbdev->board.platform_data);
     281                 :          0 :         smbdev->board.platform_data = NULL;
     282                 :            : 
     283         [ #  # ]:          0 :         if (error < 0 || !leave_breadcrumbs) {
     284                 :          0 :                 mutex_lock(&psmouse_smbus_mutex);
     285                 :          0 :                 list_del(&smbdev->node);
     286                 :          0 :                 mutex_unlock(&psmouse_smbus_mutex);
     287                 :            : 
     288                 :          0 :                 kfree(smbdev);
     289                 :            :         }
     290                 :            : 
     291         [ #  # ]:          0 :         return error < 0 ? error : -EAGAIN;
     292                 :            : }
     293                 :            : 
     294                 :         28 : int __init psmouse_smbus_module_init(void)
     295                 :            : {
     296                 :         28 :         int error;
     297                 :            : 
     298                 :         28 :         error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
     299         [ -  + ]:         28 :         if (error) {
     300                 :          0 :                 pr_err("failed to register i2c bus notifier: %d\n", error);
     301                 :          0 :                 return error;
     302                 :            :         }
     303                 :            : 
     304                 :            :         return 0;
     305                 :            : }
     306                 :            : 
     307                 :          0 : void psmouse_smbus_module_exit(void)
     308                 :            : {
     309                 :          0 :         bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
     310                 :          0 :         flush_scheduled_work();
     311                 :          0 : }

Generated by: LCOV version 1.14