led.c (3258B)
1// SPDX-License-Identifier: GPL-2.0-only 2#include <linux/kernel.h> 3#include <linux/module.h> 4#include <linux/init.h> 5#include <linux/proc_fs.h> 6#include <linux/seq_file.h> 7#include <linux/slab.h> 8#include <linux/string.h> 9#include <linux/jiffies.h> 10#include <linux/timer.h> 11#include <linux/uaccess.h> 12#include <linux/sched/loadavg.h> 13 14#include <asm/auxio.h> 15 16#define LED_MAX_LENGTH 8 /* maximum chars written to proc file */ 17 18static inline void led_toggle(void) 19{ 20 unsigned char val = get_auxio(); 21 unsigned char on, off; 22 23 if (val & AUXIO_LED) { 24 on = 0; 25 off = AUXIO_LED; 26 } else { 27 on = AUXIO_LED; 28 off = 0; 29 } 30 31 set_auxio(on, off); 32} 33 34static struct timer_list led_blink_timer; 35static unsigned long led_blink_timer_timeout; 36 37static void led_blink(struct timer_list *unused) 38{ 39 unsigned long timeout = led_blink_timer_timeout; 40 41 led_toggle(); 42 43 /* reschedule */ 44 if (!timeout) { /* blink according to load */ 45 led_blink_timer.expires = jiffies + 46 ((1 + (avenrun[0] >> FSHIFT)) * HZ); 47 } else { /* blink at user specified interval */ 48 led_blink_timer.expires = jiffies + (timeout * HZ); 49 } 50 add_timer(&led_blink_timer); 51} 52 53#ifdef CONFIG_PROC_FS 54static int led_proc_show(struct seq_file *m, void *v) 55{ 56 if (get_auxio() & AUXIO_LED) 57 seq_puts(m, "on\n"); 58 else 59 seq_puts(m, "off\n"); 60 return 0; 61} 62 63static int led_proc_open(struct inode *inode, struct file *file) 64{ 65 return single_open(file, led_proc_show, NULL); 66} 67 68static ssize_t led_proc_write(struct file *file, const char __user *buffer, 69 size_t count, loff_t *ppos) 70{ 71 char *buf = NULL; 72 73 if (count > LED_MAX_LENGTH) 74 count = LED_MAX_LENGTH; 75 76 buf = memdup_user_nul(buffer, count); 77 if (IS_ERR(buf)) 78 return PTR_ERR(buf); 79 80 /* work around \n when echo'ing into proc */ 81 if (buf[count - 1] == '\n') 82 buf[count - 1] = '\0'; 83 84 /* before we change anything we want to stop any running timers, 85 * otherwise calls such as on will have no persistent effect 86 */ 87 del_timer_sync(&led_blink_timer); 88 89 if (!strcmp(buf, "on")) { 90 auxio_set_led(AUXIO_LED_ON); 91 } else if (!strcmp(buf, "toggle")) { 92 led_toggle(); 93 } else if ((*buf > '0') && (*buf <= '9')) { 94 led_blink_timer_timeout = simple_strtoul(buf, NULL, 10); 95 led_blink(&led_blink_timer); 96 } else if (!strcmp(buf, "load")) { 97 led_blink_timer_timeout = 0; 98 led_blink(&led_blink_timer); 99 } else { 100 auxio_set_led(AUXIO_LED_OFF); 101 } 102 103 kfree(buf); 104 105 return count; 106} 107 108static const struct proc_ops led_proc_ops = { 109 .proc_open = led_proc_open, 110 .proc_read = seq_read, 111 .proc_lseek = seq_lseek, 112 .proc_release = single_release, 113 .proc_write = led_proc_write, 114}; 115#endif 116 117#define LED_VERSION "0.1" 118 119static int __init led_init(void) 120{ 121 timer_setup(&led_blink_timer, led_blink, 0); 122 123#ifdef CONFIG_PROC_FS 124 if (!proc_create("led", 0, NULL, &led_proc_ops)) 125 return -ENOMEM; 126#endif 127 printk(KERN_INFO 128 "led: version %s, Lars Kotthoff <metalhead@metalhead.ws>\n", 129 LED_VERSION); 130 131 return 0; 132} 133 134static void __exit led_exit(void) 135{ 136 remove_proc_entry("led", NULL); 137 del_timer_sync(&led_blink_timer); 138} 139 140module_init(led_init); 141module_exit(led_exit); 142 143MODULE_AUTHOR("Lars Kotthoff <metalhead@metalhead.ws>"); 144MODULE_DESCRIPTION("Provides control of the front LED on SPARC systems."); 145MODULE_LICENSE("GPL"); 146MODULE_VERSION(LED_VERSION);