[PATCH] uml: move libc-dependent irq code to os-Linux
[pandora-kernel.git] / arch / um / kernel / irq_user.c
1 /*
2  * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
3  * Licensed under the GPL
4  */
5
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <errno.h>
9 #include <signal.h>
10 #include <string.h>
11 #include <sys/poll.h>
12 #include <sys/types.h>
13 #include <sys/time.h>
14 #include "user_util.h"
15 #include "kern_util.h"
16 #include "user.h"
17 #include "process.h"
18 #include "sigio.h"
19 #include "irq_user.h"
20 #include "os.h"
21 #include "misc_constants.h"
22
23 struct irq_fd *active_fds = NULL;
24 static struct irq_fd **last_irq_ptr = &active_fds;
25
26 extern void free_irqs(void);
27
28 void sigio_handler(int sig, union uml_pt_regs *regs)
29 {
30         struct irq_fd *irq_fd;
31         int n;
32
33         if(smp_sigio_handler()) return;
34         while(1){
35                 n = os_waiting_for_events(active_fds);
36                 if (n <= 0) {
37                         if(n == -EINTR) continue;
38                         else break;
39                 }
40
41                 for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){
42                         if(irq_fd->current_events != 0){
43                                 irq_fd->current_events = 0;
44                                 do_IRQ(irq_fd->irq, regs);
45                         }
46                 }
47         }
48
49         free_irqs();
50 }
51
52 static void maybe_sigio_broken(int fd, int type)
53 {
54         if(os_isatty(fd)){
55                 if((type == IRQ_WRITE) && !pty_output_sigio){
56                         write_sigio_workaround();
57                         add_sigio_fd(fd, 0);
58                 }
59                 else if((type == IRQ_READ) && !pty_close_sigio){
60                         write_sigio_workaround();
61                         add_sigio_fd(fd, 1);                    
62                 }
63         }
64 }
65
66
67 int activate_fd(int irq, int fd, int type, void *dev_id)
68 {
69         struct pollfd *tmp_pfd;
70         struct irq_fd *new_fd, *irq_fd;
71         unsigned long flags;
72         int pid, events, err, n;
73
74         pid = os_getpid();
75         err = os_set_fd_async(fd, pid);
76         if(err < 0)
77                 goto out;
78
79         new_fd = um_kmalloc(sizeof(*new_fd));
80         err = -ENOMEM;
81         if(new_fd == NULL)
82                 goto out;
83
84         if(type == IRQ_READ) events = UM_POLLIN | UM_POLLPRI;
85         else events = UM_POLLOUT;
86         *new_fd = ((struct irq_fd) { .next              = NULL,
87                                      .id                = dev_id,
88                                      .fd                = fd,
89                                      .type              = type,
90                                      .irq               = irq,
91                                      .pid               = pid,
92                                      .events            = events,
93                                      .current_events    = 0 } );
94
95         /* Critical section - locked by a spinlock because this stuff can
96          * be changed from interrupt handlers.  The stuff above is done
97          * outside the lock because it allocates memory.
98          */
99
100         /* Actually, it only looks like it can be called from interrupt
101          * context.  The culprit is reactivate_fd, which calls
102          * maybe_sigio_broken, which calls write_sigio_workaround,
103          * which calls activate_fd.  However, write_sigio_workaround should
104          * only be called once, at boot time.  That would make it clear that
105          * this is called only from process context, and can be locked with
106          * a semaphore.
107          */
108         flags = irq_lock();
109         for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){
110                 if((irq_fd->fd == fd) && (irq_fd->type == type)){
111                         printk("Registering fd %d twice\n", fd);
112                         printk("Irqs : %d, %d\n", irq_fd->irq, irq);
113                         printk("Ids : 0x%x, 0x%x\n", irq_fd->id, dev_id);
114                         goto out_unlock;
115                 }
116         }
117
118         /*-------------*/
119         if(type == IRQ_WRITE)
120                 fd = -1;
121
122         tmp_pfd = NULL;
123         n = 0;
124
125         while(1){
126                 n = os_create_pollfd(fd, events, tmp_pfd, n);
127                 if (n == 0)
128                         break;
129
130                 /* n > 0
131                  * It means we couldn't put new pollfd to current pollfds
132                  * and tmp_fds is NULL or too small for new pollfds array.
133                  * Needed size is equal to n as minimum.
134                  *
135                  * Here we have to drop the lock in order to call
136                  * kmalloc, which might sleep.
137                  * If something else came in and changed the pollfds array
138                  * so we will not be able to put new pollfd struct to pollfds
139                  * then we free the buffer tmp_fds and try again.
140                  */
141                 irq_unlock(flags);
142                 if (tmp_pfd != NULL) {
143                         kfree(tmp_pfd);
144                         tmp_pfd = NULL;
145                 }
146
147                 tmp_pfd = um_kmalloc(n);
148                 if (tmp_pfd == NULL)
149                         goto out_kfree;
150
151                 flags = irq_lock();
152         }
153         /*-------------*/
154
155         *last_irq_ptr = new_fd;
156         last_irq_ptr = &new_fd->next;
157
158         irq_unlock(flags);
159
160         /* This calls activate_fd, so it has to be outside the critical
161          * section.
162          */
163         maybe_sigio_broken(fd, type);
164
165         return(0);
166
167  out_unlock:
168         irq_unlock(flags);
169  out_kfree:
170         kfree(new_fd);
171  out:
172         return(err);
173 }
174
175 static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg)
176 {
177         unsigned long flags;
178
179         flags = irq_lock();
180         os_free_irq_by_cb(test, arg, active_fds, &last_irq_ptr);
181         irq_unlock(flags);
182 }
183
184 struct irq_and_dev {
185         int irq;
186         void *dev;
187 };
188
189 static int same_irq_and_dev(struct irq_fd *irq, void *d)
190 {
191         struct irq_and_dev *data = d;
192
193         return((irq->irq == data->irq) && (irq->id == data->dev));
194 }
195
196 void free_irq_by_irq_and_dev(unsigned int irq, void *dev)
197 {
198         struct irq_and_dev data = ((struct irq_and_dev) { .irq  = irq,
199                                                           .dev  = dev });
200
201         free_irq_by_cb(same_irq_and_dev, &data);
202 }
203
204 static int same_fd(struct irq_fd *irq, void *fd)
205 {
206         return(irq->fd == *((int *) fd));
207 }
208
209 void free_irq_by_fd(int fd)
210 {
211         free_irq_by_cb(same_fd, &fd);
212 }
213
214 static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out)
215 {
216         struct irq_fd *irq;
217         int i = 0;
218         int fdi;
219
220         for(irq=active_fds; irq != NULL; irq = irq->next){
221                 if((irq->fd == fd) && (irq->irq == irqnum)) break;
222                 i++;
223         }
224         if(irq == NULL){
225                 printk("find_irq_by_fd doesn't have descriptor %d\n", fd);
226                 goto out;
227         }
228         fdi = os_get_pollfd(i);
229         if((fdi != -1) && (fdi != fd)){
230                 printk("find_irq_by_fd - mismatch between active_fds and "
231                        "pollfds, fd %d vs %d, need %d\n", irq->fd,
232                        fdi, fd);
233                 irq = NULL;
234                 goto out;
235         }
236         *index_out = i;
237  out:
238         return(irq);
239 }
240
241 void reactivate_fd(int fd, int irqnum)
242 {
243         struct irq_fd *irq;
244         unsigned long flags;
245         int i;
246
247         flags = irq_lock();
248         irq = find_irq_by_fd(fd, irqnum, &i);
249         if(irq == NULL){
250                 irq_unlock(flags);
251                 return;
252         }
253         os_set_pollfd(i, irq->fd);
254         irq_unlock(flags);
255
256         /* This calls activate_fd, so it has to be outside the critical
257          * section.
258          */
259         maybe_sigio_broken(fd, irq->type);
260 }
261
262 void deactivate_fd(int fd, int irqnum)
263 {
264         struct irq_fd *irq;
265         unsigned long flags;
266         int i;
267
268         flags = irq_lock();
269         irq = find_irq_by_fd(fd, irqnum, &i);
270         if(irq == NULL)
271                 goto out;
272         os_set_pollfd(i, -1);
273  out:
274         irq_unlock(flags);
275 }
276
277 int deactivate_all_fds(void)
278 {
279         struct irq_fd *irq;
280         int err;
281
282         for(irq=active_fds;irq != NULL;irq = irq->next){
283                 err = os_clear_fd_async(irq->fd);
284                 if(err)
285                         return(err);
286         }
287         /* If there is a signal already queued, after unblocking ignore it */
288         os_set_ioignore();
289
290         return(0);
291 }
292
293 void forward_interrupts(int pid)
294 {
295         struct irq_fd *irq;
296         unsigned long flags;
297         int err;
298
299         flags = irq_lock();
300         for(irq=active_fds;irq != NULL;irq = irq->next){
301                 err = os_set_owner(irq->fd, pid);
302                 if(err < 0){
303                         /* XXX Just remove the irq rather than
304                          * print out an infinite stream of these
305                          */
306                         printk("Failed to forward %d to pid %d, err = %d\n",
307                                irq->fd, pid, -err);
308                 }
309
310                 irq->pid = pid;
311         }
312         irq_unlock(flags);
313 }
314
315 /*
316  * Overrides for Emacs so that we follow Linus's tabbing style.
317  * Emacs will notice this stuff at the end of the file and automatically
318  * adjust the settings for this buffer only.  This must remain at the end
319  * of the file.
320  * ---------------------------------------------------------------------------
321  * Local variables:
322  * c-file-style: "linux"
323  * End:
324  */