diff options
Diffstat (limited to 'arch/um/drivers/slip_user.c')
-rw-r--r-- | arch/um/drivers/slip_user.c | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/arch/um/drivers/slip_user.c b/arch/um/drivers/slip_user.c new file mode 100644 index 000000000..76d155631 --- /dev/null +++ b/arch/um/drivers/slip_user.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) + * Licensed under the GPL. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/termios.h> +#include <sys/wait.h> +#include <net_user.h> +#include <os.h> +#include "slip.h" +#include <um_malloc.h> + +static int slip_user_init(void *data, void *dev) +{ + struct slip_data *pri = data; + + pri->dev = dev; + return 0; +} + +static int set_up_tty(int fd) +{ + int i; + struct termios tios; + + if (tcgetattr(fd, &tios) < 0) { + printk(UM_KERN_ERR "could not get initial terminal " + "attributes\n"); + return -1; + } + + tios.c_cflag = CS8 | CREAD | HUPCL | CLOCAL; + tios.c_iflag = IGNBRK | IGNPAR; + tios.c_oflag = 0; + tios.c_lflag = 0; + for (i = 0; i < NCCS; i++) + tios.c_cc[i] = 0; + tios.c_cc[VMIN] = 1; + tios.c_cc[VTIME] = 0; + + cfsetospeed(&tios, B38400); + cfsetispeed(&tios, B38400); + + if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { + printk(UM_KERN_ERR "failed to set terminal attributes\n"); + return -1; + } + return 0; +} + +struct slip_pre_exec_data { + int stdin_fd; + int stdout_fd; + int close_me; +}; + +static void slip_pre_exec(void *arg) +{ + struct slip_pre_exec_data *data = arg; + + if (data->stdin_fd >= 0) + dup2(data->stdin_fd, 0); + dup2(data->stdout_fd, 1); + if (data->close_me >= 0) + close(data->close_me); +} + +static int slip_tramp(char **argv, int fd) +{ + struct slip_pre_exec_data pe_data; + char *output; + int pid, fds[2], err, output_len; + + err = os_pipe(fds, 1, 0); + if (err < 0) { + printk(UM_KERN_ERR "slip_tramp : pipe failed, err = %d\n", + -err); + goto out; + } + + err = 0; + pe_data.stdin_fd = fd; + pe_data.stdout_fd = fds[1]; + pe_data.close_me = fds[0]; + err = run_helper(slip_pre_exec, &pe_data, argv); + if (err < 0) + goto out_close; + pid = err; + + output_len = UM_KERN_PAGE_SIZE; + output = uml_kmalloc(output_len, UM_GFP_KERNEL); + if (output == NULL) { + printk(UM_KERN_ERR "slip_tramp : failed to allocate output " + "buffer\n"); + os_kill_process(pid, 1); + err = -ENOMEM; + goto out_close; + } + + close(fds[1]); + read_output(fds[0], output, output_len); + printk("%s", output); + + err = helper_wait(pid); + close(fds[0]); + + kfree(output); + return err; + +out_close: + close(fds[0]); + close(fds[1]); +out: + return err; +} + +static int slip_open(void *data) +{ + struct slip_data *pri = data; + char version_buf[sizeof("nnnnn\0")]; + char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")]; + char *argv[] = { "uml_net", version_buf, "slip", "up", gate_buf, + NULL }; + int sfd, mfd, err; + + err = get_pty(); + if (err < 0) { + printk(UM_KERN_ERR "slip-open : Failed to open pty, err = %d\n", + -err); + goto out; + } + mfd = err; + + err = open(ptsname(mfd), O_RDWR, 0); + if (err < 0) { + printk(UM_KERN_ERR "Couldn't open tty for slip line, " + "err = %d\n", -err); + goto out_close; + } + sfd = err; + + err = set_up_tty(sfd); + if (err) + goto out_close2; + + pri->slave = sfd; + pri->slip.pos = 0; + pri->slip.esc = 0; + if (pri->gate_addr != NULL) { + sprintf(version_buf, "%d", UML_NET_VERSION); + strcpy(gate_buf, pri->gate_addr); + + err = slip_tramp(argv, sfd); + + if (err < 0) { + printk(UM_KERN_ERR "slip_tramp failed - err = %d\n", + -err); + goto out_close2; + } + err = os_get_ifname(pri->slave, pri->name); + if (err < 0) { + printk(UM_KERN_ERR "get_ifname failed, err = %d\n", + -err); + goto out_close2; + } + iter_addresses(pri->dev, open_addr, pri->name); + } + else { + err = os_set_slip(sfd); + if (err < 0) { + printk(UM_KERN_ERR "Failed to set slip discipline " + "encapsulation - err = %d\n", -err); + goto out_close2; + } + } + return mfd; +out_close2: + close(sfd); +out_close: + close(mfd); +out: + return err; +} + +static void slip_close(int fd, void *data) +{ + struct slip_data *pri = data; + char version_buf[sizeof("nnnnn\0")]; + char *argv[] = { "uml_net", version_buf, "slip", "down", pri->name, + NULL }; + int err; + + if (pri->gate_addr != NULL) + iter_addresses(pri->dev, close_addr, pri->name); + + sprintf(version_buf, "%d", UML_NET_VERSION); + + err = slip_tramp(argv, pri->slave); + + if (err != 0) + printk(UM_KERN_ERR "slip_tramp failed - errno = %d\n", -err); + close(fd); + close(pri->slave); + pri->slave = -1; +} + +int slip_user_read(int fd, void *buf, int len, struct slip_data *pri) +{ + return slip_proto_read(fd, buf, len, &pri->slip); +} + +int slip_user_write(int fd, void *buf, int len, struct slip_data *pri) +{ + return slip_proto_write(fd, buf, len, &pri->slip); +} + +static void slip_add_addr(unsigned char *addr, unsigned char *netmask, + void *data) +{ + struct slip_data *pri = data; + + if (pri->slave < 0) + return; + open_addr(addr, netmask, pri->name); +} + +static void slip_del_addr(unsigned char *addr, unsigned char *netmask, + void *data) +{ + struct slip_data *pri = data; + + if (pri->slave < 0) + return; + close_addr(addr, netmask, pri->name); +} + +const struct net_user_info slip_user_info = { + .init = slip_user_init, + .open = slip_open, + .close = slip_close, + .remove = NULL, + .add_address = slip_add_addr, + .delete_address = slip_del_addr, + .mtu = BUF_SIZE, + .max_packet = BUF_SIZE, +}; |