378ae8616dac85a9b82100c0be6c06e62d95ef02
[linux-flexiantxendom0-3.2.10.git] / drivers / input / serio / serio.c
1 /*
2  * $Id: serio.c,v 1.15 2002/01/22 21:12:03 vojtech Exp $
3  *
4  *  Copyright (c) 1999-2001 Vojtech Pavlik
5  */
6
7 /*
8  *  The Serio abstraction module
9  */
10
11 /*
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or 
15  * (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25  * 
26  * Should you need to contact me, the author, you can do so either by
27  * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
28  * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
29  */
30
31 #include <linux/stddef.h>
32 #include <linux/module.h>
33 #include <linux/serio.h>
34 #include <linux/errno.h>
35 #include <linux/wait.h>
36 #include <linux/completion.h>
37 #include <linux/sched.h>
38 #include <linux/smp_lock.h>
39 #include <linux/suspend.h>
40 #include <linux/slab.h>
41
42 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
43 MODULE_DESCRIPTION("Serio abstraction core");
44 MODULE_LICENSE("GPL");
45
46 EXPORT_SYMBOL(serio_interrupt);
47 EXPORT_SYMBOL(serio_register_port);
48 EXPORT_SYMBOL(serio_unregister_port);
49 EXPORT_SYMBOL(serio_register_device);
50 EXPORT_SYMBOL(serio_unregister_device);
51 EXPORT_SYMBOL(serio_open);
52 EXPORT_SYMBOL(serio_close);
53 EXPORT_SYMBOL(serio_rescan);
54
55 struct serio_event {
56         int type;
57         struct serio *serio;
58         struct list_head node;
59 };
60
61 static DECLARE_MUTEX(serio_sem);
62 static LIST_HEAD(serio_list);
63 static LIST_HEAD(serio_dev_list);
64 static LIST_HEAD(serio_event_list);
65 static int serio_pid;
66
67 static void serio_find_dev(struct serio *serio)
68 {
69         struct serio_dev *dev;
70
71         list_for_each_entry(dev, &serio_dev_list, node) {
72                 if (serio->dev)
73                         break;
74                 if (dev->connect)
75                         dev->connect(serio, dev);
76         }
77 }
78
79 #define SERIO_RESCAN    1
80
81 static DECLARE_WAIT_QUEUE_HEAD(serio_wait);
82 static DECLARE_COMPLETION(serio_exited);
83
84 void serio_handle_events(void)
85 {
86         struct list_head *node, *next;
87         struct serio_event *event;
88
89         list_for_each_safe(node, next, &serio_event_list) {
90                 event = container_of(node, struct serio_event, node);   
91
92                 switch (event->type) {
93                         case SERIO_RESCAN :
94                                 down(&serio_sem);
95                                 if (event->serio->dev && event->serio->dev->disconnect)
96                                         event->serio->dev->disconnect(event->serio);
97                                 serio_find_dev(event->serio);
98                                 up(&serio_sem);
99                                 break;
100                         default:
101                                 break;
102                 }
103                 list_del_init(node);
104                 kfree(event);
105         }
106 }
107
108 static int serio_thread(void *nothing)
109 {
110         lock_kernel();
111         daemonize("kseriod");
112         allow_signal(SIGTERM);
113
114         do {
115                 serio_handle_events();
116                 wait_event_interruptible(serio_wait, !list_empty(&serio_event_list)); 
117                 if (current->flags & PF_FREEZE)
118                         refrigerator(PF_IOTHREAD);
119         } while (!signal_pending(current));
120
121         printk(KERN_DEBUG "serio: kseriod exiting\n");
122
123         unlock_kernel();
124         complete_and_exit(&serio_exited, 0);
125 }
126
127 void serio_rescan(struct serio *serio)
128 {
129         struct serio_event *event;
130
131         if (!(event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC)))
132                 return;
133
134         event->type = SERIO_RESCAN;
135         event->serio = serio;
136
137         list_add_tail(&event->node, &serio_event_list);
138         wake_up(&serio_wait);
139 }
140
141 irqreturn_t serio_interrupt(struct serio *serio,
142                 unsigned char data, unsigned int flags, struct pt_regs *regs)
143 {
144         irqreturn_t ret = IRQ_NONE;
145
146         if (serio->dev && serio->dev->interrupt) {
147                 ret = serio->dev->interrupt(serio, data, flags, regs);
148         } else {
149                 if (!flags) {
150                         serio_rescan(serio);
151                         ret = IRQ_HANDLED;
152                 }
153         }
154         return ret;
155 }
156
157 void serio_register_port(struct serio *serio)
158 {
159         down(&serio_sem);
160         list_add_tail(&serio->node, &serio_list);
161         serio_find_dev(serio);
162         up(&serio_sem);
163 }
164
165 void serio_unregister_port(struct serio *serio)
166 {
167         down(&serio_sem);
168         list_del_init(&serio->node);
169         if (serio->dev && serio->dev->disconnect)
170                 serio->dev->disconnect(serio);
171         up(&serio_sem);
172 }
173
174 void serio_register_device(struct serio_dev *dev)
175 {
176         struct serio *serio;
177         down(&serio_sem);
178         list_add_tail(&dev->node, &serio_dev_list);
179         list_for_each_entry(serio, &serio_list, node)
180                 if (!serio->dev && dev->connect)
181                         dev->connect(serio, dev);
182         up(&serio_sem);
183 }
184
185 void serio_unregister_device(struct serio_dev *dev)
186 {
187         struct serio *serio;
188
189         down(&serio_sem);
190         list_del_init(&dev->node);
191
192         list_for_each_entry(serio, &serio_list, node) {
193                 if (serio->dev == dev && dev->disconnect)
194                         dev->disconnect(serio);
195                 serio_find_dev(serio);
196         }
197         up(&serio_sem);
198 }
199
200 /* called from serio_dev->connect/disconnect methods under serio_sem */
201 int serio_open(struct serio *serio, struct serio_dev *dev)
202 {
203         if (serio->open(serio))
204                 return -1;
205         serio->dev = dev;
206         return 0;
207 }
208
209 /* called from serio_dev->connect/disconnect methods under serio_sem */
210 void serio_close(struct serio *serio)
211 {
212         serio->close(serio);
213         serio->dev = NULL;
214 }
215
216 int serio_init(void)
217 {
218         int pid;
219
220         pid = kernel_thread(serio_thread, NULL,
221                 CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
222
223         if (!pid) {
224                 printk(KERN_WARNING "serio: Failed to start kseriod\n");
225                 return -1;
226         }
227
228         serio_pid = pid;
229
230         return 0;
231 }
232
233 void serio_exit(void)
234 {
235         kill_proc(serio_pid, SIGTERM, 1);
236         wait_for_completion(&serio_exited);
237 }
238
239 module_init(serio_init);
240 module_exit(serio_exit);