1/* 2 * NetWinder Button Driver- 3 * Copyright (C) Alex Holden <alex@linuxhacker.org> 1998, 1999. 4 * 5 */ 6 7#include <linux/module.h> 8#include <linux/kernel.h> 9#include <linux/sched.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> 18 19#include <asm/uaccess.h> 20#include <asm/irq.h> 21#include <asm/mach-types.h> 22 23#define __NWBUTTON_C /* Tell the header file who we are */ 24#include "nwbutton.h" 25 26static void button_sequence_finished (unsigned long parameters); 27 28static 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, 0, 0); 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 */ 38 39 40int button_add_callback (void (*callback) (void), int count) 41{ 42 int lp = 0; 43 if (callback_count == 32) { 44 return -ENOMEM; 45 } 46 if (!callback) { 47 return -EINVAL; 48 } 49 callback_count++; 50 for (; (button_callback_list [lp].callback); lp++); 51 button_callback_list [lp].callback = callback; 52 button_callback_list [lp].count = count; 53 return 0; 54} 55 56/* 57 * This function is called by other drivers to deregister a callback function. 58 * If you attempt to unregister a callback which does not exist, it will fail 59 * with -EINVAL. If there is more than one entry with the same address, 60 * because it searches the list from end to beginning, it will unregister the 61 * last one to be registered first (FILO- First In Last Out). 62 * Note that this is not neccessarily true if the entries are not submitted 63 * at the same time, because another driver could have unregistered a callback 64 * between the submissions creating a gap earlier in the list, which would 65 * be filled first at submission time. 66 */ 67 68int button_del_callback (void (*callback) (void)) 69{ 70 int lp = 31; 71 if (!callback) { 72 return -EINVAL; 73 } 74 while (lp >= 0) { 75 if ((button_callback_list [lp].callback) == callback) { 76 button_callback_list [lp].callback = NULL; 77 button_callback_list [lp].count = 0; 78 callback_count--; 79 return 0; 80 }; 81 lp--; 82 }; 83 return -EINVAL; 84} 85 86/* 87 * This function is called by button_sequence_finished to search through the 88 * list of callback functions, and call any of them whose count argument 89 * matches the current count of button presses. It starts at the beginning 90 * of the list and works up to the end. It will refuse to follow a null 91 * pointer (which should never happen anyway). 92 */ 93 94static void button_consume_callbacks (int bpcount) 95{ 96 int lp = 0; 97 for (; lp <= 31; lp++) { 98 if ((button_callback_list [lp].count) == bpcount) { 99 if (button_callback_list [lp].callback) { 100 button_callback_list[lp].callback(); 101 } 102 } 103 } 104} 105 106/* 107 * This function is called when the button_timer times out. 108 * ie. When you don't press the button for bdelay jiffies, this is taken to 109 * mean you have ended the sequence of key presses, and this function is 110 * called to wind things up (write the press_count out to /dev/button, call 111 * any matching registered function callbacks, initiate reboot, etc.). 112 */ 113 114static void button_sequence_finished (unsigned long parameters) 115{ 116#ifdef CONFIG_NWBUTTON_REBOOT /* Reboot using button is enabled */ 117 if (button_press_count == reboot_count) 118 kill_cad_pid(SIGINT, 1); /* Ask init to reboot us */ 119#endif /* CONFIG_NWBUTTON_REBOOT */ 120 button_consume_callbacks (button_press_count); 121 bcount = sprintf (button_output_buffer, "%d\n", button_press_count); 122 button_press_count = 0; /* Reset the button press counter */ 123 wake_up_interruptible (&button_wait_queue); 124} 125 126/* 127 * This handler is called when the orange button is pressed (GPIO 10 of the 128 * SuperIO chip, which maps to logical IRQ 26). If the press_count is 0, 129 * this is the first press, so it starts a timer and increments the counter. 130 * If it is higher than 0, it deletes the old timer, starts a new one, and 131 * increments the counter. 132 */ 133 134static irqreturn_t button_handler (int irq, void *dev_id) 135{ 136 button_press_count++; 137 mod_timer(&button_timer, jiffies + bdelay); 138 139 return IRQ_HANDLED; 140} 141 142/* 143 * This function is called when a user space program attempts to read 144 * /dev/nwbutton. It puts the device to sleep on the wait queue until 145 * button_sequence_finished writes some data to the buffer and flushes 146 * the queue, at which point it writes the data out to the device and 147 * returns the number of characters it has written. This function is 148 * reentrant, so that many processes can be attempting to read from the 149 * device at any one time. 150 */ 151 152static int button_read (struct file *filp, char __user *buffer, 153 size_t count, loff_t *ppos) 154{ 155 interruptible_sleep_on (&button_wait_queue); 156 return (copy_to_user (buffer, &button_output_buffer, bcount)) 157 ? -EFAULT : bcount; 158} 159 160/* 161 * This structure is the file operations structure, which specifies what 162 * callbacks functions the kernel should call when a user mode process 163 * attempts to perform these operations on the device. 164 */ 165 166static const struct file_operations button_fops = { 167 .owner = THIS_MODULE, 168 .read = button_read, 169}; 170 171/* 172 * This structure is the misc device structure, which specifies the minor 173 * device number (158 in this case), the name of the device (for /proc/misc), 174 * and the address of the above file operations structure. 175 */ 176 177static struct miscdevice button_misc_device = { 178 BUTTON_MINOR, 179 "nwbutton", 180 &button_fops, 181}; 182 183/* 184 * This function is called to initialise the driver, either from misc.c at 185 * bootup if the driver is compiled into the kernel, or from init_module 186 * below at module insert time. It attempts to register the device node 187 * and the IRQ and fails with a warning message if either fails, though 188 * neither ever should because the device number and IRQ are unique to 189 * this driver. 190 */ 191 192static int __init nwbutton_init(void) 193{ 194 if (!machine_is_netwinder()) 195 return -ENODEV; 196 197 printk (KERN_INFO "NetWinder Button Driver Version %s (C) Alex Holden " 198 "<alex@linuxhacker.org> 1998.\n", VERSION); 199 200 if (misc_register (&button_misc_device)) { 201 printk (KERN_WARNING "nwbutton: Couldn't register device 10, " 202 "%d.\n", BUTTON_MINOR); 203 return -EBUSY; 204 } 205 206 if (request_irq (IRQ_NETWINDER_BUTTON, button_handler, IRQF_DISABLED, 207 "nwbutton", NULL)) { 208 printk (KERN_WARNING "nwbutton: IRQ %d is not free.\n", 209 IRQ_NETWINDER_BUTTON); 210 misc_deregister (&button_misc_device); 211 return -EIO; 212 } 213 return 0; 214} 215 216static void __exit nwbutton_exit (void) 217{ 218 free_irq (IRQ_NETWINDER_BUTTON, NULL); 219 misc_deregister (&button_misc_device); 220} 221 222 223MODULE_AUTHOR("Alex Holden"); 224MODULE_LICENSE("GPL"); 225 226module_init(nwbutton_init); 227module_exit(nwbutton_exit); 228