[PATCH] uml: add and use generic hw_controller_type->release
[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/interrupt.h"
10 #include "linux/irq.h"
11 #include "linux/spinlock.h"
12 #include "linux/errno.h"
13 #include "asm/atomic.h"
14 #include "asm/semaphore.h"
15 #include "asm/errno.h"
16 #include "kern_util.h"
17 #include "kern.h"
18 #include "irq_user.h"
19 #include "irq_kern.h"
20 #include "port.h"
21 #include "init.h"
22 #include "os.h"
23
24 struct port_list {
25         struct list_head list;
26         atomic_t wait_count;
27         int has_connection;
28         struct completion done;
29         int port;
30         int fd;
31         spinlock_t lock;
32         struct list_head pending;
33         struct list_head connections;
34 };
35
36 struct port_dev {
37         struct port_list *port;
38         int helper_pid;
39         int telnetd_pid;
40 };
41
42 struct connection {
43         struct list_head list;
44         int fd;
45         int helper_pid;
46         int socket[2];
47         int telnetd_pid;
48         struct port_list *port;
49 };
50
51 static irqreturn_t pipe_interrupt(int irq, void *data, struct pt_regs *regs)
52 {
53         struct connection *conn = data;
54         int fd;
55
56         fd = os_rcv_fd(conn->socket[0], &conn->helper_pid);
57         if(fd < 0){
58                 if(fd == -EAGAIN)
59                         return(IRQ_NONE);
60
61                 printk(KERN_ERR "pipe_interrupt : os_rcv_fd returned %d\n", 
62                        -fd);
63                 os_close_file(conn->fd);
64         }
65
66         list_del(&conn->list);
67
68         conn->fd = fd;
69         list_add(&conn->list, &conn->port->connections);
70
71         complete(&conn->port->done);
72         return(IRQ_HANDLED);
73 }
74
75 #define NO_WAITER_MSG \
76     "****\n" \
77     "There are currently no UML consoles waiting for port connections.\n" \
78     "Either disconnect from one to make it available or activate some more\n" \
79     "by enabling more consoles in the UML /etc/inittab.\n" \
80     "****\n"
81
82 static int port_accept(struct port_list *port)
83 {
84         struct connection *conn;
85         int fd, socket[2], pid, ret = 0;
86
87         fd = port_connection(port->fd, socket, &pid);
88         if(fd < 0){
89                 if(fd != -EAGAIN)
90                         printk(KERN_ERR "port_accept : port_connection "
91                                "returned %d\n", -fd);
92                 goto out;
93         }
94
95         conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
96         if(conn == NULL){
97                 printk(KERN_ERR "port_accept : failed to allocate "
98                        "connection\n");
99                 goto out_close;
100         }
101         *conn = ((struct connection) 
102                 { .list         = LIST_HEAD_INIT(conn->list),
103                   .fd           = fd,
104                   .socket       = { socket[0], socket[1] },
105                   .telnetd_pid  = pid,
106                   .port         = port });
107
108         if(um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt, 
109                           SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, 
110                           "telnetd", conn)){
111                 printk(KERN_ERR "port_accept : failed to get IRQ for "
112                        "telnetd\n");
113                 goto out_free;
114         }
115
116         if(atomic_read(&port->wait_count) == 0){
117                 os_write_file(fd, NO_WAITER_MSG, sizeof(NO_WAITER_MSG));
118                 printk("No one waiting for port\n");
119         }
120         list_add(&conn->list, &port->pending);
121         return(1);
122
123  out_free:
124         kfree(conn);
125  out_close:
126         os_close_file(fd);
127         if(pid != -1) 
128                 os_kill_process(pid, 1);
129  out:
130         return(ret);
131
132
133 DECLARE_MUTEX(ports_sem);
134 struct list_head ports = LIST_HEAD_INIT(ports);
135
136 void port_work_proc(void *unused)
137 {
138         struct port_list *port;
139         struct list_head *ele;
140         unsigned long flags;
141
142         local_irq_save(flags);
143         list_for_each(ele, &ports){
144                 port = list_entry(ele, struct port_list, list);
145                 if(!port->has_connection)
146                         continue;
147                 reactivate_fd(port->fd, ACCEPT_IRQ);
148                 while(port_accept(port)) ;
149                 port->has_connection = 0;
150         }
151         local_irq_restore(flags);
152 }
153
154 DECLARE_WORK(port_work, port_work_proc, NULL);
155
156 static irqreturn_t port_interrupt(int irq, void *data, struct pt_regs *regs)
157 {
158         struct port_list *port = data;
159
160         port->has_connection = 1;
161         schedule_work(&port_work);
162         return(IRQ_HANDLED);
163
164
165 void *port_data(int port_num)
166 {
167         struct list_head *ele;
168         struct port_list *port;
169         struct port_dev *dev = NULL;
170         int fd;
171
172         down(&ports_sem);
173         list_for_each(ele, &ports){
174                 port = list_entry(ele, struct port_list, list);
175                 if(port->port == port_num) goto found;
176         }
177         port = kmalloc(sizeof(struct port_list), GFP_KERNEL);
178         if(port == NULL){
179                 printk(KERN_ERR "Allocation of port list failed\n");
180                 goto out;
181         }
182
183         fd = port_listen_fd(port_num);
184         if(fd < 0){
185                 printk(KERN_ERR "binding to port %d failed, errno = %d\n",
186                        port_num, -fd);
187                 goto out_free;
188         }
189         if(um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt, 
190                           SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, "port",
191                           port)){
192                 printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num);
193                 goto out_close;
194         }
195
196         *port = ((struct port_list) 
197                 { .list                 = LIST_HEAD_INIT(port->list),
198                   .wait_count           = ATOMIC_INIT(0),
199                   .has_connection       = 0,
200                   .port                 = port_num,
201                   .fd                   = fd,
202                   .pending              = LIST_HEAD_INIT(port->pending),
203                   .connections          = LIST_HEAD_INIT(port->connections) });
204         spin_lock_init(&port->lock);
205         init_completion(&port->done);
206         list_add(&port->list, &ports);
207
208  found:
209         dev = kmalloc(sizeof(struct port_dev), GFP_KERNEL);
210         if(dev == NULL){
211                 printk(KERN_ERR "Allocation of port device entry failed\n");
212                 goto out;
213         }
214
215         *dev = ((struct port_dev) { .port               = port,
216                                     .helper_pid         = -1,
217                                     .telnetd_pid        = -1 });
218         goto out;
219
220  out_free:
221         kfree(port);
222  out_close:
223         os_close_file(fd);
224  out:
225         up(&ports_sem);
226         return(dev);
227 }
228
229 int port_wait(void *data)
230 {
231         struct port_dev *dev = data;
232         struct connection *conn;
233         struct port_list *port = dev->port;
234         int fd;
235
236         atomic_inc(&port->wait_count);
237         while(1){
238                 fd = -ERESTARTSYS;
239                 if(wait_for_completion_interruptible(&port->done))
240                         goto out;
241
242                 spin_lock(&port->lock);
243
244                 conn = list_entry(port->connections.next, struct connection, 
245                                   list);
246                 list_del(&conn->list);
247                 spin_unlock(&port->lock);
248
249                 os_shutdown_socket(conn->socket[0], 1, 1);
250                 os_close_file(conn->socket[0]);
251                 os_shutdown_socket(conn->socket[1], 1, 1);
252                 os_close_file(conn->socket[1]); 
253
254                 /* This is done here because freeing an IRQ can't be done
255                  * within the IRQ handler.  So, pipe_interrupt always ups
256                  * the semaphore regardless of whether it got a successful
257                  * connection.  Then we loop here throwing out failed 
258                  * connections until a good one is found.
259                  */
260                 free_irq(TELNETD_IRQ, conn);
261
262                 if(conn->fd >= 0) break;
263                 os_close_file(conn->fd);
264                 kfree(conn);
265         }
266
267         fd = conn->fd;
268         dev->helper_pid = conn->helper_pid;
269         dev->telnetd_pid = conn->telnetd_pid;
270         kfree(conn);
271  out:
272         atomic_dec(&port->wait_count);
273         return fd;
274 }
275
276 void port_remove_dev(void *d)
277 {
278         struct port_dev *dev = d;
279
280         if(dev->helper_pid != -1)
281                 os_kill_process(dev->helper_pid, 0);
282         if(dev->telnetd_pid != -1)
283                 os_kill_process(dev->telnetd_pid, 1);
284         dev->helper_pid = -1;
285         dev->telnetd_pid = -1;
286 }
287
288 void port_kern_free(void *d)
289 {
290         struct port_dev *dev = d;
291
292         port_remove_dev(dev);
293         kfree(dev);
294 }
295
296 static void free_port(void)
297 {
298         struct list_head *ele;
299         struct port_list *port;
300
301         list_for_each(ele, &ports){
302                 port = list_entry(ele, struct port_list, list);
303                 free_irq_by_fd(port->fd);
304                 os_close_file(port->fd);
305         }
306 }
307
308 __uml_exitcall(free_port);