diff options
Diffstat (limited to 'arch/um/drivers/slip_user.c')
-rw-r--r-- | arch/um/drivers/slip_user.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/arch/um/drivers/slip_user.c b/arch/um/drivers/slip_user.c new file mode 100644 index 000000000000..d94846b1b4cf --- /dev/null +++ b/arch/um/drivers/slip_user.c | |||
@@ -0,0 +1,280 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <unistd.h> | ||
4 | #include <stddef.h> | ||
5 | #include <sched.h> | ||
6 | #include <string.h> | ||
7 | #include <errno.h> | ||
8 | #include <sys/termios.h> | ||
9 | #include <sys/wait.h> | ||
10 | #include <sys/signal.h> | ||
11 | #include "user_util.h" | ||
12 | #include "kern_util.h" | ||
13 | #include "user.h" | ||
14 | #include "net_user.h" | ||
15 | #include "slip.h" | ||
16 | #include "slip_proto.h" | ||
17 | #include "helper.h" | ||
18 | #include "os.h" | ||
19 | |||
20 | void slip_user_init(void *data, void *dev) | ||
21 | { | ||
22 | struct slip_data *pri = data; | ||
23 | |||
24 | pri->dev = dev; | ||
25 | } | ||
26 | |||
27 | static int set_up_tty(int fd) | ||
28 | { | ||
29 | int i; | ||
30 | struct termios tios; | ||
31 | |||
32 | if (tcgetattr(fd, &tios) < 0) { | ||
33 | printk("could not get initial terminal attributes\n"); | ||
34 | return(-1); | ||
35 | } | ||
36 | |||
37 | tios.c_cflag = CS8 | CREAD | HUPCL | CLOCAL; | ||
38 | tios.c_iflag = IGNBRK | IGNPAR; | ||
39 | tios.c_oflag = 0; | ||
40 | tios.c_lflag = 0; | ||
41 | for (i = 0; i < NCCS; i++) | ||
42 | tios.c_cc[i] = 0; | ||
43 | tios.c_cc[VMIN] = 1; | ||
44 | tios.c_cc[VTIME] = 0; | ||
45 | |||
46 | cfsetospeed(&tios, B38400); | ||
47 | cfsetispeed(&tios, B38400); | ||
48 | |||
49 | if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { | ||
50 | printk("failed to set terminal attributes\n"); | ||
51 | return(-1); | ||
52 | } | ||
53 | return(0); | ||
54 | } | ||
55 | |||
56 | struct slip_pre_exec_data { | ||
57 | int stdin; | ||
58 | int stdout; | ||
59 | int close_me; | ||
60 | }; | ||
61 | |||
62 | static void slip_pre_exec(void *arg) | ||
63 | { | ||
64 | struct slip_pre_exec_data *data = arg; | ||
65 | |||
66 | if(data->stdin >= 0) dup2(data->stdin, 0); | ||
67 | dup2(data->stdout, 1); | ||
68 | if(data->close_me >= 0) os_close_file(data->close_me); | ||
69 | } | ||
70 | |||
71 | static int slip_tramp(char **argv, int fd) | ||
72 | { | ||
73 | struct slip_pre_exec_data pe_data; | ||
74 | char *output; | ||
75 | int status, pid, fds[2], err, output_len; | ||
76 | |||
77 | err = os_pipe(fds, 1, 0); | ||
78 | if(err < 0){ | ||
79 | printk("slip_tramp : pipe failed, err = %d\n", -err); | ||
80 | return(err); | ||
81 | } | ||
82 | |||
83 | err = 0; | ||
84 | pe_data.stdin = fd; | ||
85 | pe_data.stdout = fds[1]; | ||
86 | pe_data.close_me = fds[0]; | ||
87 | pid = run_helper(slip_pre_exec, &pe_data, argv, NULL); | ||
88 | |||
89 | if(pid < 0) err = pid; | ||
90 | else { | ||
91 | output_len = page_size(); | ||
92 | output = um_kmalloc(output_len); | ||
93 | if(output == NULL) | ||
94 | printk("slip_tramp : failed to allocate output " | ||
95 | "buffer\n"); | ||
96 | |||
97 | os_close_file(fds[1]); | ||
98 | read_output(fds[0], output, output_len); | ||
99 | if(output != NULL){ | ||
100 | printk("%s", output); | ||
101 | kfree(output); | ||
102 | } | ||
103 | CATCH_EINTR(err = waitpid(pid, &status, 0)); | ||
104 | if(err < 0) | ||
105 | err = errno; | ||
106 | else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)){ | ||
107 | printk("'%s' didn't exit with status 0\n", argv[0]); | ||
108 | err = -EINVAL; | ||
109 | } | ||
110 | } | ||
111 | |||
112 | os_close_file(fds[0]); | ||
113 | |||
114 | return(err); | ||
115 | } | ||
116 | |||
117 | static int slip_open(void *data) | ||
118 | { | ||
119 | struct slip_data *pri = data; | ||
120 | char version_buf[sizeof("nnnnn\0")]; | ||
121 | char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")]; | ||
122 | char *argv[] = { "uml_net", version_buf, "slip", "up", gate_buf, | ||
123 | NULL }; | ||
124 | int sfd, mfd, err; | ||
125 | |||
126 | mfd = get_pty(); | ||
127 | if(mfd < 0){ | ||
128 | printk("umn : Failed to open pty, err = %d\n", -mfd); | ||
129 | return(mfd); | ||
130 | } | ||
131 | sfd = os_open_file(ptsname(mfd), of_rdwr(OPENFLAGS()), 0); | ||
132 | if(sfd < 0){ | ||
133 | printk("Couldn't open tty for slip line, err = %d\n", -sfd); | ||
134 | os_close_file(mfd); | ||
135 | return(sfd); | ||
136 | } | ||
137 | if(set_up_tty(sfd)) return(-1); | ||
138 | pri->slave = sfd; | ||
139 | pri->pos = 0; | ||
140 | pri->esc = 0; | ||
141 | if(pri->gate_addr != NULL){ | ||
142 | sprintf(version_buf, "%d", UML_NET_VERSION); | ||
143 | strcpy(gate_buf, pri->gate_addr); | ||
144 | |||
145 | err = slip_tramp(argv, sfd); | ||
146 | |||
147 | if(err < 0){ | ||
148 | printk("slip_tramp failed - err = %d\n", -err); | ||
149 | return(err); | ||
150 | } | ||
151 | err = os_get_ifname(pri->slave, pri->name); | ||
152 | if(err < 0){ | ||
153 | printk("get_ifname failed, err = %d\n", -err); | ||
154 | return(err); | ||
155 | } | ||
156 | iter_addresses(pri->dev, open_addr, pri->name); | ||
157 | } | ||
158 | else { | ||
159 | err = os_set_slip(sfd); | ||
160 | if(err < 0){ | ||
161 | printk("Failed to set slip discipline encapsulation - " | ||
162 | "err = %d\n", -err); | ||
163 | return(err); | ||
164 | } | ||
165 | } | ||
166 | return(mfd); | ||
167 | } | ||
168 | |||
169 | static void slip_close(int fd, void *data) | ||
170 | { | ||
171 | struct slip_data *pri = data; | ||
172 | char version_buf[sizeof("nnnnn\0")]; | ||
173 | char *argv[] = { "uml_net", version_buf, "slip", "down", pri->name, | ||
174 | NULL }; | ||
175 | int err; | ||
176 | |||
177 | if(pri->gate_addr != NULL) | ||
178 | iter_addresses(pri->dev, close_addr, pri->name); | ||
179 | |||
180 | sprintf(version_buf, "%d", UML_NET_VERSION); | ||
181 | |||
182 | err = slip_tramp(argv, pri->slave); | ||
183 | |||
184 | if(err != 0) | ||
185 | printk("slip_tramp failed - errno = %d\n", -err); | ||
186 | os_close_file(fd); | ||
187 | os_close_file(pri->slave); | ||
188 | pri->slave = -1; | ||
189 | } | ||
190 | |||
191 | int slip_user_read(int fd, void *buf, int len, struct slip_data *pri) | ||
192 | { | ||
193 | int i, n, size, start; | ||
194 | |||
195 | if(pri->more>0) { | ||
196 | i = 0; | ||
197 | while(i < pri->more) { | ||
198 | size = slip_unesc(pri->ibuf[i++], | ||
199 | pri->ibuf, &pri->pos, &pri->esc); | ||
200 | if(size){ | ||
201 | memcpy(buf, pri->ibuf, size); | ||
202 | memmove(pri->ibuf, &pri->ibuf[i], pri->more-i); | ||
203 | pri->more=pri->more-i; | ||
204 | return(size); | ||
205 | } | ||
206 | } | ||
207 | pri->more=0; | ||
208 | } | ||
209 | |||
210 | n = net_read(fd, &pri->ibuf[pri->pos], sizeof(pri->ibuf) - pri->pos); | ||
211 | if(n <= 0) return(n); | ||
212 | |||
213 | start = pri->pos; | ||
214 | for(i = 0; i < n; i++){ | ||
215 | size = slip_unesc(pri->ibuf[start + i], | ||
216 | pri->ibuf, &pri->pos, &pri->esc); | ||
217 | if(size){ | ||
218 | memcpy(buf, pri->ibuf, size); | ||
219 | memmove(pri->ibuf, &pri->ibuf[start+i+1], n-(i+1)); | ||
220 | pri->more=n-(i+1); | ||
221 | return(size); | ||
222 | } | ||
223 | } | ||
224 | return(0); | ||
225 | } | ||
226 | |||
227 | int slip_user_write(int fd, void *buf, int len, struct slip_data *pri) | ||
228 | { | ||
229 | int actual, n; | ||
230 | |||
231 | actual = slip_esc(buf, pri->obuf, len); | ||
232 | n = net_write(fd, pri->obuf, actual); | ||
233 | if(n < 0) return(n); | ||
234 | else return(len); | ||
235 | } | ||
236 | |||
237 | static int slip_set_mtu(int mtu, void *data) | ||
238 | { | ||
239 | return(mtu); | ||
240 | } | ||
241 | |||
242 | static void slip_add_addr(unsigned char *addr, unsigned char *netmask, | ||
243 | void *data) | ||
244 | { | ||
245 | struct slip_data *pri = data; | ||
246 | |||
247 | if(pri->slave < 0) return; | ||
248 | open_addr(addr, netmask, pri->name); | ||
249 | } | ||
250 | |||
251 | static void slip_del_addr(unsigned char *addr, unsigned char *netmask, | ||
252 | void *data) | ||
253 | { | ||
254 | struct slip_data *pri = data; | ||
255 | |||
256 | if(pri->slave < 0) return; | ||
257 | close_addr(addr, netmask, pri->name); | ||
258 | } | ||
259 | |||
260 | struct net_user_info slip_user_info = { | ||
261 | .init = slip_user_init, | ||
262 | .open = slip_open, | ||
263 | .close = slip_close, | ||
264 | .remove = NULL, | ||
265 | .set_mtu = slip_set_mtu, | ||
266 | .add_address = slip_add_addr, | ||
267 | .delete_address = slip_del_addr, | ||
268 | .max_packet = BUF_SIZE | ||
269 | }; | ||
270 | |||
271 | /* | ||
272 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
273 | * Emacs will notice this stuff at the end of the file and automatically | ||
274 | * adjust the settings for this buffer only. This must remain at the end | ||
275 | * of the file. | ||
276 | * --------------------------------------------------------------------------- | ||
277 | * Local variables: | ||
278 | * c-file-style: "linux" | ||
279 | * End: | ||
280 | */ | ||