18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright 2017 Ben Whitten <ben.whitten@gmail.com> 38c2ecf20Sopenharmony_ci// Copyright 2007 Oliver Jowett <oliver@opencloud.com> 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// LED Kernel Netdev Trigger 68c2ecf20Sopenharmony_ci// 78c2ecf20Sopenharmony_ci// Toggles the LED to reflect the link and traffic state of a named net device 88c2ecf20Sopenharmony_ci// 98c2ecf20Sopenharmony_ci// Derived from ledtrig-timer.c which is: 108c2ecf20Sopenharmony_ci// Copyright 2005-2006 Openedhand Ltd. 118c2ecf20Sopenharmony_ci// Author: Richard Purdie <rpurdie@openedhand.com> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/atomic.h> 148c2ecf20Sopenharmony_ci#include <linux/ctype.h> 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/leds.h> 208c2ecf20Sopenharmony_ci#include <linux/list.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 238c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 248c2ecf20Sopenharmony_ci#include <linux/timer.h> 258c2ecf20Sopenharmony_ci#include "../leds.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * Configurable sysfs attributes: 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * device_name - network device name to monitor 318c2ecf20Sopenharmony_ci * interval - duration of LED blink, in milliseconds 328c2ecf20Sopenharmony_ci * link - LED's normal state reflects whether the link is up 338c2ecf20Sopenharmony_ci * (has carrier) or not 348c2ecf20Sopenharmony_ci * tx - LED blinks on transmitted data 358c2ecf20Sopenharmony_ci * rx - LED blinks on receive data 368c2ecf20Sopenharmony_ci * 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct led_netdev_data { 408c2ecf20Sopenharmony_ci spinlock_t lock; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci struct delayed_work work; 438c2ecf20Sopenharmony_ci struct notifier_block notifier; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci struct led_classdev *led_cdev; 468c2ecf20Sopenharmony_ci struct net_device *net_dev; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci char device_name[IFNAMSIZ]; 498c2ecf20Sopenharmony_ci atomic_t interval; 508c2ecf20Sopenharmony_ci unsigned int last_activity; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci unsigned long mode; 538c2ecf20Sopenharmony_ci#define NETDEV_LED_LINK 0 548c2ecf20Sopenharmony_ci#define NETDEV_LED_TX 1 558c2ecf20Sopenharmony_ci#define NETDEV_LED_RX 2 568c2ecf20Sopenharmony_ci#define NETDEV_LED_MODE_LINKUP 3 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cienum netdev_led_attr { 608c2ecf20Sopenharmony_ci NETDEV_ATTR_LINK, 618c2ecf20Sopenharmony_ci NETDEV_ATTR_TX, 628c2ecf20Sopenharmony_ci NETDEV_ATTR_RX 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void set_baseline_state(struct led_netdev_data *trigger_data) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci int current_brightness; 688c2ecf20Sopenharmony_ci struct led_classdev *led_cdev = trigger_data->led_cdev; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci current_brightness = led_cdev->brightness; 718c2ecf20Sopenharmony_ci if (current_brightness) 728c2ecf20Sopenharmony_ci led_cdev->blink_brightness = current_brightness; 738c2ecf20Sopenharmony_ci if (!led_cdev->blink_brightness) 748c2ecf20Sopenharmony_ci led_cdev->blink_brightness = led_cdev->max_brightness; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci if (!test_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode)) 778c2ecf20Sopenharmony_ci led_set_brightness(led_cdev, LED_OFF); 788c2ecf20Sopenharmony_ci else { 798c2ecf20Sopenharmony_ci if (test_bit(NETDEV_LED_LINK, &trigger_data->mode)) 808c2ecf20Sopenharmony_ci led_set_brightness(led_cdev, 818c2ecf20Sopenharmony_ci led_cdev->blink_brightness); 828c2ecf20Sopenharmony_ci else 838c2ecf20Sopenharmony_ci led_set_brightness(led_cdev, LED_OFF); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* If we are looking for RX/TX start periodically 868c2ecf20Sopenharmony_ci * checking stats 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ci if (test_bit(NETDEV_LED_TX, &trigger_data->mode) || 898c2ecf20Sopenharmony_ci test_bit(NETDEV_LED_RX, &trigger_data->mode)) 908c2ecf20Sopenharmony_ci schedule_delayed_work(&trigger_data->work, 0); 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic ssize_t device_name_show(struct device *dev, 958c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); 988c2ecf20Sopenharmony_ci ssize_t len; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci spin_lock_bh(&trigger_data->lock); 1018c2ecf20Sopenharmony_ci len = sprintf(buf, "%s\n", trigger_data->device_name); 1028c2ecf20Sopenharmony_ci spin_unlock_bh(&trigger_data->lock); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return len; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic ssize_t device_name_store(struct device *dev, 1088c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 1098c2ecf20Sopenharmony_ci size_t size) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (size >= IFNAMSIZ) 1148c2ecf20Sopenharmony_ci return -EINVAL; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&trigger_data->work); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci spin_lock_bh(&trigger_data->lock); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (trigger_data->net_dev) { 1218c2ecf20Sopenharmony_ci dev_put(trigger_data->net_dev); 1228c2ecf20Sopenharmony_ci trigger_data->net_dev = NULL; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci memcpy(trigger_data->device_name, buf, size); 1268c2ecf20Sopenharmony_ci trigger_data->device_name[size] = 0; 1278c2ecf20Sopenharmony_ci if (size > 0 && trigger_data->device_name[size - 1] == '\n') 1288c2ecf20Sopenharmony_ci trigger_data->device_name[size - 1] = 0; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (trigger_data->device_name[0] != 0) 1318c2ecf20Sopenharmony_ci trigger_data->net_dev = 1328c2ecf20Sopenharmony_ci dev_get_by_name(&init_net, trigger_data->device_name); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); 1358c2ecf20Sopenharmony_ci if (trigger_data->net_dev != NULL) 1368c2ecf20Sopenharmony_ci if (netif_carrier_ok(trigger_data->net_dev)) 1378c2ecf20Sopenharmony_ci set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci trigger_data->last_activity = 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci set_baseline_state(trigger_data); 1428c2ecf20Sopenharmony_ci spin_unlock_bh(&trigger_data->lock); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return size; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(device_name); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic ssize_t netdev_led_attr_show(struct device *dev, char *buf, 1508c2ecf20Sopenharmony_ci enum netdev_led_attr attr) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); 1538c2ecf20Sopenharmony_ci int bit; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci switch (attr) { 1568c2ecf20Sopenharmony_ci case NETDEV_ATTR_LINK: 1578c2ecf20Sopenharmony_ci bit = NETDEV_LED_LINK; 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci case NETDEV_ATTR_TX: 1608c2ecf20Sopenharmony_ci bit = NETDEV_LED_TX; 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci case NETDEV_ATTR_RX: 1638c2ecf20Sopenharmony_ci bit = NETDEV_LED_RX; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci default: 1668c2ecf20Sopenharmony_ci return -EINVAL; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode)); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic ssize_t netdev_led_attr_store(struct device *dev, const char *buf, 1738c2ecf20Sopenharmony_ci size_t size, enum netdev_led_attr attr) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); 1768c2ecf20Sopenharmony_ci unsigned long state; 1778c2ecf20Sopenharmony_ci int ret; 1788c2ecf20Sopenharmony_ci int bit; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 0, &state); 1818c2ecf20Sopenharmony_ci if (ret) 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci switch (attr) { 1858c2ecf20Sopenharmony_ci case NETDEV_ATTR_LINK: 1868c2ecf20Sopenharmony_ci bit = NETDEV_LED_LINK; 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci case NETDEV_ATTR_TX: 1898c2ecf20Sopenharmony_ci bit = NETDEV_LED_TX; 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci case NETDEV_ATTR_RX: 1928c2ecf20Sopenharmony_ci bit = NETDEV_LED_RX; 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci default: 1958c2ecf20Sopenharmony_ci return -EINVAL; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&trigger_data->work); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (state) 2018c2ecf20Sopenharmony_ci set_bit(bit, &trigger_data->mode); 2028c2ecf20Sopenharmony_ci else 2038c2ecf20Sopenharmony_ci clear_bit(bit, &trigger_data->mode); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci set_baseline_state(trigger_data); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return size; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic ssize_t link_show(struct device *dev, 2118c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic ssize_t link_store(struct device *dev, 2178c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t size) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(link); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic ssize_t tx_show(struct device *dev, 2258c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic ssize_t tx_store(struct device *dev, 2318c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t size) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(tx); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic ssize_t rx_show(struct device *dev, 2398c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic ssize_t rx_store(struct device *dev, 2458c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t size) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(rx); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic ssize_t interval_show(struct device *dev, 2538c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", 2588c2ecf20Sopenharmony_ci jiffies_to_msecs(atomic_read(&trigger_data->interval))); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic ssize_t interval_store(struct device *dev, 2628c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 2638c2ecf20Sopenharmony_ci size_t size) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); 2668c2ecf20Sopenharmony_ci unsigned long value; 2678c2ecf20Sopenharmony_ci int ret; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 0, &value); 2708c2ecf20Sopenharmony_ci if (ret) 2718c2ecf20Sopenharmony_ci return ret; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* impose some basic bounds on the timer interval */ 2748c2ecf20Sopenharmony_ci if (value >= 5 && value <= 10000) { 2758c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&trigger_data->work); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci atomic_set(&trigger_data->interval, msecs_to_jiffies(value)); 2788c2ecf20Sopenharmony_ci set_baseline_state(trigger_data); /* resets timer */ 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return size; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(interval); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic struct attribute *netdev_trig_attrs[] = { 2878c2ecf20Sopenharmony_ci &dev_attr_device_name.attr, 2888c2ecf20Sopenharmony_ci &dev_attr_link.attr, 2898c2ecf20Sopenharmony_ci &dev_attr_rx.attr, 2908c2ecf20Sopenharmony_ci &dev_attr_tx.attr, 2918c2ecf20Sopenharmony_ci &dev_attr_interval.attr, 2928c2ecf20Sopenharmony_ci NULL 2938c2ecf20Sopenharmony_ci}; 2948c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(netdev_trig); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic int netdev_trig_notify(struct notifier_block *nb, 2978c2ecf20Sopenharmony_ci unsigned long evt, void *dv) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct net_device *dev = 3008c2ecf20Sopenharmony_ci netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv); 3018c2ecf20Sopenharmony_ci struct led_netdev_data *trigger_data = 3028c2ecf20Sopenharmony_ci container_of(nb, struct led_netdev_data, notifier); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE 3058c2ecf20Sopenharmony_ci && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER 3068c2ecf20Sopenharmony_ci && evt != NETDEV_CHANGENAME) 3078c2ecf20Sopenharmony_ci return NOTIFY_DONE; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (!(dev == trigger_data->net_dev || 3108c2ecf20Sopenharmony_ci (evt == NETDEV_CHANGENAME && !strcmp(dev->name, trigger_data->device_name)) || 3118c2ecf20Sopenharmony_ci (evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name)))) 3128c2ecf20Sopenharmony_ci return NOTIFY_DONE; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&trigger_data->work); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci spin_lock_bh(&trigger_data->lock); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); 3198c2ecf20Sopenharmony_ci switch (evt) { 3208c2ecf20Sopenharmony_ci case NETDEV_CHANGENAME: 3218c2ecf20Sopenharmony_ci if (netif_carrier_ok(dev)) 3228c2ecf20Sopenharmony_ci set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); 3238c2ecf20Sopenharmony_ci fallthrough; 3248c2ecf20Sopenharmony_ci case NETDEV_REGISTER: 3258c2ecf20Sopenharmony_ci if (trigger_data->net_dev) 3268c2ecf20Sopenharmony_ci dev_put(trigger_data->net_dev); 3278c2ecf20Sopenharmony_ci dev_hold(dev); 3288c2ecf20Sopenharmony_ci trigger_data->net_dev = dev; 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 3318c2ecf20Sopenharmony_ci dev_put(trigger_data->net_dev); 3328c2ecf20Sopenharmony_ci trigger_data->net_dev = NULL; 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci case NETDEV_UP: 3358c2ecf20Sopenharmony_ci case NETDEV_CHANGE: 3368c2ecf20Sopenharmony_ci if (netif_carrier_ok(dev)) 3378c2ecf20Sopenharmony_ci set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci set_baseline_state(trigger_data); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci spin_unlock_bh(&trigger_data->lock); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return NOTIFY_DONE; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/* here's the real work! */ 3498c2ecf20Sopenharmony_cistatic void netdev_trig_work(struct work_struct *work) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct led_netdev_data *trigger_data = 3528c2ecf20Sopenharmony_ci container_of(work, struct led_netdev_data, work.work); 3538c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *dev_stats; 3548c2ecf20Sopenharmony_ci unsigned int new_activity; 3558c2ecf20Sopenharmony_ci struct rtnl_link_stats64 temp; 3568c2ecf20Sopenharmony_ci unsigned long interval; 3578c2ecf20Sopenharmony_ci int invert; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* If we dont have a device, insure we are off */ 3608c2ecf20Sopenharmony_ci if (!trigger_data->net_dev) { 3618c2ecf20Sopenharmony_ci led_set_brightness(trigger_data->led_cdev, LED_OFF); 3628c2ecf20Sopenharmony_ci return; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* If we are not looking for RX/TX then return */ 3668c2ecf20Sopenharmony_ci if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) && 3678c2ecf20Sopenharmony_ci !test_bit(NETDEV_LED_RX, &trigger_data->mode)) 3688c2ecf20Sopenharmony_ci return; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci dev_stats = dev_get_stats(trigger_data->net_dev, &temp); 3718c2ecf20Sopenharmony_ci new_activity = 3728c2ecf20Sopenharmony_ci (test_bit(NETDEV_LED_TX, &trigger_data->mode) ? 3738c2ecf20Sopenharmony_ci dev_stats->tx_packets : 0) + 3748c2ecf20Sopenharmony_ci (test_bit(NETDEV_LED_RX, &trigger_data->mode) ? 3758c2ecf20Sopenharmony_ci dev_stats->rx_packets : 0); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (trigger_data->last_activity != new_activity) { 3788c2ecf20Sopenharmony_ci led_stop_software_blink(trigger_data->led_cdev); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode); 3818c2ecf20Sopenharmony_ci interval = jiffies_to_msecs( 3828c2ecf20Sopenharmony_ci atomic_read(&trigger_data->interval)); 3838c2ecf20Sopenharmony_ci /* base state is ON (link present) */ 3848c2ecf20Sopenharmony_ci led_blink_set_oneshot(trigger_data->led_cdev, 3858c2ecf20Sopenharmony_ci &interval, 3868c2ecf20Sopenharmony_ci &interval, 3878c2ecf20Sopenharmony_ci invert); 3888c2ecf20Sopenharmony_ci trigger_data->last_activity = new_activity; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci schedule_delayed_work(&trigger_data->work, 3928c2ecf20Sopenharmony_ci (atomic_read(&trigger_data->interval)*2)); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic int netdev_trig_activate(struct led_classdev *led_cdev) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct led_netdev_data *trigger_data; 3988c2ecf20Sopenharmony_ci int rc; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL); 4018c2ecf20Sopenharmony_ci if (!trigger_data) 4028c2ecf20Sopenharmony_ci return -ENOMEM; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci spin_lock_init(&trigger_data->lock); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci trigger_data->notifier.notifier_call = netdev_trig_notify; 4078c2ecf20Sopenharmony_ci trigger_data->notifier.priority = 10; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci trigger_data->led_cdev = led_cdev; 4128c2ecf20Sopenharmony_ci trigger_data->net_dev = NULL; 4138c2ecf20Sopenharmony_ci trigger_data->device_name[0] = 0; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci trigger_data->mode = 0; 4168c2ecf20Sopenharmony_ci atomic_set(&trigger_data->interval, msecs_to_jiffies(50)); 4178c2ecf20Sopenharmony_ci trigger_data->last_activity = 0; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci led_set_trigger_data(led_cdev, trigger_data); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci rc = register_netdevice_notifier(&trigger_data->notifier); 4228c2ecf20Sopenharmony_ci if (rc) 4238c2ecf20Sopenharmony_ci kfree(trigger_data); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return rc; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic void netdev_trig_deactivate(struct led_classdev *led_cdev) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct led_netdev_data *trigger_data = led_get_trigger_data(led_cdev); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&trigger_data->notifier); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&trigger_data->work); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (trigger_data->net_dev) 4378c2ecf20Sopenharmony_ci dev_put(trigger_data->net_dev); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci kfree(trigger_data); 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic struct led_trigger netdev_led_trigger = { 4438c2ecf20Sopenharmony_ci .name = "netdev", 4448c2ecf20Sopenharmony_ci .activate = netdev_trig_activate, 4458c2ecf20Sopenharmony_ci .deactivate = netdev_trig_deactivate, 4468c2ecf20Sopenharmony_ci .groups = netdev_trig_groups, 4478c2ecf20Sopenharmony_ci}; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int __init netdev_trig_init(void) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci return led_trigger_register(&netdev_led_trigger); 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic void __exit netdev_trig_exit(void) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci led_trigger_unregister(&netdev_led_trigger); 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cimodule_init(netdev_trig_init); 4608c2ecf20Sopenharmony_cimodule_exit(netdev_trig_exit); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>"); 4638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>"); 4648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Netdev LED trigger"); 4658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 466