ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / um / drivers / port_kern.c
1 /* 
2  * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
3  * Licensed under the GPL
4  */
5
6 #include "linux/list.h"
7 #include "linux/sched.h"
8 #include "linux/slab.h"
9 #include "linux/irq.h"
10 #include "linux/spinlock.h"
11 #include "linux/errno.h"
12 #include "asm/semaphore.h"
13 #include "asm/errno.h"
14 #include "kern_util.h"
15 #include "kern.h"
16 #include "irq_user.h"
17 #include "port.h"
18 #include "init.h"
19 #include "os.h"
20
21 struct port_list {
22         struct list_head list;
23         int has_connection;
24         struct semaphore sem;
25         int port;
26         int fd;
27         spinlock_t lock;
28         struct list_head pending;
29         struct list_head connections;
30 };
31
32 struct port_dev {
33         struct port_list *port;
34         int helper_pid;
35         int telnetd_pid;
36 };
37
38 struct connection {
39         struct list_head list;
40         int fd;
41         int helper_pid;
42         int socket[2];
43         int telnetd_pid;
44         struct port_list *port;
45 };
46
47 static void pipe_interrupt(int irq, void *data, struct pt_regs *regs)
48 {
49         struct connection *conn = data;
50         int fd;
51
52         fd = os_rcv_fd(conn->socket[0], &conn->helper_pid);
53         if(fd < 0){
54                 if(fd == -EAGAIN)
55                         return;
56
57                 printk(KERN_ERR "pipe_interrupt : os_rcv_fd returned %d\n", 
58                        -fd);
59                 os_close_file(conn->fd);
60         }
61
62         list_del(&conn->list);
63
64         conn->fd = fd;
65         list_add(&conn->list, &conn->port->connections);
66
67         up(&conn->port->sem);
68 }
69
70 static int port_accept(struct port_list *port)
71 {
72         struct connection *conn;
73         int fd, socket[2], pid, ret = 0;
74
75         fd = port_connection(port->fd, socket, &pid);
76         if(fd < 0){
77                 if(fd != -EAGAIN)
78                         printk(KERN_ERR "port_accept : port_connection "
79                                "returned %d\n", -fd);
80                 goto out;
81         }
82
83         conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
84         if(conn == NULL){
85                 printk(KERN_ERR "port_accept : failed to allocate "
86                        "connection\n");
87                 goto out_close;
88         }
89         *conn = ((struct connection) 
90                 { .list         = LIST_HEAD_INIT(conn->list),
91                   .fd           = fd,
92                   .socket       = { socket[0], socket[1] },
93                   .telnetd_pid  = pid,
94                   .port         = port });
95
96         if(um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt, 
97                           SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, 
98                           "telnetd", conn)){
99                 printk(KERN_ERR "port_accept : failed to get IRQ for "
100                        "telnetd\n");
101                 goto out_free;
102         }
103
104         list_add(&conn->list, &port->pending);
105         ret = 1;
106         goto out;
107
108  out_free:
109         kfree(conn);
110  out_close:
111         os_close_file(fd);
112         if(pid != -1) 
113                 os_kill_process(pid, 1);
114  out:
115         return(ret);
116
117
118 DECLARE_MUTEX(ports_sem);
119 struct list_head ports = LIST_HEAD_INIT(ports);
120
121 void port_work_proc(void *unused)
122 {
123         struct port_list *port;
124         struct list_head *ele;
125         unsigned long flags;
126
127         local_irq_save(flags);
128         list_for_each(ele, &ports){
129                 port = list_entry(ele, struct port_list, list);
130                 if(!port->has_connection)
131                         continue;
132                 reactivate_fd(port->fd, ACCEPT_IRQ);
133                 while(port_accept(port)) ;
134                 port->has_connection = 0;
135         }
136         local_irq_restore(flags);
137 }
138
139 DECLARE_WORK(port_work, port_work_proc, NULL);
140
141 static void port_interrupt(int irq, void *data, struct pt_regs *regs)
142 {
143         struct port_list *port = data;
144
145         port->has_connection = 1;
146         schedule_work(&port_work);
147
148
149 void *port_data(int port_num)
150 {
151         struct list_head *ele;
152         struct port_list *port;
153         struct port_dev *dev = NULL;
154         int fd;
155
156         down(&ports_sem);
157         list_for_each(ele, &ports){
158                 port = list_entry(ele, struct port_list, list);
159                 if(port->port == port_num) goto found;
160         }
161         port = kmalloc(sizeof(struct port_list), GFP_KERNEL);
162         if(port == NULL){
163                 printk(KERN_ERR "Allocation of port list failed\n");
164                 goto out;
165         }
166
167         fd = port_listen_fd(port_num);
168         if(fd < 0){
169                 printk(KERN_ERR "binding to port %d failed, errno = %d\n",
170                        port_num, -fd);
171                 goto out_free;
172         }
173         if(um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt, 
174                           SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, "port",
175                           port)){
176                 printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num);
177                 goto out_close;
178         }
179
180         *port = ((struct port_list) 
181                 { .list                 = LIST_HEAD_INIT(port->list),
182                   .has_connection       = 0,
183                   .sem                  = __SEMAPHORE_INITIALIZER(port->sem, 
184                                                                   0),
185                   .lock                 = SPIN_LOCK_UNLOCKED,
186                   .port                 = port_num,
187                   .fd                   = fd,
188                   .pending              = LIST_HEAD_INIT(port->pending),
189                   .connections          = LIST_HEAD_INIT(port->connections) });
190         list_add(&port->list, &ports);
191
192  found:
193         dev = kmalloc(sizeof(struct port_dev), GFP_KERNEL);
194         if(dev == NULL){
195                 printk(KERN_ERR "Allocation of port device entry failed\n");
196                 goto out;
197         }
198
199         *dev = ((struct port_dev) { .port               = port,
200                                     .helper_pid         = -1,
201                                     .telnetd_pid        = -1 });
202         goto out;
203
204  out_free:
205         kfree(port);
206  out_close:
207         os_close_file(fd);
208  out:
209         up(&ports_sem);
210         return(dev);
211 }
212
213 int port_wait(void *data)
214 {
215         struct port_dev *dev = data;
216         struct connection *conn;
217         struct port_list *port = dev->port;
218         int fd;
219
220         while(1){
221                 if(down_interruptible(&port->sem))
222                         return(-ERESTARTSYS);
223
224                 spin_lock(&port->lock);
225
226                 conn = list_entry(port->connections.next, struct connection, 
227                                   list);
228                 list_del(&conn->list);
229                 spin_unlock(&port->lock);
230
231                 os_shutdown_socket(conn->socket[0], 1, 1);
232                 os_close_file(conn->socket[0]);
233                 os_shutdown_socket(conn->socket[1], 1, 1);
234                 os_close_file(conn->socket[1]); 
235
236                 /* This is done here because freeing an IRQ can't be done
237                  * within the IRQ handler.  So, pipe_interrupt always ups
238                  * the semaphore regardless of whether it got a successful
239                  * connection.  Then we loop here throwing out failed 
240                  * connections until a good one is found.
241                  */
242                 free_irq(TELNETD_IRQ, conn);
243
244                 if(conn->fd >= 0) break;
245                 os_close_file(conn->fd);
246                 kfree(conn);
247         }
248
249         fd = conn->fd;
250         dev->helper_pid = conn->helper_pid;
251         dev->telnetd_pid = conn->telnetd_pid;
252         kfree(conn);
253
254         return(fd);
255 }
256
257 void port_remove_dev(void *d)
258 {
259         struct port_dev *dev = d;
260
261         if(dev->helper_pid != -1)
262                 os_kill_process(dev->helper_pid, 0);
263         if(dev->telnetd_pid != -1)
264                 os_kill_process(dev->telnetd_pid, 1);
265         dev->helper_pid = -1;
266         dev->telnetd_pid = -1;
267 }
268
269 void port_kern_free(void *d)
270 {
271         struct port_dev *dev = d;
272
273         port_remove_dev(dev);
274         kfree(dev);
275 }
276
277 static void free_port(void)
278 {
279         struct list_head *ele;
280         struct port_list *port;
281
282         list_for_each(ele, &ports){
283                 port = list_entry(ele, struct port_list, list);
284                 free_irq_by_fd(port->fd);
285                 os_close_file(port->fd);
286         }
287 }
288
289 __uml_exitcall(free_port);
290
291 /*
292  * Overrides for Emacs so that we follow Linus's tabbing style.
293  * Emacs will notice this stuff at the end of the file and automatically
294  * adjust the settings for this buffer only.  This must remain at the end
295  * of the file.
296  * ---------------------------------------------------------------------------
297  * Local variables:
298  * c-file-style: "linux"
299  * End:
300  */