summaryrefslogtreecommitdiffstats
path: root/arch/um/drivers/slip_user.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/um/drivers/slip_user.c')
-rw-r--r--arch/um/drivers/slip_user.c252
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,
+};