PageRenderTime 33ms CodeModel.GetById 17ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/arch/um/drivers/port_kern.c

https://bitbucket.org/evzijst/gittest
C | 309 lines | 254 code | 45 blank | 10 comment | 28 complexity | da31ec9305b7479daf34c0e027862091 MD5 | raw file
  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
 24struct 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
 36struct port_dev {
 37	struct port_list *port;
 38	int helper_pid;
 39	int telnetd_pid;
 40};
 41
 42struct 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
 51static 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
 82static 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
133DECLARE_MUTEX(ports_sem);
134struct list_head ports = LIST_HEAD_INIT(ports);
135
136void 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
154DECLARE_WORK(port_work, port_work_proc, NULL);
155
156static 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
165void *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
229int 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_by_irq_and_dev(TELNETD_IRQ, conn);
261		free_irq(TELNETD_IRQ, conn);
262
263		if(conn->fd >= 0) break;
264		os_close_file(conn->fd);
265		kfree(conn);
266	}
267
268	fd = conn->fd;
269	dev->helper_pid = conn->helper_pid;
270	dev->telnetd_pid = conn->telnetd_pid;
271	kfree(conn);
272 out:
273	atomic_dec(&port->wait_count);
274	return fd;
275}
276
277void port_remove_dev(void *d)
278{
279	struct port_dev *dev = d;
280
281	if(dev->helper_pid != -1)
282		os_kill_process(dev->helper_pid, 0);
283	if(dev->telnetd_pid != -1)
284		os_kill_process(dev->telnetd_pid, 1);
285	dev->helper_pid = -1;
286	dev->telnetd_pid = -1;
287}
288
289void port_kern_free(void *d)
290{
291	struct port_dev *dev = d;
292
293	port_remove_dev(dev);
294	kfree(dev);
295}
296
297static void free_port(void)
298{
299	struct list_head *ele;
300	struct port_list *port;
301
302	list_for_each(ele, &ports){
303		port = list_entry(ele, struct port_list, list);
304		free_irq_by_fd(port->fd);
305		os_close_file(port->fd);
306	}
307}
308
309__uml_exitcall(free_port);