• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/char/
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