initial commit with v2.6.9
[linux-2.6.9-moxart.git] / arch / um / drivers / port_kern.c
blob4044053cd9e369d86ebc16e1905145de5e146d4f
1 /*
2 * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL
4 */
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/semaphore.h"
14 #include "asm/errno.h"
15 #include "kern_util.h"
16 #include "kern.h"
17 #include "irq_user.h"
18 #include "irq_kern.h"
19 #include "port.h"
20 #include "init.h"
21 #include "os.h"
23 struct port_list {
24 struct list_head list;
25 int has_connection;
26 struct semaphore sem;
27 int port;
28 int fd;
29 spinlock_t lock;
30 struct list_head pending;
31 struct list_head connections;
34 struct port_dev {
35 struct port_list *port;
36 int helper_pid;
37 int telnetd_pid;
40 struct connection {
41 struct list_head list;
42 int fd;
43 int helper_pid;
44 int socket[2];
45 int telnetd_pid;
46 struct port_list *port;
49 static irqreturn_t pipe_interrupt(int irq, void *data, struct pt_regs *regs)
51 struct connection *conn = data;
52 int fd;
54 fd = os_rcv_fd(conn->socket[0], &conn->helper_pid);
55 if(fd < 0){
56 if(fd == -EAGAIN)
57 return(IRQ_NONE);
59 printk(KERN_ERR "pipe_interrupt : os_rcv_fd returned %d\n",
60 -fd);
61 os_close_file(conn->fd);
64 list_del(&conn->list);
66 conn->fd = fd;
67 list_add(&conn->list, &conn->port->connections);
69 up(&conn->port->sem);
70 return(IRQ_HANDLED);
73 static int port_accept(struct port_list *port)
75 struct connection *conn;
76 int fd, socket[2], pid, ret = 0;
78 fd = port_connection(port->fd, socket, &pid);
79 if(fd < 0){
80 if(fd != -EAGAIN)
81 printk(KERN_ERR "port_accept : port_connection "
82 "returned %d\n", -fd);
83 goto out;
86 conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
87 if(conn == NULL){
88 printk(KERN_ERR "port_accept : failed to allocate "
89 "connection\n");
90 goto out_close;
92 *conn = ((struct connection)
93 { .list = LIST_HEAD_INIT(conn->list),
94 .fd = fd,
95 .socket = { socket[0], socket[1] },
96 .telnetd_pid = pid,
97 .port = port });
99 if(um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt,
100 SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM,
101 "telnetd", conn)){
102 printk(KERN_ERR "port_accept : failed to get IRQ for "
103 "telnetd\n");
104 goto out_free;
107 list_add(&conn->list, &port->pending);
108 return(1);
110 out_free:
111 kfree(conn);
112 out_close:
113 os_close_file(fd);
114 if(pid != -1)
115 os_kill_process(pid, 1);
116 out:
117 return(ret);
120 DECLARE_MUTEX(ports_sem);
121 struct list_head ports = LIST_HEAD_INIT(ports);
123 void port_work_proc(void *unused)
125 struct port_list *port;
126 struct list_head *ele;
127 unsigned long flags;
129 local_irq_save(flags);
130 list_for_each(ele, &ports){
131 port = list_entry(ele, struct port_list, list);
132 if(!port->has_connection)
133 continue;
134 reactivate_fd(port->fd, ACCEPT_IRQ);
135 while(port_accept(port)) ;
136 port->has_connection = 0;
138 local_irq_restore(flags);
141 DECLARE_WORK(port_work, port_work_proc, NULL);
143 static irqreturn_t port_interrupt(int irq, void *data, struct pt_regs *regs)
145 struct port_list *port = data;
147 port->has_connection = 1;
148 schedule_work(&port_work);
149 return(IRQ_HANDLED);
152 void *port_data(int port_num)
154 struct list_head *ele;
155 struct port_list *port;
156 struct port_dev *dev = NULL;
157 int fd;
159 down(&ports_sem);
160 list_for_each(ele, &ports){
161 port = list_entry(ele, struct port_list, list);
162 if(port->port == port_num) goto found;
164 port = kmalloc(sizeof(struct port_list), GFP_KERNEL);
165 if(port == NULL){
166 printk(KERN_ERR "Allocation of port list failed\n");
167 goto out;
170 fd = port_listen_fd(port_num);
171 if(fd < 0){
172 printk(KERN_ERR "binding to port %d failed, errno = %d\n",
173 port_num, -fd);
174 goto out_free;
176 if(um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt,
177 SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, "port",
178 port)){
179 printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num);
180 goto out_close;
183 *port = ((struct port_list)
184 { .list = LIST_HEAD_INIT(port->list),
185 .has_connection = 0,
186 .sem = __SEMAPHORE_INITIALIZER(port->sem,
188 .lock = SPIN_LOCK_UNLOCKED,
189 .port = port_num,
190 .fd = fd,
191 .pending = LIST_HEAD_INIT(port->pending),
192 .connections = LIST_HEAD_INIT(port->connections) });
193 list_add(&port->list, &ports);
195 found:
196 dev = kmalloc(sizeof(struct port_dev), GFP_KERNEL);
197 if(dev == NULL){
198 printk(KERN_ERR "Allocation of port device entry failed\n");
199 goto out;
202 *dev = ((struct port_dev) { .port = port,
203 .helper_pid = -1,
204 .telnetd_pid = -1 });
205 goto out;
207 out_free:
208 kfree(port);
209 out_close:
210 os_close_file(fd);
211 out:
212 up(&ports_sem);
213 return(dev);
216 int port_wait(void *data)
218 struct port_dev *dev = data;
219 struct connection *conn;
220 struct port_list *port = dev->port;
221 int fd;
223 while(1){
224 if(down_interruptible(&port->sem))
225 return(-ERESTARTSYS);
227 spin_lock(&port->lock);
229 conn = list_entry(port->connections.next, struct connection,
230 list);
231 list_del(&conn->list);
232 spin_unlock(&port->lock);
234 os_shutdown_socket(conn->socket[0], 1, 1);
235 os_close_file(conn->socket[0]);
236 os_shutdown_socket(conn->socket[1], 1, 1);
237 os_close_file(conn->socket[1]);
239 /* This is done here because freeing an IRQ can't be done
240 * within the IRQ handler. So, pipe_interrupt always ups
241 * the semaphore regardless of whether it got a successful
242 * connection. Then we loop here throwing out failed
243 * connections until a good one is found.
245 free_irq(TELNETD_IRQ, conn);
247 if(conn->fd >= 0) break;
248 os_close_file(conn->fd);
249 kfree(conn);
252 fd = conn->fd;
253 dev->helper_pid = conn->helper_pid;
254 dev->telnetd_pid = conn->telnetd_pid;
255 kfree(conn);
257 return(fd);
260 void port_remove_dev(void *d)
262 struct port_dev *dev = d;
264 if(dev->helper_pid != -1)
265 os_kill_process(dev->helper_pid, 0);
266 if(dev->telnetd_pid != -1)
267 os_kill_process(dev->telnetd_pid, 1);
268 dev->helper_pid = -1;
269 dev->telnetd_pid = -1;
272 void port_kern_free(void *d)
274 struct port_dev *dev = d;
276 port_remove_dev(dev);
277 kfree(dev);
280 static void free_port(void)
282 struct list_head *ele;
283 struct port_list *port;
285 list_for_each(ele, &ports){
286 port = list_entry(ele, struct port_list, list);
287 free_irq_by_fd(port->fd);
288 os_close_file(port->fd);
292 __uml_exitcall(free_port);
295 * Overrides for Emacs so that we follow Linus's tabbing style.
296 * Emacs will notice this stuff at the end of the file and automatically
297 * adjust the settings for this buffer only. This must remain at the end
298 * of the file.
299 * ---------------------------------------------------------------------------
300 * Local variables:
301 * c-file-style: "linux"
302 * End: