blob: 483e79c5ab98d9ddec8644c3f651756c528df86c [file] [log] [blame]
Jeff Dike63ae2a92006-03-27 01:14:30 -08001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * 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"
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include "sigio.h"
19#include "irq_user.h"
20#include "os.h"
Jeff Dike63ae2a92006-03-27 01:14:30 -080021#include "misc_constants.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
Jeff Dike63ae2a92006-03-27 01:14:30 -080023struct irq_fd *active_fds = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070024static struct irq_fd **last_irq_ptr = &active_fds;
25
Jeff Dike165dc592006-01-06 00:18:57 -080026extern void free_irqs(void);
27
Linus Torvalds1da177e2005-04-16 15:20:36 -070028void sigio_handler(int sig, union uml_pt_regs *regs)
29{
Jeff Dike165dc592006-01-06 00:18:57 -080030 struct irq_fd *irq_fd;
Jeff Dike63ae2a92006-03-27 01:14:30 -080031 int n;
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
33 if(smp_sigio_handler()) return;
34 while(1){
Jeff Dike63ae2a92006-03-27 01:14:30 -080035 n = os_waiting_for_events(active_fds);
36 if (n <= 0) {
37 if(n == -EINTR) continue;
38 else break;
39 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
Jeff Dike165dc592006-01-06 00:18:57 -080041 for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 if(irq_fd->current_events != 0){
43 irq_fd->current_events = 0;
44 do_IRQ(irq_fd->irq, regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -070045 }
46 }
47 }
Jeff Dike165dc592006-01-06 00:18:57 -080048
49 free_irqs();
Linus Torvalds1da177e2005-04-16 15:20:36 -070050}
51
Linus Torvalds1da177e2005-04-16 15:20:36 -070052static void maybe_sigio_broken(int fd, int type)
53{
Jeff Dike63ae2a92006-03-27 01:14:30 -080054 if(os_isatty(fd)){
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 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
Jeff Dike63ae2a92006-03-27 01:14:30 -080066
Linus Torvalds1da177e2005-04-16 15:20:36 -070067int 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;
Jeff Dike63ae2a92006-03-27 01:14:30 -080072 int pid, events, err, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
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
Jeff Dike63ae2a92006-03-27 01:14:30 -080084 if(type == IRQ_READ) events = UM_POLLIN | UM_POLLPRI;
85 else events = UM_POLLOUT;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 *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,
Jeff Dike165dc592006-01-06 00:18:57 -080093 .current_events = 0 } );
Linus Torvalds1da177e2005-04-16 15:20:36 -070094
95 /* Critical section - locked by a spinlock because this stuff can
Jeff Dike63ae2a92006-03-27 01:14:30 -080096 * be changed from interrupt handlers. The stuff above is done
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 * outside the lock because it allocates memory.
98 */
99
100 /* Actually, it only looks like it can be called from interrupt
Jeff Dike63ae2a92006-03-27 01:14:30 -0800101 * context. The culprit is reactivate_fd, which calls
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 * 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
Jeff Dike63ae2a92006-03-27 01:14:30 -0800118 /*-------------*/
119 if(type == IRQ_WRITE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 fd = -1;
121
Jeff Dike63ae2a92006-03-27 01:14:30 -0800122 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 /*-------------*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
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);
Jeff Dike63ae2a92006-03-27 01:14:30 -0800169 out_kfree:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 kfree(new_fd);
171 out:
172 return(err);
173}
174
175static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg)
176{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178
179 flags = irq_lock();
Jeff Dike63ae2a92006-03-27 01:14:30 -0800180 os_free_irq_by_cb(test, arg, active_fds, &last_irq_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 irq_unlock(flags);
182}
183
184struct irq_and_dev {
185 int irq;
186 void *dev;
187};
188
189static 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
196void 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
204static int same_fd(struct irq_fd *irq, void *fd)
205{
206 return(irq->fd == *((int *) fd));
207}
208
209void free_irq_by_fd(int fd)
210{
211 free_irq_by_cb(same_fd, &fd);
212}
213
214static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out)
215{
216 struct irq_fd *irq;
217 int i = 0;
Jeff Dike63ae2a92006-03-27 01:14:30 -0800218 int fdi;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
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 }
Jeff Dike63ae2a92006-03-27 01:14:30 -0800228 fdi = os_get_pollfd(i);
229 if((fdi != -1) && (fdi != fd)){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 printk("find_irq_by_fd - mismatch between active_fds and "
Jeff Dike63ae2a92006-03-27 01:14:30 -0800231 "pollfds, fd %d vs %d, need %d\n", irq->fd,
232 fdi, fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 irq = NULL;
234 goto out;
235 }
236 *index_out = i;
237 out:
238 return(irq);
239}
240
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241void 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 }
Jeff Dike63ae2a92006-03-27 01:14:30 -0800253 os_set_pollfd(i, irq->fd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 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
262void 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;
Jeff Dike63ae2a92006-03-27 01:14:30 -0800272 os_set_pollfd(i, -1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 out:
274 irq_unlock(flags);
275}
276
277int 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 */
Jeff Dike63ae2a92006-03-27 01:14:30 -0800288 os_set_ioignore();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
290 return(0);
291}
292
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293void 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315/*
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 */