// SPDX-License-Identifier: GPL-2.0-only1/*2* NetWinder Button Driver-3* Copyright (C) Alex Holden <[email protected]> 1998, 1999.4*5*/67#include <linux/module.h>8#include <linux/kernel.h>9#include <linux/sched/signal.h>10#include <linux/interrupt.h>11#include <linux/time.h>12#include <linux/timer.h>13#include <linux/fs.h>14#include <linux/miscdevice.h>15#include <linux/string.h>16#include <linux/errno.h>17#include <linux/init.h>1819#include <linux/uaccess.h>20#include <asm/irq.h>21#include <asm/mach-types.h>2223#define __NWBUTTON_C /* Tell the header file who we are */24#include "nwbutton.h"2526static void button_sequence_finished(struct timer_list *unused);2728static int button_press_count; /* The count of button presses */29/* Times for the end of a sequence */30static DEFINE_TIMER(button_timer, button_sequence_finished);31static DECLARE_WAIT_QUEUE_HEAD(button_wait_queue); /* Used for blocking read */32static char button_output_buffer[32]; /* Stores data to write out of device */33static int bcount; /* The number of bytes in the buffer */34static int bdelay = BUTTON_DELAY; /* The delay, in jiffies */35static struct button_callback button_callback_list[32]; /* The callback list */36static int callback_count; /* The number of callbacks registered */37static int reboot_count = NUM_PRESSES_REBOOT; /* Number of presses to reboot */3839/*40* This function is called by other drivers to register a callback function41* to be called when a particular number of button presses occurs.42* The callback list is a static array of 32 entries (I somehow doubt many43* people are ever going to want to register more than 32 different actions44* to be performed by the kernel on different numbers of button presses ;).45* However, if an attempt to register a 33rd entry (perhaps a stuck loop46* somewhere registering the same entry over and over?) it will fail to47* do so and return -ENOMEM. If an attempt is made to register a null pointer,48* it will fail to do so and return -EINVAL.49* Because callbacks can be unregistered at random the list can become50* fragmented, so we need to search through the list until we find the first51* free entry.52*53* FIXME: Has anyone spotted any locking functions int his code recently ??54*/5556int button_add_callback (void (*callback) (void), int count)57{58int lp = 0;59if (callback_count == 32) {60return -ENOMEM;61}62if (!callback) {63return -EINVAL;64}65callback_count++;66for (; (button_callback_list [lp].callback); lp++);67button_callback_list [lp].callback = callback;68button_callback_list [lp].count = count;69return 0;70}7172/*73* This function is called by other drivers to deregister a callback function.74* If you attempt to unregister a callback which does not exist, it will fail75* with -EINVAL. If there is more than one entry with the same address,76* because it searches the list from end to beginning, it will unregister the77* last one to be registered first (FILO- First In Last Out).78* Note that this is not necessarily true if the entries are not submitted79* at the same time, because another driver could have unregistered a callback80* between the submissions creating a gap earlier in the list, which would81* be filled first at submission time.82*/8384int button_del_callback (void (*callback) (void))85{86int lp = 31;87if (!callback) {88return -EINVAL;89}90while (lp >= 0) {91if ((button_callback_list [lp].callback) == callback) {92button_callback_list [lp].callback = NULL;93button_callback_list [lp].count = 0;94callback_count--;95return 0;96}97lp--;98}99return -EINVAL;100}101102/*103* This function is called by button_sequence_finished to search through the104* list of callback functions, and call any of them whose count argument105* matches the current count of button presses. It starts at the beginning106* of the list and works up to the end. It will refuse to follow a null107* pointer (which should never happen anyway).108*/109110static void button_consume_callbacks (int bpcount)111{112int lp = 0;113for (; lp <= 31; lp++) {114if ((button_callback_list [lp].count) == bpcount) {115if (button_callback_list [lp].callback) {116button_callback_list[lp].callback();117}118}119}120}121122/*123* This function is called when the button_timer times out.124* ie. When you don't press the button for bdelay jiffies, this is taken to125* mean you have ended the sequence of key presses, and this function is126* called to wind things up (write the press_count out to /dev/button, call127* any matching registered function callbacks, initiate reboot, etc.).128*/129130static void button_sequence_finished(struct timer_list *unused)131{132if (IS_ENABLED(CONFIG_NWBUTTON_REBOOT) &&133button_press_count == reboot_count)134kill_cad_pid(SIGINT, 1); /* Ask init to reboot us */135button_consume_callbacks (button_press_count);136bcount = sprintf (button_output_buffer, "%d\n", button_press_count);137button_press_count = 0; /* Reset the button press counter */138wake_up_interruptible (&button_wait_queue);139}140141/*142* This handler is called when the orange button is pressed (GPIO 10 of the143* SuperIO chip, which maps to logical IRQ 26). If the press_count is 0,144* this is the first press, so it starts a timer and increments the counter.145* If it is higher than 0, it deletes the old timer, starts a new one, and146* increments the counter.147*/148149static irqreturn_t button_handler (int irq, void *dev_id)150{151button_press_count++;152mod_timer(&button_timer, jiffies + bdelay);153154return IRQ_HANDLED;155}156157/*158* This function is called when a user space program attempts to read159* /dev/nwbutton. It puts the device to sleep on the wait queue until160* button_sequence_finished writes some data to the buffer and flushes161* the queue, at which point it writes the data out to the device and162* returns the number of characters it has written. This function is163* reentrant, so that many processes can be attempting to read from the164* device at any one time.165*/166167static int button_read (struct file *filp, char __user *buffer,168size_t count, loff_t *ppos)169{170DEFINE_WAIT(wait);171prepare_to_wait(&button_wait_queue, &wait, TASK_INTERRUPTIBLE);172schedule();173finish_wait(&button_wait_queue, &wait);174return (copy_to_user (buffer, &button_output_buffer, bcount))175? -EFAULT : bcount;176}177178/*179* This structure is the file operations structure, which specifies what180* callbacks functions the kernel should call when a user mode process181* attempts to perform these operations on the device.182*/183184static const struct file_operations button_fops = {185.owner = THIS_MODULE,186.read = button_read,187.llseek = noop_llseek,188};189190/*191* This structure is the misc device structure, which specifies the minor192* device number (158 in this case), the name of the device (for /proc/misc),193* and the address of the above file operations structure.194*/195196static struct miscdevice button_misc_device = {197BUTTON_MINOR,198"nwbutton",199&button_fops,200};201202/*203* This function is called to initialise the driver, either from misc.c at204* bootup if the driver is compiled into the kernel, or from init_module205* below at module insert time. It attempts to register the device node206* and the IRQ and fails with a warning message if either fails, though207* neither ever should because the device number and IRQ are unique to208* this driver.209*/210211static int __init nwbutton_init(void)212{213if (!machine_is_netwinder())214return -ENODEV;215216printk (KERN_INFO "NetWinder Button Driver Version %s (C) Alex Holden "217"<[email protected]> 1998.\n", VERSION);218219if (misc_register (&button_misc_device)) {220printk (KERN_WARNING "nwbutton: Couldn't register device 10, "221"%d.\n", BUTTON_MINOR);222return -EBUSY;223}224225if (request_irq (IRQ_NETWINDER_BUTTON, button_handler, 0,226"nwbutton", NULL)) {227printk (KERN_WARNING "nwbutton: IRQ %d is not free.\n",228IRQ_NETWINDER_BUTTON);229misc_deregister (&button_misc_device);230return -EIO;231}232return 0;233}234235static void __exit nwbutton_exit (void)236{237free_irq (IRQ_NETWINDER_BUTTON, NULL);238misc_deregister (&button_misc_device);239}240241242MODULE_AUTHOR("Alex Holden");243MODULE_DESCRIPTION("NetWinder button driver");244MODULE_LICENSE("GPL");245246module_init(nwbutton_init);247module_exit(nwbutton_exit);248249250