LCOV - code coverage report
Current view: top level - drivers/leds/trigger - ledtrig-heartbeat.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_real_modules_combined.info Lines: 6 66 9.1 %
Date: 2020-09-30 20:25:40 Functions: 1 9 11.1 %
Branches: 1 24 4.2 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-only
       2                 :            : /*
       3                 :            :  * LED Heartbeat Trigger
       4                 :            :  *
       5                 :            :  * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
       6                 :            :  *
       7                 :            :  * Based on Richard Purdie's ledtrig-timer.c and some arch's
       8                 :            :  * CONFIG_HEARTBEAT code.
       9                 :            :  */
      10                 :            : 
      11                 :            : #include <linux/module.h>
      12                 :            : #include <linux/kernel.h>
      13                 :            : #include <linux/init.h>
      14                 :            : #include <linux/slab.h>
      15                 :            : #include <linux/timer.h>
      16                 :            : #include <linux/sched.h>
      17                 :            : #include <linux/sched/loadavg.h>
      18                 :            : #include <linux/leds.h>
      19                 :            : #include <linux/reboot.h>
      20                 :            : #include "../leds.h"
      21                 :            : 
      22                 :            : static int panic_heartbeats;
      23                 :            : 
      24                 :            : struct heartbeat_trig_data {
      25                 :            :         struct led_classdev *led_cdev;
      26                 :            :         unsigned int phase;
      27                 :            :         unsigned int period;
      28                 :            :         struct timer_list timer;
      29                 :            :         unsigned int invert;
      30                 :            : };
      31                 :            : 
      32                 :          0 : static void led_heartbeat_function(struct timer_list *t)
      33                 :            : {
      34                 :            :         struct heartbeat_trig_data *heartbeat_data =
      35                 :            :                 from_timer(heartbeat_data, t, timer);
      36                 :            :         struct led_classdev *led_cdev;
      37                 :            :         unsigned long brightness = LED_OFF;
      38                 :            :         unsigned long delay = 0;
      39                 :            : 
      40                 :          0 :         led_cdev = heartbeat_data->led_cdev;
      41                 :            : 
      42         [ #  # ]:          0 :         if (unlikely(panic_heartbeats)) {
      43                 :          0 :                 led_set_brightness_nosleep(led_cdev, LED_OFF);
      44                 :          0 :                 return;
      45                 :            :         }
      46                 :            : 
      47         [ #  # ]:          0 :         if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
      48                 :          0 :                 led_cdev->blink_brightness = led_cdev->new_blink_brightness;
      49                 :            : 
      50                 :            :         /* acts like an actual heart beat -- ie thump-thump-pause... */
      51   [ #  #  #  # ]:          0 :         switch (heartbeat_data->phase) {
      52                 :            :         case 0:
      53                 :            :                 /*
      54                 :            :                  * The hyperbolic function below modifies the
      55                 :            :                  * heartbeat period length in dependency of the
      56                 :            :                  * current (1min) load. It goes through the points
      57                 :            :                  * f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
      58                 :            :                  */
      59                 :          0 :                 heartbeat_data->period = 300 +
      60                 :          0 :                         (6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
      61                 :          0 :                 heartbeat_data->period =
      62                 :            :                         msecs_to_jiffies(heartbeat_data->period);
      63                 :            :                 delay = msecs_to_jiffies(70);
      64                 :          0 :                 heartbeat_data->phase++;
      65         [ #  # ]:          0 :                 if (!heartbeat_data->invert)
      66                 :          0 :                         brightness = led_cdev->blink_brightness;
      67                 :            :                 break;
      68                 :            :         case 1:
      69                 :          0 :                 delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
      70                 :          0 :                 heartbeat_data->phase++;
      71         [ #  # ]:          0 :                 if (heartbeat_data->invert)
      72                 :          0 :                         brightness = led_cdev->blink_brightness;
      73                 :            :                 break;
      74                 :            :         case 2:
      75                 :            :                 delay = msecs_to_jiffies(70);
      76                 :          0 :                 heartbeat_data->phase++;
      77         [ #  # ]:          0 :                 if (!heartbeat_data->invert)
      78                 :          0 :                         brightness = led_cdev->blink_brightness;
      79                 :            :                 break;
      80                 :            :         default:
      81                 :          0 :                 delay = heartbeat_data->period - heartbeat_data->period / 4 -
      82                 :            :                         msecs_to_jiffies(70);
      83                 :          0 :                 heartbeat_data->phase = 0;
      84         [ #  # ]:          0 :                 if (heartbeat_data->invert)
      85                 :          0 :                         brightness = led_cdev->blink_brightness;
      86                 :            :                 break;
      87                 :            :         }
      88                 :            : 
      89                 :          0 :         led_set_brightness_nosleep(led_cdev, brightness);
      90                 :          0 :         mod_timer(&heartbeat_data->timer, jiffies + delay);
      91                 :            : }
      92                 :            : 
      93                 :          0 : static ssize_t led_invert_show(struct device *dev,
      94                 :            :                 struct device_attribute *attr, char *buf)
      95                 :            : {
      96                 :            :         struct heartbeat_trig_data *heartbeat_data =
      97                 :            :                 led_trigger_get_drvdata(dev);
      98                 :            : 
      99                 :          0 :         return sprintf(buf, "%u\n", heartbeat_data->invert);
     100                 :            : }
     101                 :            : 
     102                 :          0 : static ssize_t led_invert_store(struct device *dev,
     103                 :            :                 struct device_attribute *attr, const char *buf, size_t size)
     104                 :            : {
     105                 :            :         struct heartbeat_trig_data *heartbeat_data =
     106                 :            :                 led_trigger_get_drvdata(dev);
     107                 :            :         unsigned long state;
     108                 :            :         int ret;
     109                 :            : 
     110                 :            :         ret = kstrtoul(buf, 0, &state);
     111         [ #  # ]:          0 :         if (ret)
     112                 :            :                 return ret;
     113                 :            : 
     114                 :          0 :         heartbeat_data->invert = !!state;
     115                 :            : 
     116                 :          0 :         return size;
     117                 :            : }
     118                 :            : 
     119                 :            : static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
     120                 :            : 
     121                 :            : static struct attribute *heartbeat_trig_attrs[] = {
     122                 :            :         &dev_attr_invert.attr,
     123                 :            :         NULL
     124                 :            : };
     125                 :            : ATTRIBUTE_GROUPS(heartbeat_trig);
     126                 :            : 
     127                 :          0 : static int heartbeat_trig_activate(struct led_classdev *led_cdev)
     128                 :            : {
     129                 :            :         struct heartbeat_trig_data *heartbeat_data;
     130                 :            : 
     131                 :          0 :         heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
     132         [ #  # ]:          0 :         if (!heartbeat_data)
     133                 :            :                 return -ENOMEM;
     134                 :            : 
     135                 :            :         led_set_trigger_data(led_cdev, heartbeat_data);
     136                 :          0 :         heartbeat_data->led_cdev = led_cdev;
     137                 :            : 
     138                 :          0 :         timer_setup(&heartbeat_data->timer, led_heartbeat_function, 0);
     139                 :          0 :         heartbeat_data->phase = 0;
     140         [ #  # ]:          0 :         if (!led_cdev->blink_brightness)
     141                 :          0 :                 led_cdev->blink_brightness = led_cdev->max_brightness;
     142                 :          0 :         led_heartbeat_function(&heartbeat_data->timer);
     143                 :          0 :         set_bit(LED_BLINK_SW, &led_cdev->work_flags);
     144                 :            : 
     145                 :          0 :         return 0;
     146                 :            : }
     147                 :            : 
     148                 :          0 : static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
     149                 :            : {
     150                 :            :         struct heartbeat_trig_data *heartbeat_data =
     151                 :            :                 led_get_trigger_data(led_cdev);
     152                 :            : 
     153                 :          0 :         del_timer_sync(&heartbeat_data->timer);
     154                 :          0 :         kfree(heartbeat_data);
     155                 :          0 :         clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
     156                 :          0 : }
     157                 :            : 
     158                 :            : static struct led_trigger heartbeat_led_trigger = {
     159                 :            :         .name     = "heartbeat",
     160                 :            :         .activate = heartbeat_trig_activate,
     161                 :            :         .deactivate = heartbeat_trig_deactivate,
     162                 :            :         .groups = heartbeat_trig_groups,
     163                 :            : };
     164                 :            : 
     165                 :          0 : static int heartbeat_reboot_notifier(struct notifier_block *nb,
     166                 :            :                                      unsigned long code, void *unused)
     167                 :            : {
     168                 :          0 :         led_trigger_unregister(&heartbeat_led_trigger);
     169                 :          0 :         return NOTIFY_DONE;
     170                 :            : }
     171                 :            : 
     172                 :          0 : static int heartbeat_panic_notifier(struct notifier_block *nb,
     173                 :            :                                      unsigned long code, void *unused)
     174                 :            : {
     175                 :          0 :         panic_heartbeats = 1;
     176                 :          0 :         return NOTIFY_DONE;
     177                 :            : }
     178                 :            : 
     179                 :            : static struct notifier_block heartbeat_reboot_nb = {
     180                 :            :         .notifier_call = heartbeat_reboot_notifier,
     181                 :            : };
     182                 :            : 
     183                 :            : static struct notifier_block heartbeat_panic_nb = {
     184                 :            :         .notifier_call = heartbeat_panic_notifier,
     185                 :            : };
     186                 :            : 
     187                 :        207 : static int __init heartbeat_trig_init(void)
     188                 :            : {
     189                 :        207 :         int rc = led_trigger_register(&heartbeat_led_trigger);
     190                 :            : 
     191         [ +  - ]:        207 :         if (!rc) {
     192                 :        207 :                 atomic_notifier_chain_register(&panic_notifier_list,
     193                 :            :                                                &heartbeat_panic_nb);
     194                 :        207 :                 register_reboot_notifier(&heartbeat_reboot_nb);
     195                 :            :         }
     196                 :        207 :         return rc;
     197                 :            : }
     198                 :            : 
     199                 :          0 : static void __exit heartbeat_trig_exit(void)
     200                 :            : {
     201                 :          0 :         unregister_reboot_notifier(&heartbeat_reboot_nb);
     202                 :          0 :         atomic_notifier_chain_unregister(&panic_notifier_list,
     203                 :            :                                          &heartbeat_panic_nb);
     204                 :          0 :         led_trigger_unregister(&heartbeat_led_trigger);
     205                 :          0 : }
     206                 :            : 
     207                 :            : module_init(heartbeat_trig_init);
     208                 :            : module_exit(heartbeat_trig_exit);
     209                 :            : 
     210                 :            : MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
     211                 :            : MODULE_DESCRIPTION("Heartbeat LED trigger");
     212                 :            : MODULE_LICENSE("GPL v2");

Generated by: LCOV version 1.14