diff options
Diffstat (limited to 'arch/um/drivers')
49 files changed, 10430 insertions, 0 deletions
diff --git a/arch/um/drivers/Makefile b/arch/um/drivers/Makefile new file mode 100644 index 000000000000..323f72c64cd2 --- /dev/null +++ b/arch/um/drivers/Makefile | |||
@@ -0,0 +1,46 @@ | |||
1 | # | ||
2 | # Copyright (C) 2000, 2002, 2003 Jeff Dike (jdike@karaya.com) | ||
3 | # Licensed under the GPL | ||
4 | # | ||
5 | |||
6 | # pcap is broken in 2.5 because kbuild doesn't allow pcap.a to be linked | ||
7 | # in to pcap.o | ||
8 | |||
9 | slip-objs := slip_kern.o slip_user.o | ||
10 | slirp-objs := slirp_kern.o slirp_user.o | ||
11 | daemon-objs := daemon_kern.o daemon_user.o | ||
12 | mcast-objs := mcast_kern.o mcast_user.o | ||
13 | #pcap-objs := pcap_kern.o pcap_user.o $(PCAP) | ||
14 | net-objs := net_kern.o net_user.o | ||
15 | mconsole-objs := mconsole_kern.o mconsole_user.o | ||
16 | hostaudio-objs := hostaudio_kern.o | ||
17 | ubd-objs := ubd_kern.o ubd_user.o | ||
18 | port-objs := port_kern.o port_user.o | ||
19 | harddog-objs := harddog_kern.o harddog_user.o | ||
20 | |||
21 | obj-y := stdio_console.o fd.o chan_kern.o chan_user.o line.o | ||
22 | obj-$(CONFIG_SSL) += ssl.o | ||
23 | obj-$(CONFIG_STDERR_CONSOLE) += stderr_console.o | ||
24 | |||
25 | obj-$(CONFIG_UML_NET_SLIP) += slip.o | ||
26 | obj-$(CONFIG_UML_NET_SLIRP) += slirp.o | ||
27 | obj-$(CONFIG_UML_NET_DAEMON) += daemon.o | ||
28 | obj-$(CONFIG_UML_NET_MCAST) += mcast.o | ||
29 | #obj-$(CONFIG_UML_NET_PCAP) += pcap.o $(PCAP) | ||
30 | obj-$(CONFIG_UML_NET) += net.o | ||
31 | obj-$(CONFIG_MCONSOLE) += mconsole.o | ||
32 | obj-$(CONFIG_MMAPPER) += mmapper_kern.o | ||
33 | obj-$(CONFIG_BLK_DEV_UBD) += ubd.o | ||
34 | obj-$(CONFIG_HOSTAUDIO) += hostaudio.o | ||
35 | obj-$(CONFIG_NULL_CHAN) += null.o | ||
36 | obj-$(CONFIG_PORT_CHAN) += port.o | ||
37 | obj-$(CONFIG_PTY_CHAN) += pty.o | ||
38 | obj-$(CONFIG_TTY_CHAN) += tty.o | ||
39 | obj-$(CONFIG_XTERM_CHAN) += xterm.o xterm_kern.o | ||
40 | obj-$(CONFIG_UML_WATCHDOG) += harddog.o | ||
41 | obj-$(CONFIG_BLK_DEV_COW_COMMON) += cow_user.o | ||
42 | obj-$(CONFIG_UML_RANDOM) += random.o | ||
43 | |||
44 | USER_OBJS := fd.o null.o pty.o tty.o xterm.o | ||
45 | |||
46 | include arch/um/scripts/Makefile.rules | ||
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c new file mode 100644 index 000000000000..1f77deb3fd23 --- /dev/null +++ b/arch/um/drivers/chan_kern.c | |||
@@ -0,0 +1,577 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <linux/stddef.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/list.h> | ||
9 | #include <linux/slab.h> | ||
10 | #include <linux/tty.h> | ||
11 | #include <linux/string.h> | ||
12 | #include <linux/tty_flip.h> | ||
13 | #include <asm/irq.h> | ||
14 | #include "chan_kern.h" | ||
15 | #include "user_util.h" | ||
16 | #include "kern.h" | ||
17 | #include "irq_user.h" | ||
18 | #include "sigio.h" | ||
19 | #include "line.h" | ||
20 | #include "os.h" | ||
21 | |||
22 | #ifdef CONFIG_NOCONFIG_CHAN | ||
23 | static void *not_configged_init(char *str, int device, struct chan_opts *opts) | ||
24 | { | ||
25 | printk(KERN_ERR "Using a channel type which is configured out of " | ||
26 | "UML\n"); | ||
27 | return(NULL); | ||
28 | } | ||
29 | |||
30 | static int not_configged_open(int input, int output, int primary, void *data, | ||
31 | char **dev_out) | ||
32 | { | ||
33 | printk(KERN_ERR "Using a channel type which is configured out of " | ||
34 | "UML\n"); | ||
35 | return(-ENODEV); | ||
36 | } | ||
37 | |||
38 | static void not_configged_close(int fd, void *data) | ||
39 | { | ||
40 | printk(KERN_ERR "Using a channel type which is configured out of " | ||
41 | "UML\n"); | ||
42 | } | ||
43 | |||
44 | static int not_configged_read(int fd, char *c_out, void *data) | ||
45 | { | ||
46 | printk(KERN_ERR "Using a channel type which is configured out of " | ||
47 | "UML\n"); | ||
48 | return(-EIO); | ||
49 | } | ||
50 | |||
51 | static int not_configged_write(int fd, const char *buf, int len, void *data) | ||
52 | { | ||
53 | printk(KERN_ERR "Using a channel type which is configured out of " | ||
54 | "UML\n"); | ||
55 | return(-EIO); | ||
56 | } | ||
57 | |||
58 | static int not_configged_console_write(int fd, const char *buf, int len, | ||
59 | void *data) | ||
60 | { | ||
61 | printk(KERN_ERR "Using a channel type which is configured out of " | ||
62 | "UML\n"); | ||
63 | return(-EIO); | ||
64 | } | ||
65 | |||
66 | static int not_configged_window_size(int fd, void *data, unsigned short *rows, | ||
67 | unsigned short *cols) | ||
68 | { | ||
69 | printk(KERN_ERR "Using a channel type which is configured out of " | ||
70 | "UML\n"); | ||
71 | return(-ENODEV); | ||
72 | } | ||
73 | |||
74 | static void not_configged_free(void *data) | ||
75 | { | ||
76 | printk(KERN_ERR "Using a channel type which is configured out of " | ||
77 | "UML\n"); | ||
78 | } | ||
79 | |||
80 | static struct chan_ops not_configged_ops = { | ||
81 | .init = not_configged_init, | ||
82 | .open = not_configged_open, | ||
83 | .close = not_configged_close, | ||
84 | .read = not_configged_read, | ||
85 | .write = not_configged_write, | ||
86 | .console_write = not_configged_console_write, | ||
87 | .window_size = not_configged_window_size, | ||
88 | .free = not_configged_free, | ||
89 | .winch = 0, | ||
90 | }; | ||
91 | #endif /* CONFIG_NOCONFIG_CHAN */ | ||
92 | |||
93 | void generic_close(int fd, void *unused) | ||
94 | { | ||
95 | os_close_file(fd); | ||
96 | } | ||
97 | |||
98 | int generic_read(int fd, char *c_out, void *unused) | ||
99 | { | ||
100 | int n; | ||
101 | |||
102 | n = os_read_file(fd, c_out, sizeof(*c_out)); | ||
103 | |||
104 | if(n == -EAGAIN) | ||
105 | return(0); | ||
106 | else if(n == 0) | ||
107 | return(-EIO); | ||
108 | return(n); | ||
109 | } | ||
110 | |||
111 | /* XXX Trivial wrapper around os_write_file */ | ||
112 | |||
113 | int generic_write(int fd, const char *buf, int n, void *unused) | ||
114 | { | ||
115 | return(os_write_file(fd, buf, n)); | ||
116 | } | ||
117 | |||
118 | int generic_window_size(int fd, void *unused, unsigned short *rows_out, | ||
119 | unsigned short *cols_out) | ||
120 | { | ||
121 | int rows, cols; | ||
122 | int ret; | ||
123 | |||
124 | ret = os_window_size(fd, &rows, &cols); | ||
125 | if(ret < 0) | ||
126 | return(ret); | ||
127 | |||
128 | ret = ((*rows_out != rows) || (*cols_out != cols)); | ||
129 | |||
130 | *rows_out = rows; | ||
131 | *cols_out = cols; | ||
132 | |||
133 | return(ret); | ||
134 | } | ||
135 | |||
136 | void generic_free(void *data) | ||
137 | { | ||
138 | kfree(data); | ||
139 | } | ||
140 | |||
141 | static void tty_receive_char(struct tty_struct *tty, char ch) | ||
142 | { | ||
143 | if(tty == NULL) return; | ||
144 | |||
145 | if(I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) { | ||
146 | if(ch == STOP_CHAR(tty)){ | ||
147 | stop_tty(tty); | ||
148 | return; | ||
149 | } | ||
150 | else if(ch == START_CHAR(tty)){ | ||
151 | start_tty(tty); | ||
152 | return; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | if((tty->flip.flag_buf_ptr == NULL) || | ||
157 | (tty->flip.char_buf_ptr == NULL)) | ||
158 | return; | ||
159 | tty_insert_flip_char(tty, ch, TTY_NORMAL); | ||
160 | } | ||
161 | |||
162 | static int open_one_chan(struct chan *chan, int input, int output, int primary) | ||
163 | { | ||
164 | int fd; | ||
165 | |||
166 | if(chan->opened) return(0); | ||
167 | if(chan->ops->open == NULL) fd = 0; | ||
168 | else fd = (*chan->ops->open)(input, output, primary, chan->data, | ||
169 | &chan->dev); | ||
170 | if(fd < 0) return(fd); | ||
171 | chan->fd = fd; | ||
172 | |||
173 | chan->opened = 1; | ||
174 | return(0); | ||
175 | } | ||
176 | |||
177 | int open_chan(struct list_head *chans) | ||
178 | { | ||
179 | struct list_head *ele; | ||
180 | struct chan *chan; | ||
181 | int ret, err = 0; | ||
182 | |||
183 | list_for_each(ele, chans){ | ||
184 | chan = list_entry(ele, struct chan, list); | ||
185 | ret = open_one_chan(chan, chan->input, chan->output, | ||
186 | chan->primary); | ||
187 | if(chan->primary) err = ret; | ||
188 | } | ||
189 | return(err); | ||
190 | } | ||
191 | |||
192 | void chan_enable_winch(struct list_head *chans, struct tty_struct *tty) | ||
193 | { | ||
194 | struct list_head *ele; | ||
195 | struct chan *chan; | ||
196 | |||
197 | list_for_each(ele, chans){ | ||
198 | chan = list_entry(ele, struct chan, list); | ||
199 | if(chan->primary && chan->output && chan->ops->winch){ | ||
200 | register_winch(chan->fd, tty); | ||
201 | return; | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | |||
206 | void enable_chan(struct list_head *chans, struct tty_struct *tty) | ||
207 | { | ||
208 | struct list_head *ele; | ||
209 | struct chan *chan; | ||
210 | |||
211 | list_for_each(ele, chans){ | ||
212 | chan = list_entry(ele, struct chan, list); | ||
213 | if(!chan->opened) continue; | ||
214 | |||
215 | line_setup_irq(chan->fd, chan->input, chan->output, tty); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | void close_chan(struct list_head *chans) | ||
220 | { | ||
221 | struct chan *chan; | ||
222 | |||
223 | /* Close in reverse order as open in case more than one of them | ||
224 | * refers to the same device and they save and restore that device's | ||
225 | * state. Then, the first one opened will have the original state, | ||
226 | * so it must be the last closed. | ||
227 | */ | ||
228 | list_for_each_entry_reverse(chan, chans, list) { | ||
229 | if(!chan->opened) continue; | ||
230 | if(chan->ops->close != NULL) | ||
231 | (*chan->ops->close)(chan->fd, chan->data); | ||
232 | chan->opened = 0; | ||
233 | chan->fd = -1; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | int write_chan(struct list_head *chans, const char *buf, int len, | ||
238 | int write_irq) | ||
239 | { | ||
240 | struct list_head *ele; | ||
241 | struct chan *chan = NULL; | ||
242 | int n, ret = 0; | ||
243 | |||
244 | list_for_each(ele, chans) { | ||
245 | chan = list_entry(ele, struct chan, list); | ||
246 | if (!chan->output || (chan->ops->write == NULL)) | ||
247 | continue; | ||
248 | n = chan->ops->write(chan->fd, buf, len, chan->data); | ||
249 | if (chan->primary) { | ||
250 | ret = n; | ||
251 | if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len))) | ||
252 | reactivate_fd(chan->fd, write_irq); | ||
253 | } | ||
254 | } | ||
255 | return(ret); | ||
256 | } | ||
257 | |||
258 | int console_write_chan(struct list_head *chans, const char *buf, int len) | ||
259 | { | ||
260 | struct list_head *ele; | ||
261 | struct chan *chan; | ||
262 | int n, ret = 0; | ||
263 | |||
264 | list_for_each(ele, chans){ | ||
265 | chan = list_entry(ele, struct chan, list); | ||
266 | if(!chan->output || (chan->ops->console_write == NULL)) | ||
267 | continue; | ||
268 | n = chan->ops->console_write(chan->fd, buf, len, chan->data); | ||
269 | if(chan->primary) ret = n; | ||
270 | } | ||
271 | return(ret); | ||
272 | } | ||
273 | |||
274 | int console_open_chan(struct line *line, struct console *co, struct chan_opts *opts) | ||
275 | { | ||
276 | if (!list_empty(&line->chan_list)) | ||
277 | return 0; | ||
278 | |||
279 | if (0 != parse_chan_pair(line->init_str, &line->chan_list, | ||
280 | line->init_pri, co->index, opts)) | ||
281 | return -1; | ||
282 | if (0 != open_chan(&line->chan_list)) | ||
283 | return -1; | ||
284 | printk("Console initialized on /dev/%s%d\n",co->name,co->index); | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | int chan_window_size(struct list_head *chans, unsigned short *rows_out, | ||
289 | unsigned short *cols_out) | ||
290 | { | ||
291 | struct list_head *ele; | ||
292 | struct chan *chan; | ||
293 | |||
294 | list_for_each(ele, chans){ | ||
295 | chan = list_entry(ele, struct chan, list); | ||
296 | if(chan->primary){ | ||
297 | if(chan->ops->window_size == NULL) return(0); | ||
298 | return(chan->ops->window_size(chan->fd, chan->data, | ||
299 | rows_out, cols_out)); | ||
300 | } | ||
301 | } | ||
302 | return(0); | ||
303 | } | ||
304 | |||
305 | void free_one_chan(struct chan *chan) | ||
306 | { | ||
307 | list_del(&chan->list); | ||
308 | if(chan->ops->free != NULL) | ||
309 | (*chan->ops->free)(chan->data); | ||
310 | free_irq_by_fd(chan->fd); | ||
311 | if(chan->primary && chan->output) ignore_sigio_fd(chan->fd); | ||
312 | kfree(chan); | ||
313 | } | ||
314 | |||
315 | void free_chan(struct list_head *chans) | ||
316 | { | ||
317 | struct list_head *ele, *next; | ||
318 | struct chan *chan; | ||
319 | |||
320 | list_for_each_safe(ele, next, chans){ | ||
321 | chan = list_entry(ele, struct chan, list); | ||
322 | free_one_chan(chan); | ||
323 | } | ||
324 | } | ||
325 | |||
326 | static int one_chan_config_string(struct chan *chan, char *str, int size, | ||
327 | char **error_out) | ||
328 | { | ||
329 | int n = 0; | ||
330 | |||
331 | if(chan == NULL){ | ||
332 | CONFIG_CHUNK(str, size, n, "none", 1); | ||
333 | return(n); | ||
334 | } | ||
335 | |||
336 | CONFIG_CHUNK(str, size, n, chan->ops->type, 0); | ||
337 | |||
338 | if(chan->dev == NULL){ | ||
339 | CONFIG_CHUNK(str, size, n, "", 1); | ||
340 | return(n); | ||
341 | } | ||
342 | |||
343 | CONFIG_CHUNK(str, size, n, ":", 0); | ||
344 | CONFIG_CHUNK(str, size, n, chan->dev, 0); | ||
345 | |||
346 | return(n); | ||
347 | } | ||
348 | |||
349 | static int chan_pair_config_string(struct chan *in, struct chan *out, | ||
350 | char *str, int size, char **error_out) | ||
351 | { | ||
352 | int n; | ||
353 | |||
354 | n = one_chan_config_string(in, str, size, error_out); | ||
355 | str += n; | ||
356 | size -= n; | ||
357 | |||
358 | if(in == out){ | ||
359 | CONFIG_CHUNK(str, size, n, "", 1); | ||
360 | return(n); | ||
361 | } | ||
362 | |||
363 | CONFIG_CHUNK(str, size, n, ",", 1); | ||
364 | n = one_chan_config_string(out, str, size, error_out); | ||
365 | str += n; | ||
366 | size -= n; | ||
367 | CONFIG_CHUNK(str, size, n, "", 1); | ||
368 | |||
369 | return(n); | ||
370 | } | ||
371 | |||
372 | int chan_config_string(struct list_head *chans, char *str, int size, | ||
373 | char **error_out) | ||
374 | { | ||
375 | struct list_head *ele; | ||
376 | struct chan *chan, *in = NULL, *out = NULL; | ||
377 | |||
378 | list_for_each(ele, chans){ | ||
379 | chan = list_entry(ele, struct chan, list); | ||
380 | if(!chan->primary) | ||
381 | continue; | ||
382 | if(chan->input) | ||
383 | in = chan; | ||
384 | if(chan->output) | ||
385 | out = chan; | ||
386 | } | ||
387 | |||
388 | return(chan_pair_config_string(in, out, str, size, error_out)); | ||
389 | } | ||
390 | |||
391 | struct chan_type { | ||
392 | char *key; | ||
393 | struct chan_ops *ops; | ||
394 | }; | ||
395 | |||
396 | struct chan_type chan_table[] = { | ||
397 | { "fd", &fd_ops }, | ||
398 | |||
399 | #ifdef CONFIG_NULL_CHAN | ||
400 | { "null", &null_ops }, | ||
401 | #else | ||
402 | { "null", ¬_configged_ops }, | ||
403 | #endif | ||
404 | |||
405 | #ifdef CONFIG_PORT_CHAN | ||
406 | { "port", &port_ops }, | ||
407 | #else | ||
408 | { "port", ¬_configged_ops }, | ||
409 | #endif | ||
410 | |||
411 | #ifdef CONFIG_PTY_CHAN | ||
412 | { "pty", &pty_ops }, | ||
413 | { "pts", &pts_ops }, | ||
414 | #else | ||
415 | { "pty", ¬_configged_ops }, | ||
416 | { "pts", ¬_configged_ops }, | ||
417 | #endif | ||
418 | |||
419 | #ifdef CONFIG_TTY_CHAN | ||
420 | { "tty", &tty_ops }, | ||
421 | #else | ||
422 | { "tty", ¬_configged_ops }, | ||
423 | #endif | ||
424 | |||
425 | #ifdef CONFIG_XTERM_CHAN | ||
426 | { "xterm", &xterm_ops }, | ||
427 | #else | ||
428 | { "xterm", ¬_configged_ops }, | ||
429 | #endif | ||
430 | }; | ||
431 | |||
432 | static struct chan *parse_chan(char *str, int pri, int device, | ||
433 | struct chan_opts *opts) | ||
434 | { | ||
435 | struct chan_type *entry; | ||
436 | struct chan_ops *ops; | ||
437 | struct chan *chan; | ||
438 | void *data; | ||
439 | int i; | ||
440 | |||
441 | ops = NULL; | ||
442 | data = NULL; | ||
443 | for(i = 0; i < sizeof(chan_table)/sizeof(chan_table[0]); i++){ | ||
444 | entry = &chan_table[i]; | ||
445 | if(!strncmp(str, entry->key, strlen(entry->key))){ | ||
446 | ops = entry->ops; | ||
447 | str += strlen(entry->key); | ||
448 | break; | ||
449 | } | ||
450 | } | ||
451 | if(ops == NULL){ | ||
452 | printk(KERN_ERR "parse_chan couldn't parse \"%s\"\n", | ||
453 | str); | ||
454 | return(NULL); | ||
455 | } | ||
456 | if(ops->init == NULL) return(NULL); | ||
457 | data = (*ops->init)(str, device, opts); | ||
458 | if(data == NULL) return(NULL); | ||
459 | |||
460 | chan = kmalloc(sizeof(*chan), GFP_KERNEL); | ||
461 | if(chan == NULL) return(NULL); | ||
462 | *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list), | ||
463 | .primary = 1, | ||
464 | .input = 0, | ||
465 | .output = 0, | ||
466 | .opened = 0, | ||
467 | .fd = -1, | ||
468 | .pri = pri, | ||
469 | .ops = ops, | ||
470 | .data = data }); | ||
471 | return(chan); | ||
472 | } | ||
473 | |||
474 | int parse_chan_pair(char *str, struct list_head *chans, int pri, int device, | ||
475 | struct chan_opts *opts) | ||
476 | { | ||
477 | struct chan *new, *chan; | ||
478 | char *in, *out; | ||
479 | |||
480 | if(!list_empty(chans)){ | ||
481 | chan = list_entry(chans->next, struct chan, list); | ||
482 | if(chan->pri >= pri) return(0); | ||
483 | free_chan(chans); | ||
484 | INIT_LIST_HEAD(chans); | ||
485 | } | ||
486 | |||
487 | out = strchr(str, ','); | ||
488 | if(out != NULL){ | ||
489 | in = str; | ||
490 | *out = '\0'; | ||
491 | out++; | ||
492 | new = parse_chan(in, pri, device, opts); | ||
493 | if(new == NULL) return(-1); | ||
494 | new->input = 1; | ||
495 | list_add(&new->list, chans); | ||
496 | |||
497 | new = parse_chan(out, pri, device, opts); | ||
498 | if(new == NULL) return(-1); | ||
499 | list_add(&new->list, chans); | ||
500 | new->output = 1; | ||
501 | } | ||
502 | else { | ||
503 | new = parse_chan(str, pri, device, opts); | ||
504 | if(new == NULL) return(-1); | ||
505 | list_add(&new->list, chans); | ||
506 | new->input = 1; | ||
507 | new->output = 1; | ||
508 | } | ||
509 | return(0); | ||
510 | } | ||
511 | |||
512 | int chan_out_fd(struct list_head *chans) | ||
513 | { | ||
514 | struct list_head *ele; | ||
515 | struct chan *chan; | ||
516 | |||
517 | list_for_each(ele, chans){ | ||
518 | chan = list_entry(ele, struct chan, list); | ||
519 | if(chan->primary && chan->output) | ||
520 | return(chan->fd); | ||
521 | } | ||
522 | return(-1); | ||
523 | } | ||
524 | |||
525 | void chan_interrupt(struct list_head *chans, struct work_struct *task, | ||
526 | struct tty_struct *tty, int irq) | ||
527 | { | ||
528 | struct list_head *ele, *next; | ||
529 | struct chan *chan; | ||
530 | int err; | ||
531 | char c; | ||
532 | |||
533 | list_for_each_safe(ele, next, chans){ | ||
534 | chan = list_entry(ele, struct chan, list); | ||
535 | if(!chan->input || (chan->ops->read == NULL)) continue; | ||
536 | do { | ||
537 | if((tty != NULL) && | ||
538 | (tty->flip.count >= TTY_FLIPBUF_SIZE)){ | ||
539 | schedule_work(task); | ||
540 | goto out; | ||
541 | } | ||
542 | err = chan->ops->read(chan->fd, &c, chan->data); | ||
543 | if(err > 0) | ||
544 | tty_receive_char(tty, c); | ||
545 | } while(err > 0); | ||
546 | |||
547 | if(err == 0) reactivate_fd(chan->fd, irq); | ||
548 | if(err == -EIO){ | ||
549 | if(chan->primary){ | ||
550 | if(tty != NULL) | ||
551 | tty_hangup(tty); | ||
552 | line_disable(tty, irq); | ||
553 | close_chan(chans); | ||
554 | free_chan(chans); | ||
555 | return; | ||
556 | } | ||
557 | else { | ||
558 | if(chan->ops->close != NULL) | ||
559 | chan->ops->close(chan->fd, chan->data); | ||
560 | free_one_chan(chan); | ||
561 | } | ||
562 | } | ||
563 | } | ||
564 | out: | ||
565 | if(tty) tty_flip_buffer_push(tty); | ||
566 | } | ||
567 | |||
568 | /* | ||
569 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
570 | * Emacs will notice this stuff at the end of the file and automatically | ||
571 | * adjust the settings for this buffer only. This must remain at the end | ||
572 | * of the file. | ||
573 | * --------------------------------------------------------------------------- | ||
574 | * Local variables: | ||
575 | * c-file-style: "linux" | ||
576 | * End: | ||
577 | */ | ||
diff --git a/arch/um/drivers/chan_user.c b/arch/um/drivers/chan_user.c new file mode 100644 index 000000000000..583b8e137c33 --- /dev/null +++ b/arch/um/drivers/chan_user.c | |||
@@ -0,0 +1,210 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <unistd.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <errno.h> | ||
9 | #include <termios.h> | ||
10 | #include <string.h> | ||
11 | #include <signal.h> | ||
12 | #include <sys/stat.h> | ||
13 | #include <sys/ioctl.h> | ||
14 | #include <sys/socket.h> | ||
15 | #include "kern_util.h" | ||
16 | #include "user_util.h" | ||
17 | #include "chan_user.h" | ||
18 | #include "user.h" | ||
19 | #include "helper.h" | ||
20 | #include "os.h" | ||
21 | #include "choose-mode.h" | ||
22 | #include "mode.h" | ||
23 | |||
24 | int generic_console_write(int fd, const char *buf, int n, void *unused) | ||
25 | { | ||
26 | struct termios save, new; | ||
27 | int err; | ||
28 | |||
29 | if(isatty(fd)){ | ||
30 | CATCH_EINTR(err = tcgetattr(fd, &save)); | ||
31 | if (err) | ||
32 | goto error; | ||
33 | new = save; | ||
34 | /* The terminal becomes a bit less raw, to handle \n also as | ||
35 | * "Carriage Return", not only as "New Line". Otherwise, the new | ||
36 | * line won't start at the first column.*/ | ||
37 | new.c_oflag |= OPOST; | ||
38 | CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &new)); | ||
39 | if (err) | ||
40 | goto error; | ||
41 | } | ||
42 | err = generic_write(fd, buf, n, NULL); | ||
43 | /* Restore raw mode, in any case; we *must* ignore any error apart | ||
44 | * EINTR, except for debug.*/ | ||
45 | if(isatty(fd)) | ||
46 | CATCH_EINTR(tcsetattr(fd, TCSAFLUSH, &save)); | ||
47 | return(err); | ||
48 | error: | ||
49 | return(-errno); | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * UML SIGWINCH handling | ||
54 | * | ||
55 | * The point of this is to handle SIGWINCH on consoles which have host ttys and | ||
56 | * relay them inside UML to whatever might be running on the console and cares | ||
57 | * about the window size (since SIGWINCH notifies about terminal size changes). | ||
58 | * | ||
59 | * So, we have a separate thread for each host tty attached to a UML device | ||
60 | * (side-issue - I'm annoyed that one thread can't have multiple controlling | ||
61 | * ttys for purposed of handling SIGWINCH, but I imagine there are other reasons | ||
62 | * that doesn't make any sense). | ||
63 | * | ||
64 | * SIGWINCH can't be received synchronously, so you have to set up to receive it | ||
65 | * as a signal. That being the case, if you are going to wait for it, it is | ||
66 | * convenient to sit in a pause() and wait for the signal to bounce you out of | ||
67 | * it (see below for how we make sure to exit only on SIGWINCH). | ||
68 | */ | ||
69 | |||
70 | static void winch_handler(int sig) | ||
71 | { | ||
72 | } | ||
73 | |||
74 | struct winch_data { | ||
75 | int pty_fd; | ||
76 | int pipe_fd; | ||
77 | int close_me; | ||
78 | }; | ||
79 | |||
80 | static int winch_thread(void *arg) | ||
81 | { | ||
82 | struct winch_data *data = arg; | ||
83 | sigset_t sigs; | ||
84 | int pty_fd, pipe_fd; | ||
85 | int count, err; | ||
86 | char c = 1; | ||
87 | |||
88 | os_close_file(data->close_me); | ||
89 | pty_fd = data->pty_fd; | ||
90 | pipe_fd = data->pipe_fd; | ||
91 | count = os_write_file(pipe_fd, &c, sizeof(c)); | ||
92 | if(count != sizeof(c)) | ||
93 | printk("winch_thread : failed to write synchronization " | ||
94 | "byte, err = %d\n", -count); | ||
95 | |||
96 | /* We are not using SIG_IGN on purpose, so don't fix it as I thought to | ||
97 | * do! If using SIG_IGN, the pause() call below would not stop on | ||
98 | * SIGWINCH. */ | ||
99 | |||
100 | signal(SIGWINCH, winch_handler); | ||
101 | sigfillset(&sigs); | ||
102 | sigdelset(&sigs, SIGWINCH); | ||
103 | /* Block anything else than SIGWINCH. */ | ||
104 | if(sigprocmask(SIG_SETMASK, &sigs, NULL) < 0){ | ||
105 | printk("winch_thread : sigprocmask failed, errno = %d\n", | ||
106 | errno); | ||
107 | exit(1); | ||
108 | } | ||
109 | |||
110 | if(setsid() < 0){ | ||
111 | printk("winch_thread : setsid failed, errno = %d\n", errno); | ||
112 | exit(1); | ||
113 | } | ||
114 | |||
115 | err = os_new_tty_pgrp(pty_fd, os_getpid()); | ||
116 | if(err < 0){ | ||
117 | printk("winch_thread : new_tty_pgrp failed, err = %d\n", -err); | ||
118 | exit(1); | ||
119 | } | ||
120 | |||
121 | /* These are synchronization calls between various UML threads on the | ||
122 | * host - since they are not different kernel threads, we cannot use | ||
123 | * kernel semaphores. We don't use SysV semaphores because they are | ||
124 | * persistant. */ | ||
125 | count = os_read_file(pipe_fd, &c, sizeof(c)); | ||
126 | if(count != sizeof(c)) | ||
127 | printk("winch_thread : failed to read synchronization byte, " | ||
128 | "err = %d\n", -count); | ||
129 | |||
130 | while(1){ | ||
131 | /* This will be interrupted by SIGWINCH only, since other signals | ||
132 | * are blocked.*/ | ||
133 | pause(); | ||
134 | |||
135 | count = os_write_file(pipe_fd, &c, sizeof(c)); | ||
136 | if(count != sizeof(c)) | ||
137 | printk("winch_thread : write failed, err = %d\n", | ||
138 | -count); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | static int winch_tramp(int fd, struct tty_struct *tty, int *fd_out) | ||
143 | { | ||
144 | struct winch_data data; | ||
145 | unsigned long stack; | ||
146 | int fds[2], pid, n, err; | ||
147 | char c; | ||
148 | |||
149 | err = os_pipe(fds, 1, 1); | ||
150 | if(err < 0){ | ||
151 | printk("winch_tramp : os_pipe failed, err = %d\n", -err); | ||
152 | return(err); | ||
153 | } | ||
154 | |||
155 | data = ((struct winch_data) { .pty_fd = fd, | ||
156 | .pipe_fd = fds[1], | ||
157 | .close_me = fds[0] } ); | ||
158 | pid = run_helper_thread(winch_thread, &data, 0, &stack, 0); | ||
159 | if(pid < 0){ | ||
160 | printk("fork of winch_thread failed - errno = %d\n", errno); | ||
161 | return(pid); | ||
162 | } | ||
163 | |||
164 | os_close_file(fds[1]); | ||
165 | *fd_out = fds[0]; | ||
166 | n = os_read_file(fds[0], &c, sizeof(c)); | ||
167 | if(n != sizeof(c)){ | ||
168 | printk("winch_tramp : failed to read synchronization byte\n"); | ||
169 | printk("read failed, err = %d\n", -n); | ||
170 | printk("fd %d will not support SIGWINCH\n", fd); | ||
171 | *fd_out = -1; | ||
172 | } | ||
173 | return(pid); | ||
174 | } | ||
175 | |||
176 | void register_winch(int fd, struct tty_struct *tty) | ||
177 | { | ||
178 | int pid, thread, thread_fd; | ||
179 | int count; | ||
180 | char c = 1; | ||
181 | |||
182 | if(!isatty(fd)) | ||
183 | return; | ||
184 | |||
185 | pid = tcgetpgrp(fd); | ||
186 | if(!CHOOSE_MODE_PROC(is_tracer_winch, is_skas_winch, pid, fd, | ||
187 | tty) && (pid == -1)){ | ||
188 | thread = winch_tramp(fd, tty, &thread_fd); | ||
189 | if(fd != -1){ | ||
190 | register_winch_irq(thread_fd, fd, thread, tty); | ||
191 | |||
192 | count = os_write_file(thread_fd, &c, sizeof(c)); | ||
193 | if(count != sizeof(c)) | ||
194 | printk("register_winch : failed to write " | ||
195 | "synchronization byte, err = %d\n", | ||
196 | -count); | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
203 | * Emacs will notice this stuff at the end of the file and automatically | ||
204 | * adjust the settings for this buffer only. This must remain at the end | ||
205 | * of the file. | ||
206 | * --------------------------------------------------------------------------- | ||
207 | * Local variables: | ||
208 | * c-file-style: "linux" | ||
209 | * End: | ||
210 | */ | ||
diff --git a/arch/um/drivers/cow.h b/arch/um/drivers/cow.h new file mode 100644 index 000000000000..4fcbe8b1b77e --- /dev/null +++ b/arch/um/drivers/cow.h | |||
@@ -0,0 +1,42 @@ | |||
1 | #ifndef __COW_H__ | ||
2 | #define __COW_H__ | ||
3 | |||
4 | #include <asm/types.h> | ||
5 | |||
6 | #if __BYTE_ORDER == __BIG_ENDIAN | ||
7 | # define ntohll(x) (x) | ||
8 | # define htonll(x) (x) | ||
9 | #elif __BYTE_ORDER == __LITTLE_ENDIAN | ||
10 | # define ntohll(x) bswap_64(x) | ||
11 | # define htonll(x) bswap_64(x) | ||
12 | #else | ||
13 | #error "__BYTE_ORDER not defined" | ||
14 | #endif | ||
15 | |||
16 | extern int init_cow_file(int fd, char *cow_file, char *backing_file, | ||
17 | int sectorsize, int alignment, int *bitmap_offset_out, | ||
18 | unsigned long *bitmap_len_out, int *data_offset_out); | ||
19 | |||
20 | extern int file_reader(__u64 offset, char *buf, int len, void *arg); | ||
21 | extern int read_cow_header(int (*reader)(__u64, char *, int, void *), | ||
22 | void *arg, __u32 *version_out, | ||
23 | char **backing_file_out, time_t *mtime_out, | ||
24 | unsigned long long *size_out, int *sectorsize_out, | ||
25 | __u32 *align_out, int *bitmap_offset_out); | ||
26 | |||
27 | extern int write_cow_header(char *cow_file, int fd, char *backing_file, | ||
28 | int sectorsize, int alignment, | ||
29 | unsigned long long *size); | ||
30 | |||
31 | extern void cow_sizes(int version, __u64 size, int sectorsize, int align, | ||
32 | int bitmap_offset, unsigned long *bitmap_len_out, | ||
33 | int *data_offset_out); | ||
34 | |||
35 | #endif | ||
36 | |||
37 | /* | ||
38 | * --------------------------------------------------------------------------- | ||
39 | * Local variables: | ||
40 | * c-file-style: "linux" | ||
41 | * End: | ||
42 | */ | ||
diff --git a/arch/um/drivers/cow_sys.h b/arch/um/drivers/cow_sys.h new file mode 100644 index 000000000000..c83fc5d68936 --- /dev/null +++ b/arch/um/drivers/cow_sys.h | |||
@@ -0,0 +1,48 @@ | |||
1 | #ifndef __COW_SYS_H__ | ||
2 | #define __COW_SYS_H__ | ||
3 | |||
4 | #include "kern_util.h" | ||
5 | #include "user_util.h" | ||
6 | #include "os.h" | ||
7 | #include "user.h" | ||
8 | |||
9 | static inline void *cow_malloc(int size) | ||
10 | { | ||
11 | return(um_kmalloc(size)); | ||
12 | } | ||
13 | |||
14 | static inline void cow_free(void *ptr) | ||
15 | { | ||
16 | kfree(ptr); | ||
17 | } | ||
18 | |||
19 | #define cow_printf printk | ||
20 | |||
21 | static inline char *cow_strdup(char *str) | ||
22 | { | ||
23 | return(uml_strdup(str)); | ||
24 | } | ||
25 | |||
26 | static inline int cow_seek_file(int fd, unsigned long long offset) | ||
27 | { | ||
28 | return(os_seek_file(fd, offset)); | ||
29 | } | ||
30 | |||
31 | static inline int cow_file_size(char *file, unsigned long long *size_out) | ||
32 | { | ||
33 | return(os_file_size(file, size_out)); | ||
34 | } | ||
35 | |||
36 | static inline int cow_write_file(int fd, char *buf, int size) | ||
37 | { | ||
38 | return(os_write_file(fd, buf, size)); | ||
39 | } | ||
40 | |||
41 | #endif | ||
42 | |||
43 | /* | ||
44 | * --------------------------------------------------------------------------- | ||
45 | * Local variables: | ||
46 | * c-file-style: "linux" | ||
47 | * End: | ||
48 | */ | ||
diff --git a/arch/um/drivers/cow_user.c b/arch/um/drivers/cow_user.c new file mode 100644 index 000000000000..a8ce6fc3ef26 --- /dev/null +++ b/arch/um/drivers/cow_user.c | |||
@@ -0,0 +1,378 @@ | |||
1 | #include <stddef.h> | ||
2 | #include <string.h> | ||
3 | #include <errno.h> | ||
4 | /* _XOPEN_SOURCE is needed for pread, but we define _GNU_SOURCE, which defines | ||
5 | * that. | ||
6 | */ | ||
7 | #include <unistd.h> | ||
8 | #include <byteswap.h> | ||
9 | #include <sys/time.h> | ||
10 | #include <sys/param.h> | ||
11 | #include <sys/user.h> | ||
12 | #include <netinet/in.h> | ||
13 | |||
14 | #include "os.h" | ||
15 | |||
16 | #include "cow.h" | ||
17 | #include "cow_sys.h" | ||
18 | |||
19 | #define PATH_LEN_V1 256 | ||
20 | |||
21 | struct cow_header_v1 { | ||
22 | int magic; | ||
23 | int version; | ||
24 | char backing_file[PATH_LEN_V1]; | ||
25 | time_t mtime; | ||
26 | __u64 size; | ||
27 | int sectorsize; | ||
28 | }; | ||
29 | |||
30 | #define PATH_LEN_V2 MAXPATHLEN | ||
31 | |||
32 | struct cow_header_v2 { | ||
33 | __u32 magic; | ||
34 | __u32 version; | ||
35 | char backing_file[PATH_LEN_V2]; | ||
36 | time_t mtime; | ||
37 | __u64 size; | ||
38 | int sectorsize; | ||
39 | }; | ||
40 | |||
41 | /* Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in | ||
42 | * case other systems have different values for MAXPATHLEN | ||
43 | */ | ||
44 | #define PATH_LEN_V3 4096 | ||
45 | |||
46 | /* Changes from V2 - | ||
47 | * PATH_LEN_V3 as described above | ||
48 | * Explicitly specify field bit lengths for systems with different | ||
49 | * lengths for the usual C types. Not sure whether char or | ||
50 | * time_t should be changed, this can be changed later without | ||
51 | * breaking compatibility | ||
52 | * Add alignment field so that different alignments can be used for the | ||
53 | * bitmap and data | ||
54 | * Add cow_format field to allow for the possibility of different ways | ||
55 | * of specifying the COW blocks. For now, the only value is 0, | ||
56 | * for the traditional COW bitmap. | ||
57 | * Move the backing_file field to the end of the header. This allows | ||
58 | * for the possibility of expanding it into the padding required | ||
59 | * by the bitmap alignment. | ||
60 | * The bitmap and data portions of the file will be aligned as specified | ||
61 | * by the alignment field. This is to allow COW files to be | ||
62 | * put on devices with restrictions on access alignments, such as | ||
63 | * /dev/raw, with a 512 byte alignment restriction. This also | ||
64 | * allows the data to be more aligned more strictly than on | ||
65 | * sector boundaries. This is needed for ubd-mmap, which needs | ||
66 | * the data to be page aligned. | ||
67 | * Fixed (finally!) the rounding bug | ||
68 | */ | ||
69 | |||
70 | struct cow_header_v3 { | ||
71 | __u32 magic; | ||
72 | __u32 version; | ||
73 | __u32 mtime; | ||
74 | __u64 size; | ||
75 | __u32 sectorsize; | ||
76 | __u32 alignment; | ||
77 | __u32 cow_format; | ||
78 | char backing_file[PATH_LEN_V3]; | ||
79 | }; | ||
80 | |||
81 | /* COW format definitions - for now, we have only the usual COW bitmap */ | ||
82 | #define COW_BITMAP 0 | ||
83 | |||
84 | union cow_header { | ||
85 | struct cow_header_v1 v1; | ||
86 | struct cow_header_v2 v2; | ||
87 | struct cow_header_v3 v3; | ||
88 | }; | ||
89 | |||
90 | #define COW_MAGIC 0x4f4f4f4d /* MOOO */ | ||
91 | #define COW_VERSION 3 | ||
92 | |||
93 | #define DIV_ROUND(x, len) (((x) + (len) - 1) / (len)) | ||
94 | #define ROUND_UP(x, align) DIV_ROUND(x, align) * (align) | ||
95 | |||
96 | void cow_sizes(int version, __u64 size, int sectorsize, int align, | ||
97 | int bitmap_offset, unsigned long *bitmap_len_out, | ||
98 | int *data_offset_out) | ||
99 | { | ||
100 | if(version < 3){ | ||
101 | *bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize); | ||
102 | |||
103 | *data_offset_out = bitmap_offset + *bitmap_len_out; | ||
104 | *data_offset_out = (*data_offset_out + sectorsize - 1) / | ||
105 | sectorsize; | ||
106 | *data_offset_out *= sectorsize; | ||
107 | } | ||
108 | else { | ||
109 | *bitmap_len_out = DIV_ROUND(size, sectorsize); | ||
110 | *bitmap_len_out = DIV_ROUND(*bitmap_len_out, 8); | ||
111 | |||
112 | *data_offset_out = bitmap_offset + *bitmap_len_out; | ||
113 | *data_offset_out = ROUND_UP(*data_offset_out, align); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | static int absolutize(char *to, int size, char *from) | ||
118 | { | ||
119 | char save_cwd[256], *slash; | ||
120 | int remaining; | ||
121 | |||
122 | if(getcwd(save_cwd, sizeof(save_cwd)) == NULL) { | ||
123 | cow_printf("absolutize : unable to get cwd - errno = %d\n", | ||
124 | errno); | ||
125 | return(-1); | ||
126 | } | ||
127 | slash = strrchr(from, '/'); | ||
128 | if(slash != NULL){ | ||
129 | *slash = '\0'; | ||
130 | if(chdir(from)){ | ||
131 | *slash = '/'; | ||
132 | cow_printf("absolutize : Can't cd to '%s' - " | ||
133 | "errno = %d\n", from, errno); | ||
134 | return(-1); | ||
135 | } | ||
136 | *slash = '/'; | ||
137 | if(getcwd(to, size) == NULL){ | ||
138 | cow_printf("absolutize : unable to get cwd of '%s' - " | ||
139 | "errno = %d\n", from, errno); | ||
140 | return(-1); | ||
141 | } | ||
142 | remaining = size - strlen(to); | ||
143 | if(strlen(slash) + 1 > remaining){ | ||
144 | cow_printf("absolutize : unable to fit '%s' into %d " | ||
145 | "chars\n", from, size); | ||
146 | return(-1); | ||
147 | } | ||
148 | strcat(to, slash); | ||
149 | } | ||
150 | else { | ||
151 | if(strlen(save_cwd) + 1 + strlen(from) + 1 > size){ | ||
152 | cow_printf("absolutize : unable to fit '%s' into %d " | ||
153 | "chars\n", from, size); | ||
154 | return(-1); | ||
155 | } | ||
156 | strcpy(to, save_cwd); | ||
157 | strcat(to, "/"); | ||
158 | strcat(to, from); | ||
159 | } | ||
160 | chdir(save_cwd); | ||
161 | return(0); | ||
162 | } | ||
163 | |||
164 | int write_cow_header(char *cow_file, int fd, char *backing_file, | ||
165 | int sectorsize, int alignment, unsigned long long *size) | ||
166 | { | ||
167 | struct cow_header_v3 *header; | ||
168 | unsigned long modtime; | ||
169 | int err; | ||
170 | |||
171 | err = cow_seek_file(fd, 0); | ||
172 | if(err < 0){ | ||
173 | cow_printf("write_cow_header - lseek failed, err = %d\n", -err); | ||
174 | goto out; | ||
175 | } | ||
176 | |||
177 | err = -ENOMEM; | ||
178 | header = cow_malloc(sizeof(*header)); | ||
179 | if(header == NULL){ | ||
180 | cow_printf("Failed to allocate COW V3 header\n"); | ||
181 | goto out; | ||
182 | } | ||
183 | header->magic = htonl(COW_MAGIC); | ||
184 | header->version = htonl(COW_VERSION); | ||
185 | |||
186 | err = -EINVAL; | ||
187 | if(strlen(backing_file) > sizeof(header->backing_file) - 1){ | ||
188 | cow_printf("Backing file name \"%s\" is too long - names are " | ||
189 | "limited to %d characters\n", backing_file, | ||
190 | sizeof(header->backing_file) - 1); | ||
191 | goto out_free; | ||
192 | } | ||
193 | |||
194 | if(absolutize(header->backing_file, sizeof(header->backing_file), | ||
195 | backing_file)) | ||
196 | goto out_free; | ||
197 | |||
198 | err = os_file_modtime(header->backing_file, &modtime); | ||
199 | if(err < 0){ | ||
200 | cow_printf("Backing file '%s' mtime request failed, " | ||
201 | "err = %d\n", header->backing_file, -err); | ||
202 | goto out_free; | ||
203 | } | ||
204 | |||
205 | err = cow_file_size(header->backing_file, size); | ||
206 | if(err < 0){ | ||
207 | cow_printf("Couldn't get size of backing file '%s', " | ||
208 | "err = %d\n", header->backing_file, -err); | ||
209 | goto out_free; | ||
210 | } | ||
211 | |||
212 | header->mtime = htonl(modtime); | ||
213 | header->size = htonll(*size); | ||
214 | header->sectorsize = htonl(sectorsize); | ||
215 | header->alignment = htonl(alignment); | ||
216 | header->cow_format = COW_BITMAP; | ||
217 | |||
218 | err = os_write_file(fd, header, sizeof(*header)); | ||
219 | if(err != sizeof(*header)){ | ||
220 | cow_printf("Write of header to new COW file '%s' failed, " | ||
221 | "err = %d\n", cow_file, -err); | ||
222 | goto out_free; | ||
223 | } | ||
224 | err = 0; | ||
225 | out_free: | ||
226 | cow_free(header); | ||
227 | out: | ||
228 | return(err); | ||
229 | } | ||
230 | |||
231 | int file_reader(__u64 offset, char *buf, int len, void *arg) | ||
232 | { | ||
233 | int fd = *((int *) arg); | ||
234 | |||
235 | return(pread(fd, buf, len, offset)); | ||
236 | } | ||
237 | |||
238 | /* XXX Need to sanity-check the values read from the header */ | ||
239 | |||
240 | int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, | ||
241 | __u32 *version_out, char **backing_file_out, | ||
242 | time_t *mtime_out, unsigned long long *size_out, | ||
243 | int *sectorsize_out, __u32 *align_out, | ||
244 | int *bitmap_offset_out) | ||
245 | { | ||
246 | union cow_header *header; | ||
247 | char *file; | ||
248 | int err, n; | ||
249 | unsigned long version, magic; | ||
250 | |||
251 | header = cow_malloc(sizeof(*header)); | ||
252 | if(header == NULL){ | ||
253 | cow_printf("read_cow_header - Failed to allocate header\n"); | ||
254 | return(-ENOMEM); | ||
255 | } | ||
256 | err = -EINVAL; | ||
257 | n = (*reader)(0, (char *) header, sizeof(*header), arg); | ||
258 | if(n < offsetof(typeof(header->v1), backing_file)){ | ||
259 | cow_printf("read_cow_header - short header\n"); | ||
260 | goto out; | ||
261 | } | ||
262 | |||
263 | magic = header->v1.magic; | ||
264 | if(magic == COW_MAGIC) { | ||
265 | version = header->v1.version; | ||
266 | } | ||
267 | else if(magic == ntohl(COW_MAGIC)){ | ||
268 | version = ntohl(header->v1.version); | ||
269 | } | ||
270 | /* No error printed because the non-COW case comes through here */ | ||
271 | else goto out; | ||
272 | |||
273 | *version_out = version; | ||
274 | |||
275 | if(version == 1){ | ||
276 | if(n < sizeof(header->v1)){ | ||
277 | cow_printf("read_cow_header - failed to read V1 " | ||
278 | "header\n"); | ||
279 | goto out; | ||
280 | } | ||
281 | *mtime_out = header->v1.mtime; | ||
282 | *size_out = header->v1.size; | ||
283 | *sectorsize_out = header->v1.sectorsize; | ||
284 | *bitmap_offset_out = sizeof(header->v1); | ||
285 | *align_out = *sectorsize_out; | ||
286 | file = header->v1.backing_file; | ||
287 | } | ||
288 | else if(version == 2){ | ||
289 | if(n < sizeof(header->v2)){ | ||
290 | cow_printf("read_cow_header - failed to read V2 " | ||
291 | "header\n"); | ||
292 | goto out; | ||
293 | } | ||
294 | *mtime_out = ntohl(header->v2.mtime); | ||
295 | *size_out = ntohll(header->v2.size); | ||
296 | *sectorsize_out = ntohl(header->v2.sectorsize); | ||
297 | *bitmap_offset_out = sizeof(header->v2); | ||
298 | *align_out = *sectorsize_out; | ||
299 | file = header->v2.backing_file; | ||
300 | } | ||
301 | else if(version == 3){ | ||
302 | if(n < sizeof(header->v3)){ | ||
303 | cow_printf("read_cow_header - failed to read V2 " | ||
304 | "header\n"); | ||
305 | goto out; | ||
306 | } | ||
307 | *mtime_out = ntohl(header->v3.mtime); | ||
308 | *size_out = ntohll(header->v3.size); | ||
309 | *sectorsize_out = ntohl(header->v3.sectorsize); | ||
310 | *align_out = ntohl(header->v3.alignment); | ||
311 | *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out); | ||
312 | file = header->v3.backing_file; | ||
313 | } | ||
314 | else { | ||
315 | cow_printf("read_cow_header - invalid COW version\n"); | ||
316 | goto out; | ||
317 | } | ||
318 | err = -ENOMEM; | ||
319 | *backing_file_out = cow_strdup(file); | ||
320 | if(*backing_file_out == NULL){ | ||
321 | cow_printf("read_cow_header - failed to allocate backing " | ||
322 | "file\n"); | ||
323 | goto out; | ||
324 | } | ||
325 | err = 0; | ||
326 | out: | ||
327 | cow_free(header); | ||
328 | return(err); | ||
329 | } | ||
330 | |||
331 | int init_cow_file(int fd, char *cow_file, char *backing_file, int sectorsize, | ||
332 | int alignment, int *bitmap_offset_out, | ||
333 | unsigned long *bitmap_len_out, int *data_offset_out) | ||
334 | { | ||
335 | unsigned long long size, offset; | ||
336 | char zero = 0; | ||
337 | int err; | ||
338 | |||
339 | err = write_cow_header(cow_file, fd, backing_file, sectorsize, | ||
340 | alignment, &size); | ||
341 | if(err) | ||
342 | goto out; | ||
343 | |||
344 | *bitmap_offset_out = ROUND_UP(sizeof(struct cow_header_v3), alignment); | ||
345 | cow_sizes(COW_VERSION, size, sectorsize, alignment, *bitmap_offset_out, | ||
346 | bitmap_len_out, data_offset_out); | ||
347 | |||
348 | offset = *data_offset_out + size - sizeof(zero); | ||
349 | err = cow_seek_file(fd, offset); | ||
350 | if(err < 0){ | ||
351 | cow_printf("cow bitmap lseek failed : err = %d\n", -err); | ||
352 | goto out; | ||
353 | } | ||
354 | |||
355 | /* does not really matter how much we write it is just to set EOF | ||
356 | * this also sets the entire COW bitmap | ||
357 | * to zero without having to allocate it | ||
358 | */ | ||
359 | err = cow_write_file(fd, &zero, sizeof(zero)); | ||
360 | if(err != sizeof(zero)){ | ||
361 | cow_printf("Write of bitmap to new COW file '%s' failed, " | ||
362 | "err = %d\n", cow_file, -err); | ||
363 | err = -EINVAL; | ||
364 | goto out; | ||
365 | } | ||
366 | |||
367 | return(0); | ||
368 | |||
369 | out: | ||
370 | return(err); | ||
371 | } | ||
372 | |||
373 | /* | ||
374 | * --------------------------------------------------------------------------- | ||
375 | * Local variables: | ||
376 | * c-file-style: "linux" | ||
377 | * End: | ||
378 | */ | ||
diff --git a/arch/um/drivers/daemon.h b/arch/um/drivers/daemon.h new file mode 100644 index 000000000000..7326c42f7ef9 --- /dev/null +++ b/arch/um/drivers/daemon.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "net_user.h" | ||
7 | |||
8 | #define SWITCH_VERSION 3 | ||
9 | |||
10 | struct daemon_data { | ||
11 | char *sock_type; | ||
12 | char *ctl_sock; | ||
13 | void *ctl_addr; | ||
14 | void *data_addr; | ||
15 | void *local_addr; | ||
16 | int fd; | ||
17 | int control; | ||
18 | void *dev; | ||
19 | }; | ||
20 | |||
21 | extern struct net_user_info daemon_user_info; | ||
22 | |||
23 | extern int daemon_user_write(int fd, void *buf, int len, | ||
24 | struct daemon_data *pri); | ||
25 | |||
26 | /* | ||
27 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
28 | * Emacs will notice this stuff at the end of the file and automatically | ||
29 | * adjust the settings for this buffer only. This must remain at the end | ||
30 | * of the file. | ||
31 | * --------------------------------------------------------------------------- | ||
32 | * Local variables: | ||
33 | * c-file-style: "linux" | ||
34 | * End: | ||
35 | */ | ||
diff --git a/arch/um/drivers/daemon_kern.c b/arch/um/drivers/daemon_kern.c new file mode 100644 index 000000000000..30d285b266af --- /dev/null +++ b/arch/um/drivers/daemon_kern.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and | ||
3 | * James Leu (jleu@mindspring.net). | ||
4 | * Copyright (C) 2001 by various other people who didn't put their name here. | ||
5 | * Licensed under the GPL. | ||
6 | */ | ||
7 | |||
8 | #include "linux/kernel.h" | ||
9 | #include "linux/init.h" | ||
10 | #include "linux/netdevice.h" | ||
11 | #include "linux/etherdevice.h" | ||
12 | #include "net_kern.h" | ||
13 | #include "net_user.h" | ||
14 | #include "daemon.h" | ||
15 | |||
16 | struct daemon_init { | ||
17 | char *sock_type; | ||
18 | char *ctl_sock; | ||
19 | }; | ||
20 | |||
21 | void daemon_init(struct net_device *dev, void *data) | ||
22 | { | ||
23 | struct uml_net_private *pri; | ||
24 | struct daemon_data *dpri; | ||
25 | struct daemon_init *init = data; | ||
26 | |||
27 | pri = dev->priv; | ||
28 | dpri = (struct daemon_data *) pri->user; | ||
29 | dpri->sock_type = init->sock_type; | ||
30 | dpri->ctl_sock = init->ctl_sock; | ||
31 | dpri->fd = -1; | ||
32 | dpri->control = -1; | ||
33 | dpri->dev = dev; | ||
34 | |||
35 | printk("daemon backend (uml_switch version %d) - %s:%s", | ||
36 | SWITCH_VERSION, dpri->sock_type, dpri->ctl_sock); | ||
37 | printk("\n"); | ||
38 | } | ||
39 | |||
40 | static int daemon_read(int fd, struct sk_buff **skb, | ||
41 | struct uml_net_private *lp) | ||
42 | { | ||
43 | *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); | ||
44 | if(*skb == NULL) return(-ENOMEM); | ||
45 | return(net_recvfrom(fd, (*skb)->mac.raw, | ||
46 | (*skb)->dev->mtu + ETH_HEADER_OTHER)); | ||
47 | } | ||
48 | |||
49 | static int daemon_write(int fd, struct sk_buff **skb, | ||
50 | struct uml_net_private *lp) | ||
51 | { | ||
52 | return(daemon_user_write(fd, (*skb)->data, (*skb)->len, | ||
53 | (struct daemon_data *) &lp->user)); | ||
54 | } | ||
55 | |||
56 | static struct net_kern_info daemon_kern_info = { | ||
57 | .init = daemon_init, | ||
58 | .protocol = eth_protocol, | ||
59 | .read = daemon_read, | ||
60 | .write = daemon_write, | ||
61 | }; | ||
62 | |||
63 | int daemon_setup(char *str, char **mac_out, void *data) | ||
64 | { | ||
65 | struct daemon_init *init = data; | ||
66 | char *remain; | ||
67 | |||
68 | *init = ((struct daemon_init) | ||
69 | { .sock_type = "unix", | ||
70 | .ctl_sock = "/tmp/uml.ctl" }); | ||
71 | |||
72 | remain = split_if_spec(str, mac_out, &init->sock_type, &init->ctl_sock, | ||
73 | NULL); | ||
74 | if(remain != NULL) | ||
75 | printk(KERN_WARNING "daemon_setup : Ignoring data socket " | ||
76 | "specification\n"); | ||
77 | |||
78 | return(1); | ||
79 | } | ||
80 | |||
81 | static struct transport daemon_transport = { | ||
82 | .list = LIST_HEAD_INIT(daemon_transport.list), | ||
83 | .name = "daemon", | ||
84 | .setup = daemon_setup, | ||
85 | .user = &daemon_user_info, | ||
86 | .kern = &daemon_kern_info, | ||
87 | .private_size = sizeof(struct daemon_data), | ||
88 | .setup_size = sizeof(struct daemon_init), | ||
89 | }; | ||
90 | |||
91 | static int register_daemon(void) | ||
92 | { | ||
93 | register_transport(&daemon_transport); | ||
94 | return(1); | ||
95 | } | ||
96 | |||
97 | __initcall(register_daemon); | ||
98 | |||
99 | /* | ||
100 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
101 | * Emacs will notice this stuff at the end of the file and automatically | ||
102 | * adjust the settings for this buffer only. This must remain at the end | ||
103 | * of the file. | ||
104 | * --------------------------------------------------------------------------- | ||
105 | * Local variables: | ||
106 | * c-file-style: "linux" | ||
107 | * End: | ||
108 | */ | ||
diff --git a/arch/um/drivers/daemon_user.c b/arch/um/drivers/daemon_user.c new file mode 100644 index 000000000000..cf15b4a8b517 --- /dev/null +++ b/arch/um/drivers/daemon_user.c | |||
@@ -0,0 +1,197 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and | ||
3 | * James Leu (jleu@mindspring.net). | ||
4 | * Copyright (C) 2001 by various other people who didn't put their name here. | ||
5 | * Licensed under the GPL. | ||
6 | */ | ||
7 | |||
8 | #include <errno.h> | ||
9 | #include <unistd.h> | ||
10 | #include <stdint.h> | ||
11 | #include <sys/socket.h> | ||
12 | #include <sys/un.h> | ||
13 | #include <sys/time.h> | ||
14 | #include "net_user.h" | ||
15 | #include "daemon.h" | ||
16 | #include "kern_util.h" | ||
17 | #include "user_util.h" | ||
18 | #include "user.h" | ||
19 | #include "os.h" | ||
20 | |||
21 | #define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER) | ||
22 | |||
23 | enum request_type { REQ_NEW_CONTROL }; | ||
24 | |||
25 | #define SWITCH_MAGIC 0xfeedface | ||
26 | |||
27 | struct request_v3 { | ||
28 | uint32_t magic; | ||
29 | uint32_t version; | ||
30 | enum request_type type; | ||
31 | struct sockaddr_un sock; | ||
32 | }; | ||
33 | |||
34 | static struct sockaddr_un *new_addr(void *name, int len) | ||
35 | { | ||
36 | struct sockaddr_un *sun; | ||
37 | |||
38 | sun = um_kmalloc(sizeof(struct sockaddr_un)); | ||
39 | if(sun == NULL){ | ||
40 | printk("new_addr: allocation of sockaddr_un failed\n"); | ||
41 | return(NULL); | ||
42 | } | ||
43 | sun->sun_family = AF_UNIX; | ||
44 | memcpy(sun->sun_path, name, len); | ||
45 | return(sun); | ||
46 | } | ||
47 | |||
48 | static int connect_to_switch(struct daemon_data *pri) | ||
49 | { | ||
50 | struct sockaddr_un *ctl_addr = pri->ctl_addr; | ||
51 | struct sockaddr_un *local_addr = pri->local_addr; | ||
52 | struct sockaddr_un *sun; | ||
53 | struct request_v3 req; | ||
54 | int fd, n, err; | ||
55 | |||
56 | pri->control = socket(AF_UNIX, SOCK_STREAM, 0); | ||
57 | if(pri->control < 0){ | ||
58 | printk("daemon_open : control socket failed, errno = %d\n", | ||
59 | errno); | ||
60 | return(-errno); | ||
61 | } | ||
62 | |||
63 | if(connect(pri->control, (struct sockaddr *) ctl_addr, | ||
64 | sizeof(*ctl_addr)) < 0){ | ||
65 | printk("daemon_open : control connect failed, errno = %d\n", | ||
66 | errno); | ||
67 | err = -errno; | ||
68 | goto out; | ||
69 | } | ||
70 | |||
71 | fd = socket(AF_UNIX, SOCK_DGRAM, 0); | ||
72 | if(fd < 0){ | ||
73 | printk("daemon_open : data socket failed, errno = %d\n", | ||
74 | errno); | ||
75 | err = -errno; | ||
76 | goto out; | ||
77 | } | ||
78 | if(bind(fd, (struct sockaddr *) local_addr, sizeof(*local_addr)) < 0){ | ||
79 | printk("daemon_open : data bind failed, errno = %d\n", | ||
80 | errno); | ||
81 | err = -errno; | ||
82 | goto out_close; | ||
83 | } | ||
84 | |||
85 | sun = um_kmalloc(sizeof(struct sockaddr_un)); | ||
86 | if(sun == NULL){ | ||
87 | printk("new_addr: allocation of sockaddr_un failed\n"); | ||
88 | err = -ENOMEM; | ||
89 | goto out_close; | ||
90 | } | ||
91 | |||
92 | req.magic = SWITCH_MAGIC; | ||
93 | req.version = SWITCH_VERSION; | ||
94 | req.type = REQ_NEW_CONTROL; | ||
95 | req.sock = *local_addr; | ||
96 | n = os_write_file(pri->control, &req, sizeof(req)); | ||
97 | if(n != sizeof(req)){ | ||
98 | printk("daemon_open : control setup request failed, err = %d\n", | ||
99 | -n); | ||
100 | err = -ENOTCONN; | ||
101 | goto out; | ||
102 | } | ||
103 | |||
104 | n = os_read_file(pri->control, sun, sizeof(*sun)); | ||
105 | if(n != sizeof(*sun)){ | ||
106 | printk("daemon_open : read of data socket failed, err = %d\n", | ||
107 | -n); | ||
108 | err = -ENOTCONN; | ||
109 | goto out_close; | ||
110 | } | ||
111 | |||
112 | pri->data_addr = sun; | ||
113 | return(fd); | ||
114 | |||
115 | out_close: | ||
116 | os_close_file(fd); | ||
117 | out: | ||
118 | os_close_file(pri->control); | ||
119 | return(err); | ||
120 | } | ||
121 | |||
122 | static void daemon_user_init(void *data, void *dev) | ||
123 | { | ||
124 | struct daemon_data *pri = data; | ||
125 | struct timeval tv; | ||
126 | struct { | ||
127 | char zero; | ||
128 | int pid; | ||
129 | int usecs; | ||
130 | } name; | ||
131 | |||
132 | if(!strcmp(pri->sock_type, "unix")) | ||
133 | pri->ctl_addr = new_addr(pri->ctl_sock, | ||
134 | strlen(pri->ctl_sock) + 1); | ||
135 | name.zero = 0; | ||
136 | name.pid = os_getpid(); | ||
137 | gettimeofday(&tv, NULL); | ||
138 | name.usecs = tv.tv_usec; | ||
139 | pri->local_addr = new_addr(&name, sizeof(name)); | ||
140 | pri->dev = dev; | ||
141 | pri->fd = connect_to_switch(pri); | ||
142 | if(pri->fd < 0){ | ||
143 | kfree(pri->local_addr); | ||
144 | pri->local_addr = NULL; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | static int daemon_open(void *data) | ||
149 | { | ||
150 | struct daemon_data *pri = data; | ||
151 | return(pri->fd); | ||
152 | } | ||
153 | |||
154 | static void daemon_remove(void *data) | ||
155 | { | ||
156 | struct daemon_data *pri = data; | ||
157 | |||
158 | os_close_file(pri->fd); | ||
159 | os_close_file(pri->control); | ||
160 | if(pri->data_addr != NULL) kfree(pri->data_addr); | ||
161 | if(pri->ctl_addr != NULL) kfree(pri->ctl_addr); | ||
162 | if(pri->local_addr != NULL) kfree(pri->local_addr); | ||
163 | } | ||
164 | |||
165 | int daemon_user_write(int fd, void *buf, int len, struct daemon_data *pri) | ||
166 | { | ||
167 | struct sockaddr_un *data_addr = pri->data_addr; | ||
168 | |||
169 | return(net_sendto(fd, buf, len, data_addr, sizeof(*data_addr))); | ||
170 | } | ||
171 | |||
172 | static int daemon_set_mtu(int mtu, void *data) | ||
173 | { | ||
174 | return(mtu); | ||
175 | } | ||
176 | |||
177 | struct net_user_info daemon_user_info = { | ||
178 | .init = daemon_user_init, | ||
179 | .open = daemon_open, | ||
180 | .close = NULL, | ||
181 | .remove = daemon_remove, | ||
182 | .set_mtu = daemon_set_mtu, | ||
183 | .add_address = NULL, | ||
184 | .delete_address = NULL, | ||
185 | .max_packet = MAX_PACKET - ETH_HEADER_OTHER | ||
186 | }; | ||
187 | |||
188 | /* | ||
189 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
190 | * Emacs will notice this stuff at the end of the file and automatically | ||
191 | * adjust the settings for this buffer only. This must remain at the end | ||
192 | * of the file. | ||
193 | * --------------------------------------------------------------------------- | ||
194 | * Local variables: | ||
195 | * c-file-style: "linux" | ||
196 | * End: | ||
197 | */ | ||
diff --git a/arch/um/drivers/fd.c b/arch/um/drivers/fd.c new file mode 100644 index 000000000000..f0b888f66e05 --- /dev/null +++ b/arch/um/drivers/fd.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <unistd.h> | ||
9 | #include <termios.h> | ||
10 | #include <errno.h> | ||
11 | #include "user.h" | ||
12 | #include "user_util.h" | ||
13 | #include "chan_user.h" | ||
14 | |||
15 | struct fd_chan { | ||
16 | int fd; | ||
17 | int raw; | ||
18 | struct termios tt; | ||
19 | char str[sizeof("1234567890\0")]; | ||
20 | }; | ||
21 | |||
22 | static void *fd_init(char *str, int device, struct chan_opts *opts) | ||
23 | { | ||
24 | struct fd_chan *data; | ||
25 | char *end; | ||
26 | int n; | ||
27 | |||
28 | if(*str != ':'){ | ||
29 | printk("fd_init : channel type 'fd' must specify a file " | ||
30 | "descriptor\n"); | ||
31 | return(NULL); | ||
32 | } | ||
33 | str++; | ||
34 | n = strtoul(str, &end, 0); | ||
35 | if((*end != '\0') || (end == str)){ | ||
36 | printk("fd_init : couldn't parse file descriptor '%s'\n", str); | ||
37 | return(NULL); | ||
38 | } | ||
39 | data = um_kmalloc(sizeof(*data)); | ||
40 | if(data == NULL) return(NULL); | ||
41 | *data = ((struct fd_chan) { .fd = n, | ||
42 | .raw = opts->raw }); | ||
43 | return(data); | ||
44 | } | ||
45 | |||
46 | static int fd_open(int input, int output, int primary, void *d, char **dev_out) | ||
47 | { | ||
48 | struct fd_chan *data = d; | ||
49 | int err; | ||
50 | |||
51 | if(data->raw && isatty(data->fd)){ | ||
52 | CATCH_EINTR(err = tcgetattr(data->fd, &data->tt)); | ||
53 | if(err) | ||
54 | return(err); | ||
55 | |||
56 | err = raw(data->fd); | ||
57 | if(err) | ||
58 | return(err); | ||
59 | } | ||
60 | sprintf(data->str, "%d", data->fd); | ||
61 | *dev_out = data->str; | ||
62 | return(data->fd); | ||
63 | } | ||
64 | |||
65 | static void fd_close(int fd, void *d) | ||
66 | { | ||
67 | struct fd_chan *data = d; | ||
68 | int err; | ||
69 | |||
70 | if(data->raw && isatty(fd)){ | ||
71 | CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &data->tt)); | ||
72 | if(err) | ||
73 | printk("Failed to restore terminal state - " | ||
74 | "errno = %d\n", -err); | ||
75 | data->raw = 0; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | static int fd_console_write(int fd, const char *buf, int n, void *d) | ||
80 | { | ||
81 | struct fd_chan *data = d; | ||
82 | |||
83 | return(generic_console_write(fd, buf, n, &data->tt)); | ||
84 | } | ||
85 | |||
86 | struct chan_ops fd_ops = { | ||
87 | .type = "fd", | ||
88 | .init = fd_init, | ||
89 | .open = fd_open, | ||
90 | .close = fd_close, | ||
91 | .read = generic_read, | ||
92 | .write = generic_write, | ||
93 | .console_write = fd_console_write, | ||
94 | .window_size = generic_window_size, | ||
95 | .free = generic_free, | ||
96 | .winch = 1, | ||
97 | }; | ||
98 | |||
99 | /* | ||
100 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
101 | * Emacs will notice this stuff at the end of the file and automatically | ||
102 | * adjust the settings for this buffer only. This must remain at the end | ||
103 | * of the file. | ||
104 | * --------------------------------------------------------------------------- | ||
105 | * Local variables: | ||
106 | * c-file-style: "linux" | ||
107 | * End: | ||
108 | */ | ||
diff --git a/arch/um/drivers/harddog_kern.c b/arch/um/drivers/harddog_kern.c new file mode 100644 index 000000000000..147ec19f6bb9 --- /dev/null +++ b/arch/um/drivers/harddog_kern.c | |||
@@ -0,0 +1,190 @@ | |||
1 | /* UML hardware watchdog, shamelessly stolen from: | ||
2 | * | ||
3 | * SoftDog 0.05: A Software Watchdog Device | ||
4 | * | ||
5 | * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved. | ||
6 | * http://www.redhat.com | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * as published by the Free Software Foundation; either version | ||
11 | * 2 of the License, or (at your option) any later version. | ||
12 | * | ||
13 | * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide | ||
14 | * warranty for any of this software. This material is provided | ||
15 | * "AS-IS" and at no charge. | ||
16 | * | ||
17 | * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> | ||
18 | * | ||
19 | * Software only watchdog driver. Unlike its big brother the WDT501P | ||
20 | * driver this won't always recover a failed machine. | ||
21 | * | ||
22 | * 03/96: Angelo Haritsis <ah@doc.ic.ac.uk> : | ||
23 | * Modularised. | ||
24 | * Added soft_margin; use upon insmod to change the timer delay. | ||
25 | * NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate | ||
26 | * minors. | ||
27 | * | ||
28 | * 19980911 Alan Cox | ||
29 | * Made SMP safe for 2.3.x | ||
30 | * | ||
31 | * 20011127 Joel Becker (jlbec@evilplan.org> | ||
32 | * Added soft_noboot; Allows testing the softdog trigger without | ||
33 | * requiring a recompile. | ||
34 | * Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT. | ||
35 | */ | ||
36 | |||
37 | #include <linux/module.h> | ||
38 | #include <linux/config.h> | ||
39 | #include <linux/types.h> | ||
40 | #include <linux/kernel.h> | ||
41 | #include <linux/fs.h> | ||
42 | #include <linux/mm.h> | ||
43 | #include <linux/miscdevice.h> | ||
44 | #include <linux/watchdog.h> | ||
45 | #include <linux/reboot.h> | ||
46 | #include <linux/smp_lock.h> | ||
47 | #include <linux/init.h> | ||
48 | #include <asm/uaccess.h> | ||
49 | #include "helper.h" | ||
50 | #include "mconsole.h" | ||
51 | |||
52 | MODULE_LICENSE("GPL"); | ||
53 | |||
54 | /* Locked by the BKL in harddog_open and harddog_release */ | ||
55 | static int timer_alive; | ||
56 | static int harddog_in_fd = -1; | ||
57 | static int harddog_out_fd = -1; | ||
58 | |||
59 | /* | ||
60 | * Allow only one person to hold it open | ||
61 | */ | ||
62 | |||
63 | extern int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock); | ||
64 | |||
65 | static int harddog_open(struct inode *inode, struct file *file) | ||
66 | { | ||
67 | int err; | ||
68 | char *sock = NULL; | ||
69 | |||
70 | lock_kernel(); | ||
71 | if(timer_alive) | ||
72 | return -EBUSY; | ||
73 | #ifdef CONFIG_HARDDOG_NOWAYOUT | ||
74 | __module_get(THIS_MODULE); | ||
75 | #endif | ||
76 | |||
77 | #ifdef CONFIG_MCONSOLE | ||
78 | sock = mconsole_notify_socket(); | ||
79 | #endif | ||
80 | err = start_watchdog(&harddog_in_fd, &harddog_out_fd, sock); | ||
81 | if(err) return(err); | ||
82 | |||
83 | timer_alive = 1; | ||
84 | unlock_kernel(); | ||
85 | return nonseekable_open(inode, file); | ||
86 | } | ||
87 | |||
88 | extern void stop_watchdog(int in_fd, int out_fd); | ||
89 | |||
90 | static int harddog_release(struct inode *inode, struct file *file) | ||
91 | { | ||
92 | /* | ||
93 | * Shut off the timer. | ||
94 | */ | ||
95 | lock_kernel(); | ||
96 | |||
97 | stop_watchdog(harddog_in_fd, harddog_out_fd); | ||
98 | harddog_in_fd = -1; | ||
99 | harddog_out_fd = -1; | ||
100 | |||
101 | timer_alive=0; | ||
102 | unlock_kernel(); | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | extern int ping_watchdog(int fd); | ||
107 | |||
108 | static ssize_t harddog_write(struct file *file, const char *data, size_t len, | ||
109 | loff_t *ppos) | ||
110 | { | ||
111 | /* | ||
112 | * Refresh the timer. | ||
113 | */ | ||
114 | if(len) | ||
115 | return(ping_watchdog(harddog_out_fd)); | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static int harddog_ioctl(struct inode *inode, struct file *file, | ||
120 | unsigned int cmd, unsigned long arg) | ||
121 | { | ||
122 | static struct watchdog_info ident = { | ||
123 | WDIOC_SETTIMEOUT, | ||
124 | 0, | ||
125 | "UML Hardware Watchdog" | ||
126 | }; | ||
127 | switch (cmd) { | ||
128 | default: | ||
129 | return -ENOTTY; | ||
130 | case WDIOC_GETSUPPORT: | ||
131 | if(copy_to_user((struct harddog_info *)arg, &ident, | ||
132 | sizeof(ident))) | ||
133 | return -EFAULT; | ||
134 | return 0; | ||
135 | case WDIOC_GETSTATUS: | ||
136 | case WDIOC_GETBOOTSTATUS: | ||
137 | return put_user(0,(int *)arg); | ||
138 | case WDIOC_KEEPALIVE: | ||
139 | return(ping_watchdog(harddog_out_fd)); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | static struct file_operations harddog_fops = { | ||
144 | .owner = THIS_MODULE, | ||
145 | .write = harddog_write, | ||
146 | .ioctl = harddog_ioctl, | ||
147 | .open = harddog_open, | ||
148 | .release = harddog_release, | ||
149 | }; | ||
150 | |||
151 | static struct miscdevice harddog_miscdev = { | ||
152 | .minor = WATCHDOG_MINOR, | ||
153 | .name = "watchdog", | ||
154 | .fops = &harddog_fops, | ||
155 | }; | ||
156 | |||
157 | static char banner[] __initdata = KERN_INFO "UML Watchdog Timer\n"; | ||
158 | |||
159 | static int __init harddog_init(void) | ||
160 | { | ||
161 | int ret; | ||
162 | |||
163 | ret = misc_register(&harddog_miscdev); | ||
164 | |||
165 | if (ret) | ||
166 | return ret; | ||
167 | |||
168 | printk(banner); | ||
169 | |||
170 | return(0); | ||
171 | } | ||
172 | |||
173 | static void __exit harddog_exit(void) | ||
174 | { | ||
175 | misc_deregister(&harddog_miscdev); | ||
176 | } | ||
177 | |||
178 | module_init(harddog_init); | ||
179 | module_exit(harddog_exit); | ||
180 | |||
181 | /* | ||
182 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
183 | * Emacs will notice this stuff at the end of the file and automatically | ||
184 | * adjust the settings for this buffer only. This must remain at the end | ||
185 | * of the file. | ||
186 | * --------------------------------------------------------------------------- | ||
187 | * Local variables: | ||
188 | * c-file-style: "linux" | ||
189 | * End: | ||
190 | */ | ||
diff --git a/arch/um/drivers/harddog_user.c b/arch/um/drivers/harddog_user.c new file mode 100644 index 000000000000..d934181b8d4c --- /dev/null +++ b/arch/um/drivers/harddog_user.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <unistd.h> | ||
8 | #include <errno.h> | ||
9 | #include "user_util.h" | ||
10 | #include "user.h" | ||
11 | #include "helper.h" | ||
12 | #include "mconsole.h" | ||
13 | #include "os.h" | ||
14 | #include "choose-mode.h" | ||
15 | #include "mode.h" | ||
16 | |||
17 | struct dog_data { | ||
18 | int stdin; | ||
19 | int stdout; | ||
20 | int close_me[2]; | ||
21 | }; | ||
22 | |||
23 | static void pre_exec(void *d) | ||
24 | { | ||
25 | struct dog_data *data = d; | ||
26 | |||
27 | dup2(data->stdin, 0); | ||
28 | dup2(data->stdout, 1); | ||
29 | dup2(data->stdout, 2); | ||
30 | os_close_file(data->stdin); | ||
31 | os_close_file(data->stdout); | ||
32 | os_close_file(data->close_me[0]); | ||
33 | os_close_file(data->close_me[1]); | ||
34 | } | ||
35 | |||
36 | int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock) | ||
37 | { | ||
38 | struct dog_data data; | ||
39 | int in_fds[2], out_fds[2], pid, n, err; | ||
40 | char pid_buf[sizeof("nnnnn\0")], c; | ||
41 | char *pid_args[] = { "/usr/bin/uml_watchdog", "-pid", pid_buf, NULL }; | ||
42 | char *mconsole_args[] = { "/usr/bin/uml_watchdog", "-mconsole", NULL, | ||
43 | NULL }; | ||
44 | char **args = NULL; | ||
45 | |||
46 | err = os_pipe(in_fds, 1, 0); | ||
47 | if(err < 0){ | ||
48 | printk("harddog_open - os_pipe failed, err = %d\n", -err); | ||
49 | goto out; | ||
50 | } | ||
51 | |||
52 | err = os_pipe(out_fds, 1, 0); | ||
53 | if(err < 0){ | ||
54 | printk("harddog_open - os_pipe failed, err = %d\n", -err); | ||
55 | goto out_close_in; | ||
56 | } | ||
57 | |||
58 | data.stdin = out_fds[0]; | ||
59 | data.stdout = in_fds[1]; | ||
60 | data.close_me[0] = out_fds[1]; | ||
61 | data.close_me[1] = in_fds[0]; | ||
62 | |||
63 | if(sock != NULL){ | ||
64 | mconsole_args[2] = sock; | ||
65 | args = mconsole_args; | ||
66 | } | ||
67 | else { | ||
68 | /* XXX The os_getpid() is not SMP correct */ | ||
69 | sprintf(pid_buf, "%d", CHOOSE_MODE(tracing_pid, os_getpid())); | ||
70 | args = pid_args; | ||
71 | } | ||
72 | |||
73 | pid = run_helper(pre_exec, &data, args, NULL); | ||
74 | |||
75 | os_close_file(out_fds[0]); | ||
76 | os_close_file(in_fds[1]); | ||
77 | |||
78 | if(pid < 0){ | ||
79 | err = -pid; | ||
80 | printk("harddog_open - run_helper failed, errno = %d\n", -err); | ||
81 | goto out_close_out; | ||
82 | } | ||
83 | |||
84 | n = os_read_file(in_fds[0], &c, sizeof(c)); | ||
85 | if(n == 0){ | ||
86 | printk("harddog_open - EOF on watchdog pipe\n"); | ||
87 | helper_wait(pid); | ||
88 | err = -EIO; | ||
89 | goto out_close_out; | ||
90 | } | ||
91 | else if(n < 0){ | ||
92 | printk("harddog_open - read of watchdog pipe failed, " | ||
93 | "err = %d\n", -n); | ||
94 | helper_wait(pid); | ||
95 | err = n; | ||
96 | goto out_close_out; | ||
97 | } | ||
98 | *in_fd_ret = in_fds[0]; | ||
99 | *out_fd_ret = out_fds[1]; | ||
100 | return(0); | ||
101 | |||
102 | out_close_in: | ||
103 | os_close_file(in_fds[0]); | ||
104 | os_close_file(in_fds[1]); | ||
105 | out_close_out: | ||
106 | os_close_file(out_fds[0]); | ||
107 | os_close_file(out_fds[1]); | ||
108 | out: | ||
109 | return(err); | ||
110 | } | ||
111 | |||
112 | void stop_watchdog(int in_fd, int out_fd) | ||
113 | { | ||
114 | os_close_file(in_fd); | ||
115 | os_close_file(out_fd); | ||
116 | } | ||
117 | |||
118 | int ping_watchdog(int fd) | ||
119 | { | ||
120 | int n; | ||
121 | char c = '\n'; | ||
122 | |||
123 | n = os_write_file(fd, &c, sizeof(c)); | ||
124 | if(n != sizeof(c)){ | ||
125 | printk("ping_watchdog - write failed, err = %d\n", -n); | ||
126 | if(n < 0) | ||
127 | return(n); | ||
128 | return(-EIO); | ||
129 | } | ||
130 | return 1; | ||
131 | |||
132 | } | ||
133 | |||
134 | /* | ||
135 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
136 | * Emacs will notice this stuff at the end of the file and automatically | ||
137 | * adjust the settings for this buffer only. This must remain at the end | ||
138 | * of the file. | ||
139 | * --------------------------------------------------------------------------- | ||
140 | * Local variables: | ||
141 | * c-file-style: "linux" | ||
142 | * End: | ||
143 | */ | ||
diff --git a/arch/um/drivers/hostaudio_kern.c b/arch/um/drivers/hostaudio_kern.c new file mode 100644 index 000000000000..d5742783e19d --- /dev/null +++ b/arch/um/drivers/hostaudio_kern.c | |||
@@ -0,0 +1,352 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Steve Schmidtke | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/config.h" | ||
7 | #include "linux/module.h" | ||
8 | #include "linux/init.h" | ||
9 | #include "linux/slab.h" | ||
10 | #include "linux/fs.h" | ||
11 | #include "linux/sound.h" | ||
12 | #include "linux/soundcard.h" | ||
13 | #include "asm/uaccess.h" | ||
14 | #include "kern_util.h" | ||
15 | #include "init.h" | ||
16 | #include "os.h" | ||
17 | |||
18 | struct hostaudio_state { | ||
19 | int fd; | ||
20 | }; | ||
21 | |||
22 | struct hostmixer_state { | ||
23 | int fd; | ||
24 | }; | ||
25 | |||
26 | #define HOSTAUDIO_DEV_DSP "/dev/sound/dsp" | ||
27 | #define HOSTAUDIO_DEV_MIXER "/dev/sound/mixer" | ||
28 | |||
29 | /* Only changed from linux_main at boot time */ | ||
30 | char *dsp = HOSTAUDIO_DEV_DSP; | ||
31 | char *mixer = HOSTAUDIO_DEV_MIXER; | ||
32 | |||
33 | #define DSP_HELP \ | ||
34 | " This is used to specify the host dsp device to the hostaudio driver.\n" \ | ||
35 | " The default is \"" HOSTAUDIO_DEV_DSP "\".\n\n" | ||
36 | |||
37 | #define MIXER_HELP \ | ||
38 | " This is used to specify the host mixer device to the hostaudio driver.\n"\ | ||
39 | " The default is \"" HOSTAUDIO_DEV_MIXER "\".\n\n" | ||
40 | |||
41 | #ifndef MODULE | ||
42 | static int set_dsp(char *name, int *add) | ||
43 | { | ||
44 | dsp = name; | ||
45 | return(0); | ||
46 | } | ||
47 | |||
48 | __uml_setup("dsp=", set_dsp, "dsp=<dsp device>\n" DSP_HELP); | ||
49 | |||
50 | static int set_mixer(char *name, int *add) | ||
51 | { | ||
52 | mixer = name; | ||
53 | return(0); | ||
54 | } | ||
55 | |||
56 | __uml_setup("mixer=", set_mixer, "mixer=<mixer device>\n" MIXER_HELP); | ||
57 | |||
58 | #else /*MODULE*/ | ||
59 | |||
60 | MODULE_PARM(dsp, "s"); | ||
61 | MODULE_PARM_DESC(dsp, DSP_HELP); | ||
62 | |||
63 | MODULE_PARM(mixer, "s"); | ||
64 | MODULE_PARM_DESC(mixer, MIXER_HELP); | ||
65 | |||
66 | #endif | ||
67 | |||
68 | /* /dev/dsp file operations */ | ||
69 | |||
70 | static ssize_t hostaudio_read(struct file *file, char *buffer, size_t count, | ||
71 | loff_t *ppos) | ||
72 | { | ||
73 | struct hostaudio_state *state = file->private_data; | ||
74 | void *kbuf; | ||
75 | int err; | ||
76 | |||
77 | #ifdef DEBUG | ||
78 | printk("hostaudio: read called, count = %d\n", count); | ||
79 | #endif | ||
80 | |||
81 | kbuf = kmalloc(count, GFP_KERNEL); | ||
82 | if(kbuf == NULL) | ||
83 | return(-ENOMEM); | ||
84 | |||
85 | err = os_read_file(state->fd, kbuf, count); | ||
86 | if(err < 0) | ||
87 | goto out; | ||
88 | |||
89 | if(copy_to_user(buffer, kbuf, err)) | ||
90 | err = -EFAULT; | ||
91 | |||
92 | out: | ||
93 | kfree(kbuf); | ||
94 | return(err); | ||
95 | } | ||
96 | |||
97 | static ssize_t hostaudio_write(struct file *file, const char *buffer, | ||
98 | size_t count, loff_t *ppos) | ||
99 | { | ||
100 | struct hostaudio_state *state = file->private_data; | ||
101 | void *kbuf; | ||
102 | int err; | ||
103 | |||
104 | #ifdef DEBUG | ||
105 | printk("hostaudio: write called, count = %d\n", count); | ||
106 | #endif | ||
107 | |||
108 | kbuf = kmalloc(count, GFP_KERNEL); | ||
109 | if(kbuf == NULL) | ||
110 | return(-ENOMEM); | ||
111 | |||
112 | err = -EFAULT; | ||
113 | if(copy_from_user(kbuf, buffer, count)) | ||
114 | goto out; | ||
115 | |||
116 | err = os_write_file(state->fd, kbuf, count); | ||
117 | if(err < 0) | ||
118 | goto out; | ||
119 | *ppos += err; | ||
120 | |||
121 | out: | ||
122 | kfree(kbuf); | ||
123 | return(err); | ||
124 | } | ||
125 | |||
126 | static unsigned int hostaudio_poll(struct file *file, | ||
127 | struct poll_table_struct *wait) | ||
128 | { | ||
129 | unsigned int mask = 0; | ||
130 | |||
131 | #ifdef DEBUG | ||
132 | printk("hostaudio: poll called (unimplemented)\n"); | ||
133 | #endif | ||
134 | |||
135 | return(mask); | ||
136 | } | ||
137 | |||
138 | static int hostaudio_ioctl(struct inode *inode, struct file *file, | ||
139 | unsigned int cmd, unsigned long arg) | ||
140 | { | ||
141 | struct hostaudio_state *state = file->private_data; | ||
142 | unsigned long data = 0; | ||
143 | int err; | ||
144 | |||
145 | #ifdef DEBUG | ||
146 | printk("hostaudio: ioctl called, cmd = %u\n", cmd); | ||
147 | #endif | ||
148 | switch(cmd){ | ||
149 | case SNDCTL_DSP_SPEED: | ||
150 | case SNDCTL_DSP_STEREO: | ||
151 | case SNDCTL_DSP_GETBLKSIZE: | ||
152 | case SNDCTL_DSP_CHANNELS: | ||
153 | case SNDCTL_DSP_SUBDIVIDE: | ||
154 | case SNDCTL_DSP_SETFRAGMENT: | ||
155 | if(get_user(data, (int *) arg)) | ||
156 | return(-EFAULT); | ||
157 | break; | ||
158 | default: | ||
159 | break; | ||
160 | } | ||
161 | |||
162 | err = os_ioctl_generic(state->fd, cmd, (unsigned long) &data); | ||
163 | |||
164 | switch(cmd){ | ||
165 | case SNDCTL_DSP_SPEED: | ||
166 | case SNDCTL_DSP_STEREO: | ||
167 | case SNDCTL_DSP_GETBLKSIZE: | ||
168 | case SNDCTL_DSP_CHANNELS: | ||
169 | case SNDCTL_DSP_SUBDIVIDE: | ||
170 | case SNDCTL_DSP_SETFRAGMENT: | ||
171 | if(put_user(data, (int *) arg)) | ||
172 | return(-EFAULT); | ||
173 | break; | ||
174 | default: | ||
175 | break; | ||
176 | } | ||
177 | |||
178 | return(err); | ||
179 | } | ||
180 | |||
181 | static int hostaudio_open(struct inode *inode, struct file *file) | ||
182 | { | ||
183 | struct hostaudio_state *state; | ||
184 | int r = 0, w = 0; | ||
185 | int ret; | ||
186 | |||
187 | #ifdef DEBUG | ||
188 | printk("hostaudio: open called (host: %s)\n", dsp); | ||
189 | #endif | ||
190 | |||
191 | state = kmalloc(sizeof(struct hostaudio_state), GFP_KERNEL); | ||
192 | if(state == NULL) | ||
193 | return(-ENOMEM); | ||
194 | |||
195 | if(file->f_mode & FMODE_READ) r = 1; | ||
196 | if(file->f_mode & FMODE_WRITE) w = 1; | ||
197 | |||
198 | ret = os_open_file(dsp, of_set_rw(OPENFLAGS(), r, w), 0); | ||
199 | if(ret < 0){ | ||
200 | kfree(state); | ||
201 | return(ret); | ||
202 | } | ||
203 | |||
204 | state->fd = ret; | ||
205 | file->private_data = state; | ||
206 | return(0); | ||
207 | } | ||
208 | |||
209 | static int hostaudio_release(struct inode *inode, struct file *file) | ||
210 | { | ||
211 | struct hostaudio_state *state = file->private_data; | ||
212 | |||
213 | #ifdef DEBUG | ||
214 | printk("hostaudio: release called\n"); | ||
215 | #endif | ||
216 | |||
217 | os_close_file(state->fd); | ||
218 | kfree(state); | ||
219 | |||
220 | return(0); | ||
221 | } | ||
222 | |||
223 | /* /dev/mixer file operations */ | ||
224 | |||
225 | static int hostmixer_ioctl_mixdev(struct inode *inode, struct file *file, | ||
226 | unsigned int cmd, unsigned long arg) | ||
227 | { | ||
228 | struct hostmixer_state *state = file->private_data; | ||
229 | |||
230 | #ifdef DEBUG | ||
231 | printk("hostmixer: ioctl called\n"); | ||
232 | #endif | ||
233 | |||
234 | return(os_ioctl_generic(state->fd, cmd, arg)); | ||
235 | } | ||
236 | |||
237 | static int hostmixer_open_mixdev(struct inode *inode, struct file *file) | ||
238 | { | ||
239 | struct hostmixer_state *state; | ||
240 | int r = 0, w = 0; | ||
241 | int ret; | ||
242 | |||
243 | #ifdef DEBUG | ||
244 | printk("hostmixer: open called (host: %s)\n", mixer); | ||
245 | #endif | ||
246 | |||
247 | state = kmalloc(sizeof(struct hostmixer_state), GFP_KERNEL); | ||
248 | if(state == NULL) return(-ENOMEM); | ||
249 | |||
250 | if(file->f_mode & FMODE_READ) r = 1; | ||
251 | if(file->f_mode & FMODE_WRITE) w = 1; | ||
252 | |||
253 | ret = os_open_file(mixer, of_set_rw(OPENFLAGS(), r, w), 0); | ||
254 | |||
255 | if(ret < 0){ | ||
256 | printk("hostaudio_open_mixdev failed to open '%s', err = %d\n", | ||
257 | dsp, -ret); | ||
258 | kfree(state); | ||
259 | return(ret); | ||
260 | } | ||
261 | |||
262 | file->private_data = state; | ||
263 | return(0); | ||
264 | } | ||
265 | |||
266 | static int hostmixer_release(struct inode *inode, struct file *file) | ||
267 | { | ||
268 | struct hostmixer_state *state = file->private_data; | ||
269 | |||
270 | #ifdef DEBUG | ||
271 | printk("hostmixer: release called\n"); | ||
272 | #endif | ||
273 | |||
274 | os_close_file(state->fd); | ||
275 | kfree(state); | ||
276 | |||
277 | return(0); | ||
278 | } | ||
279 | |||
280 | |||
281 | /* kernel module operations */ | ||
282 | |||
283 | static struct file_operations hostaudio_fops = { | ||
284 | .owner = THIS_MODULE, | ||
285 | .llseek = no_llseek, | ||
286 | .read = hostaudio_read, | ||
287 | .write = hostaudio_write, | ||
288 | .poll = hostaudio_poll, | ||
289 | .ioctl = hostaudio_ioctl, | ||
290 | .mmap = NULL, | ||
291 | .open = hostaudio_open, | ||
292 | .release = hostaudio_release, | ||
293 | }; | ||
294 | |||
295 | static struct file_operations hostmixer_fops = { | ||
296 | .owner = THIS_MODULE, | ||
297 | .llseek = no_llseek, | ||
298 | .ioctl = hostmixer_ioctl_mixdev, | ||
299 | .open = hostmixer_open_mixdev, | ||
300 | .release = hostmixer_release, | ||
301 | }; | ||
302 | |||
303 | struct { | ||
304 | int dev_audio; | ||
305 | int dev_mixer; | ||
306 | } module_data; | ||
307 | |||
308 | MODULE_AUTHOR("Steve Schmidtke"); | ||
309 | MODULE_DESCRIPTION("UML Audio Relay"); | ||
310 | MODULE_LICENSE("GPL"); | ||
311 | |||
312 | static int __init hostaudio_init_module(void) | ||
313 | { | ||
314 | printk(KERN_INFO "UML Audio Relay (host dsp = %s, host mixer = %s)\n", | ||
315 | dsp, mixer); | ||
316 | |||
317 | module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1); | ||
318 | if(module_data.dev_audio < 0){ | ||
319 | printk(KERN_ERR "hostaudio: couldn't register DSP device!\n"); | ||
320 | return -ENODEV; | ||
321 | } | ||
322 | |||
323 | module_data.dev_mixer = register_sound_mixer(&hostmixer_fops, -1); | ||
324 | if(module_data.dev_mixer < 0){ | ||
325 | printk(KERN_ERR "hostmixer: couldn't register mixer " | ||
326 | "device!\n"); | ||
327 | unregister_sound_dsp(module_data.dev_audio); | ||
328 | return -ENODEV; | ||
329 | } | ||
330 | |||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static void __exit hostaudio_cleanup_module (void) | ||
335 | { | ||
336 | unregister_sound_mixer(module_data.dev_mixer); | ||
337 | unregister_sound_dsp(module_data.dev_audio); | ||
338 | } | ||
339 | |||
340 | module_init(hostaudio_init_module); | ||
341 | module_exit(hostaudio_cleanup_module); | ||
342 | |||
343 | /* | ||
344 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
345 | * Emacs will notice this stuff at the end of the file and automatically | ||
346 | * adjust the settings for this buffer only. This must remain at the end | ||
347 | * of the file. | ||
348 | * --------------------------------------------------------------------------- | ||
349 | * Local variables: | ||
350 | * c-file-style: "linux" | ||
351 | * End: | ||
352 | */ | ||
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c new file mode 100644 index 000000000000..6924f273ced9 --- /dev/null +++ b/arch/um/drivers/line.c | |||
@@ -0,0 +1,681 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/sched.h" | ||
7 | #include "linux/slab.h" | ||
8 | #include "linux/list.h" | ||
9 | #include "linux/kd.h" | ||
10 | #include "linux/interrupt.h" | ||
11 | #include "linux/devfs_fs_kernel.h" | ||
12 | #include "asm/uaccess.h" | ||
13 | #include "chan_kern.h" | ||
14 | #include "irq_user.h" | ||
15 | #include "line.h" | ||
16 | #include "kern.h" | ||
17 | #include "user_util.h" | ||
18 | #include "kern_util.h" | ||
19 | #include "os.h" | ||
20 | #include "irq_kern.h" | ||
21 | |||
22 | #define LINE_BUFSIZE 4096 | ||
23 | |||
24 | static irqreturn_t line_interrupt(int irq, void *data, struct pt_regs *unused) | ||
25 | { | ||
26 | struct tty_struct *tty = data; | ||
27 | struct line *line = tty->driver_data; | ||
28 | |||
29 | if (line) | ||
30 | chan_interrupt(&line->chan_list, &line->task, tty, irq); | ||
31 | return IRQ_HANDLED; | ||
32 | } | ||
33 | |||
34 | static void line_timer_cb(void *arg) | ||
35 | { | ||
36 | struct tty_struct *tty = arg; | ||
37 | struct line *line = tty->driver_data; | ||
38 | |||
39 | line_interrupt(line->driver->read_irq, arg, NULL); | ||
40 | } | ||
41 | |||
42 | static int write_room(struct line *dev) | ||
43 | { | ||
44 | int n; | ||
45 | |||
46 | if (dev->buffer == NULL) | ||
47 | return (LINE_BUFSIZE - 1); | ||
48 | |||
49 | n = dev->head - dev->tail; | ||
50 | if (n <= 0) | ||
51 | n = LINE_BUFSIZE + n; | ||
52 | return (n - 1); | ||
53 | } | ||
54 | |||
55 | static int buffer_data(struct line *line, const char *buf, int len) | ||
56 | { | ||
57 | int end, room; | ||
58 | |||
59 | if(line->buffer == NULL){ | ||
60 | line->buffer = kmalloc(LINE_BUFSIZE, GFP_ATOMIC); | ||
61 | if (line->buffer == NULL) { | ||
62 | printk("buffer_data - atomic allocation failed\n"); | ||
63 | return(0); | ||
64 | } | ||
65 | line->head = line->buffer; | ||
66 | line->tail = line->buffer; | ||
67 | } | ||
68 | |||
69 | room = write_room(line); | ||
70 | len = (len > room) ? room : len; | ||
71 | |||
72 | end = line->buffer + LINE_BUFSIZE - line->tail; | ||
73 | if(len < end){ | ||
74 | memcpy(line->tail, buf, len); | ||
75 | line->tail += len; | ||
76 | } | ||
77 | else { | ||
78 | memcpy(line->tail, buf, end); | ||
79 | buf += end; | ||
80 | memcpy(line->buffer, buf, len - end); | ||
81 | line->tail = line->buffer + len - end; | ||
82 | } | ||
83 | |||
84 | return(len); | ||
85 | } | ||
86 | |||
87 | static int flush_buffer(struct line *line) | ||
88 | { | ||
89 | int n, count; | ||
90 | |||
91 | if ((line->buffer == NULL) || (line->head == line->tail)) | ||
92 | return(1); | ||
93 | |||
94 | if (line->tail < line->head) { | ||
95 | count = line->buffer + LINE_BUFSIZE - line->head; | ||
96 | n = write_chan(&line->chan_list, line->head, count, | ||
97 | line->driver->write_irq); | ||
98 | if (n < 0) | ||
99 | return(n); | ||
100 | if (n == count) | ||
101 | line->head = line->buffer; | ||
102 | else { | ||
103 | line->head += n; | ||
104 | return(0); | ||
105 | } | ||
106 | } | ||
107 | |||
108 | count = line->tail - line->head; | ||
109 | n = write_chan(&line->chan_list, line->head, count, | ||
110 | line->driver->write_irq); | ||
111 | if(n < 0) return(n); | ||
112 | |||
113 | line->head += n; | ||
114 | return(line->head == line->tail); | ||
115 | } | ||
116 | |||
117 | int line_write(struct tty_struct *tty, const unsigned char *buf, int len) | ||
118 | { | ||
119 | struct line *line = tty->driver_data; | ||
120 | unsigned long flags; | ||
121 | int n, err, ret = 0; | ||
122 | |||
123 | if(tty->stopped) return 0; | ||
124 | |||
125 | down(&line->sem); | ||
126 | if(line->head != line->tail){ | ||
127 | local_irq_save(flags); | ||
128 | ret = buffer_data(line, buf, len); | ||
129 | err = flush_buffer(line); | ||
130 | local_irq_restore(flags); | ||
131 | if(err <= 0 && (err != -EAGAIN || !ret)) | ||
132 | ret = err; | ||
133 | } | ||
134 | else { | ||
135 | n = write_chan(&line->chan_list, buf, len, | ||
136 | line->driver->write_irq); | ||
137 | if(n < 0){ | ||
138 | ret = n; | ||
139 | goto out_up; | ||
140 | } | ||
141 | |||
142 | len -= n; | ||
143 | ret += n; | ||
144 | if(len > 0) | ||
145 | ret += buffer_data(line, buf + n, len); | ||
146 | } | ||
147 | out_up: | ||
148 | up(&line->sem); | ||
149 | return(ret); | ||
150 | } | ||
151 | |||
152 | void line_put_char(struct tty_struct *tty, unsigned char ch) | ||
153 | { | ||
154 | line_write(tty, &ch, sizeof(ch)); | ||
155 | } | ||
156 | |||
157 | void line_set_termios(struct tty_struct *tty, struct termios * old) | ||
158 | { | ||
159 | /* nothing */ | ||
160 | } | ||
161 | |||
162 | int line_chars_in_buffer(struct tty_struct *tty) | ||
163 | { | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static struct { | ||
168 | int cmd; | ||
169 | char *level; | ||
170 | char *name; | ||
171 | } tty_ioctls[] = { | ||
172 | /* don't print these, they flood the log ... */ | ||
173 | { TCGETS, NULL, "TCGETS" }, | ||
174 | { TCSETS, NULL, "TCSETS" }, | ||
175 | { TCSETSW, NULL, "TCSETSW" }, | ||
176 | { TCFLSH, NULL, "TCFLSH" }, | ||
177 | { TCSBRK, NULL, "TCSBRK" }, | ||
178 | |||
179 | /* general tty stuff */ | ||
180 | { TCSETSF, KERN_DEBUG, "TCSETSF" }, | ||
181 | { TCGETA, KERN_DEBUG, "TCGETA" }, | ||
182 | { TIOCMGET, KERN_DEBUG, "TIOCMGET" }, | ||
183 | { TCSBRKP, KERN_DEBUG, "TCSBRKP" }, | ||
184 | { TIOCMSET, KERN_DEBUG, "TIOCMSET" }, | ||
185 | |||
186 | /* linux-specific ones */ | ||
187 | { TIOCLINUX, KERN_INFO, "TIOCLINUX" }, | ||
188 | { KDGKBMODE, KERN_INFO, "KDGKBMODE" }, | ||
189 | { KDGKBTYPE, KERN_INFO, "KDGKBTYPE" }, | ||
190 | { KDSIGACCEPT, KERN_INFO, "KDSIGACCEPT" }, | ||
191 | }; | ||
192 | |||
193 | int line_ioctl(struct tty_struct *tty, struct file * file, | ||
194 | unsigned int cmd, unsigned long arg) | ||
195 | { | ||
196 | int ret; | ||
197 | int i; | ||
198 | |||
199 | ret = 0; | ||
200 | switch(cmd) { | ||
201 | #ifdef TIOCGETP | ||
202 | case TIOCGETP: | ||
203 | case TIOCSETP: | ||
204 | case TIOCSETN: | ||
205 | #endif | ||
206 | #ifdef TIOCGETC | ||
207 | case TIOCGETC: | ||
208 | case TIOCSETC: | ||
209 | #endif | ||
210 | #ifdef TIOCGLTC | ||
211 | case TIOCGLTC: | ||
212 | case TIOCSLTC: | ||
213 | #endif | ||
214 | case TCGETS: | ||
215 | case TCSETSF: | ||
216 | case TCSETSW: | ||
217 | case TCSETS: | ||
218 | case TCGETA: | ||
219 | case TCSETAF: | ||
220 | case TCSETAW: | ||
221 | case TCSETA: | ||
222 | case TCXONC: | ||
223 | case TCFLSH: | ||
224 | case TIOCOUTQ: | ||
225 | case TIOCINQ: | ||
226 | case TIOCGLCKTRMIOS: | ||
227 | case TIOCSLCKTRMIOS: | ||
228 | case TIOCPKT: | ||
229 | case TIOCGSOFTCAR: | ||
230 | case TIOCSSOFTCAR: | ||
231 | return -ENOIOCTLCMD; | ||
232 | #if 0 | ||
233 | case TCwhatever: | ||
234 | /* do something */ | ||
235 | break; | ||
236 | #endif | ||
237 | default: | ||
238 | for (i = 0; i < ARRAY_SIZE(tty_ioctls); i++) | ||
239 | if (cmd == tty_ioctls[i].cmd) | ||
240 | break; | ||
241 | if (i < ARRAY_SIZE(tty_ioctls)) { | ||
242 | if (NULL != tty_ioctls[i].level) | ||
243 | printk("%s%s: %s: ioctl %s called\n", | ||
244 | tty_ioctls[i].level, __FUNCTION__, | ||
245 | tty->name, tty_ioctls[i].name); | ||
246 | } else { | ||
247 | printk(KERN_ERR "%s: %s: unknown ioctl: 0x%x\n", | ||
248 | __FUNCTION__, tty->name, cmd); | ||
249 | } | ||
250 | ret = -ENOIOCTLCMD; | ||
251 | break; | ||
252 | } | ||
253 | return(ret); | ||
254 | } | ||
255 | |||
256 | static irqreturn_t line_write_interrupt(int irq, void *data, | ||
257 | struct pt_regs *unused) | ||
258 | { | ||
259 | struct tty_struct *tty = data; | ||
260 | struct line *line = tty->driver_data; | ||
261 | int err; | ||
262 | |||
263 | err = flush_buffer(line); | ||
264 | if(err == 0) | ||
265 | return(IRQ_NONE); | ||
266 | else if(err < 0){ | ||
267 | line->head = line->buffer; | ||
268 | line->tail = line->buffer; | ||
269 | } | ||
270 | |||
271 | if(tty == NULL) | ||
272 | return(IRQ_NONE); | ||
273 | |||
274 | if(test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && | ||
275 | (tty->ldisc.write_wakeup != NULL)) | ||
276 | (tty->ldisc.write_wakeup)(tty); | ||
277 | |||
278 | /* BLOCKING mode | ||
279 | * In blocking mode, everything sleeps on tty->write_wait. | ||
280 | * Sleeping in the console driver would break non-blocking | ||
281 | * writes. | ||
282 | */ | ||
283 | |||
284 | if(waitqueue_active(&tty->write_wait)) | ||
285 | wake_up_interruptible(&tty->write_wait); | ||
286 | return(IRQ_HANDLED); | ||
287 | } | ||
288 | |||
289 | int line_setup_irq(int fd, int input, int output, struct tty_struct *tty) | ||
290 | { | ||
291 | struct line *line = tty->driver_data; | ||
292 | struct line_driver *driver = line->driver; | ||
293 | int err = 0, flags = SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM; | ||
294 | |||
295 | if(input) err = um_request_irq(driver->read_irq, fd, IRQ_READ, | ||
296 | line_interrupt, flags, | ||
297 | driver->read_irq_name, tty); | ||
298 | if(err) return(err); | ||
299 | if(output) err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, | ||
300 | line_write_interrupt, flags, | ||
301 | driver->write_irq_name, tty); | ||
302 | line->have_irq = 1; | ||
303 | return(err); | ||
304 | } | ||
305 | |||
306 | void line_disable(struct tty_struct *tty, int current_irq) | ||
307 | { | ||
308 | struct line *line = tty->driver_data; | ||
309 | |||
310 | if(!line->have_irq) | ||
311 | return; | ||
312 | |||
313 | if(line->driver->read_irq == current_irq) | ||
314 | free_irq_later(line->driver->read_irq, tty); | ||
315 | else { | ||
316 | free_irq_by_irq_and_dev(line->driver->read_irq, tty); | ||
317 | free_irq(line->driver->read_irq, tty); | ||
318 | } | ||
319 | |||
320 | if(line->driver->write_irq == current_irq) | ||
321 | free_irq_later(line->driver->write_irq, tty); | ||
322 | else { | ||
323 | free_irq_by_irq_and_dev(line->driver->write_irq, tty); | ||
324 | free_irq(line->driver->write_irq, tty); | ||
325 | } | ||
326 | |||
327 | line->have_irq = 0; | ||
328 | } | ||
329 | |||
330 | int line_open(struct line *lines, struct tty_struct *tty, | ||
331 | struct chan_opts *opts) | ||
332 | { | ||
333 | struct line *line; | ||
334 | int err = 0; | ||
335 | |||
336 | line = &lines[tty->index]; | ||
337 | tty->driver_data = line; | ||
338 | |||
339 | down(&line->sem); | ||
340 | if (tty->count == 1) { | ||
341 | if (!line->valid) { | ||
342 | err = -ENODEV; | ||
343 | goto out; | ||
344 | } | ||
345 | if (list_empty(&line->chan_list)) { | ||
346 | err = parse_chan_pair(line->init_str, &line->chan_list, | ||
347 | line->init_pri, tty->index, opts); | ||
348 | if(err) goto out; | ||
349 | err = open_chan(&line->chan_list); | ||
350 | if(err) goto out; | ||
351 | } | ||
352 | enable_chan(&line->chan_list, tty); | ||
353 | INIT_WORK(&line->task, line_timer_cb, tty); | ||
354 | } | ||
355 | |||
356 | if(!line->sigio){ | ||
357 | chan_enable_winch(&line->chan_list, tty); | ||
358 | line->sigio = 1; | ||
359 | } | ||
360 | chan_window_size(&line->chan_list, &tty->winsize.ws_row, | ||
361 | &tty->winsize.ws_col); | ||
362 | line->count++; | ||
363 | |||
364 | out: | ||
365 | up(&line->sem); | ||
366 | return(err); | ||
367 | } | ||
368 | |||
369 | void line_close(struct tty_struct *tty, struct file * filp) | ||
370 | { | ||
371 | struct line *line = tty->driver_data; | ||
372 | |||
373 | down(&line->sem); | ||
374 | line->count--; | ||
375 | if (tty->count == 1) { | ||
376 | line_disable(tty, -1); | ||
377 | tty->driver_data = NULL; | ||
378 | } | ||
379 | up(&line->sem); | ||
380 | } | ||
381 | |||
382 | void close_lines(struct line *lines, int nlines) | ||
383 | { | ||
384 | int i; | ||
385 | |||
386 | for(i = 0; i < nlines; i++) | ||
387 | close_chan(&lines[i].chan_list); | ||
388 | } | ||
389 | |||
390 | int line_setup(struct line *lines, int num, char *init, int all_allowed) | ||
391 | { | ||
392 | int i, n; | ||
393 | char *end; | ||
394 | |||
395 | if(*init == '=') n = -1; | ||
396 | else { | ||
397 | n = simple_strtoul(init, &end, 0); | ||
398 | if(*end != '='){ | ||
399 | printk(KERN_ERR "line_setup failed to parse \"%s\"\n", | ||
400 | init); | ||
401 | return(0); | ||
402 | } | ||
403 | init = end; | ||
404 | } | ||
405 | init++; | ||
406 | if((n >= 0) && (n >= num)){ | ||
407 | printk("line_setup - %d out of range ((0 ... %d) allowed)\n", | ||
408 | n, num - 1); | ||
409 | return(0); | ||
410 | } | ||
411 | else if (n >= 0){ | ||
412 | if (lines[n].count > 0) { | ||
413 | printk("line_setup - device %d is open\n", n); | ||
414 | return(0); | ||
415 | } | ||
416 | if (lines[n].init_pri <= INIT_ONE){ | ||
417 | lines[n].init_pri = INIT_ONE; | ||
418 | if (!strcmp(init, "none")) | ||
419 | lines[n].valid = 0; | ||
420 | else { | ||
421 | lines[n].init_str = init; | ||
422 | lines[n].valid = 1; | ||
423 | } | ||
424 | } | ||
425 | } | ||
426 | else if(!all_allowed){ | ||
427 | printk("line_setup - can't configure all devices from " | ||
428 | "mconsole\n"); | ||
429 | return(0); | ||
430 | } | ||
431 | else { | ||
432 | for(i = 0; i < num; i++){ | ||
433 | if(lines[i].init_pri <= INIT_ALL){ | ||
434 | lines[i].init_pri = INIT_ALL; | ||
435 | if(!strcmp(init, "none")) lines[i].valid = 0; | ||
436 | else { | ||
437 | lines[i].init_str = init; | ||
438 | lines[i].valid = 1; | ||
439 | } | ||
440 | } | ||
441 | } | ||
442 | } | ||
443 | return(1); | ||
444 | } | ||
445 | |||
446 | int line_config(struct line *lines, int num, char *str) | ||
447 | { | ||
448 | char *new = uml_strdup(str); | ||
449 | |||
450 | if(new == NULL){ | ||
451 | printk("line_config - uml_strdup failed\n"); | ||
452 | return(-ENOMEM); | ||
453 | } | ||
454 | return(!line_setup(lines, num, new, 0)); | ||
455 | } | ||
456 | |||
457 | int line_get_config(char *name, struct line *lines, int num, char *str, | ||
458 | int size, char **error_out) | ||
459 | { | ||
460 | struct line *line; | ||
461 | char *end; | ||
462 | int dev, n = 0; | ||
463 | |||
464 | dev = simple_strtoul(name, &end, 0); | ||
465 | if((*end != '\0') || (end == name)){ | ||
466 | *error_out = "line_get_config failed to parse device number"; | ||
467 | return(0); | ||
468 | } | ||
469 | |||
470 | if((dev < 0) || (dev >= num)){ | ||
471 | *error_out = "device number of of range"; | ||
472 | return(0); | ||
473 | } | ||
474 | |||
475 | line = &lines[dev]; | ||
476 | |||
477 | down(&line->sem); | ||
478 | if(!line->valid) | ||
479 | CONFIG_CHUNK(str, size, n, "none", 1); | ||
480 | else if(line->count == 0) | ||
481 | CONFIG_CHUNK(str, size, n, line->init_str, 1); | ||
482 | else n = chan_config_string(&line->chan_list, str, size, error_out); | ||
483 | up(&line->sem); | ||
484 | |||
485 | return(n); | ||
486 | } | ||
487 | |||
488 | int line_remove(struct line *lines, int num, char *str) | ||
489 | { | ||
490 | char config[sizeof("conxxxx=none\0")]; | ||
491 | |||
492 | sprintf(config, "%s=none", str); | ||
493 | return(!line_setup(lines, num, config, 0)); | ||
494 | } | ||
495 | |||
496 | int line_write_room(struct tty_struct *tty) | ||
497 | { | ||
498 | struct line *dev = tty->driver_data; | ||
499 | int room; | ||
500 | |||
501 | if (tty->stopped) | ||
502 | return 0; | ||
503 | room = write_room(dev); | ||
504 | if (0 == room) | ||
505 | printk(KERN_DEBUG "%s: %s: no room left in buffer\n", | ||
506 | __FUNCTION__,tty->name); | ||
507 | return room; | ||
508 | } | ||
509 | |||
510 | struct tty_driver *line_register_devfs(struct lines *set, | ||
511 | struct line_driver *line_driver, | ||
512 | struct tty_operations *ops, struct line *lines, | ||
513 | int nlines) | ||
514 | { | ||
515 | int i; | ||
516 | struct tty_driver *driver = alloc_tty_driver(nlines); | ||
517 | |||
518 | if (!driver) | ||
519 | return NULL; | ||
520 | |||
521 | driver->driver_name = line_driver->name; | ||
522 | driver->name = line_driver->device_name; | ||
523 | driver->devfs_name = line_driver->devfs_name; | ||
524 | driver->major = line_driver->major; | ||
525 | driver->minor_start = line_driver->minor_start; | ||
526 | driver->type = line_driver->type; | ||
527 | driver->subtype = line_driver->subtype; | ||
528 | driver->flags = TTY_DRIVER_REAL_RAW; | ||
529 | driver->init_termios = tty_std_termios; | ||
530 | tty_set_operations(driver, ops); | ||
531 | |||
532 | if (tty_register_driver(driver)) { | ||
533 | printk("%s: can't register %s driver\n", | ||
534 | __FUNCTION__,line_driver->name); | ||
535 | put_tty_driver(driver); | ||
536 | return NULL; | ||
537 | } | ||
538 | |||
539 | for(i = 0; i < nlines; i++){ | ||
540 | if(!lines[i].valid) | ||
541 | tty_unregister_device(driver, i); | ||
542 | } | ||
543 | |||
544 | mconsole_register_dev(&line_driver->mc); | ||
545 | return driver; | ||
546 | } | ||
547 | |||
548 | void lines_init(struct line *lines, int nlines) | ||
549 | { | ||
550 | struct line *line; | ||
551 | int i; | ||
552 | |||
553 | for(i = 0; i < nlines; i++){ | ||
554 | line = &lines[i]; | ||
555 | INIT_LIST_HEAD(&line->chan_list); | ||
556 | sema_init(&line->sem, 1); | ||
557 | if(line->init_str != NULL){ | ||
558 | line->init_str = uml_strdup(line->init_str); | ||
559 | if(line->init_str == NULL) | ||
560 | printk("lines_init - uml_strdup returned " | ||
561 | "NULL\n"); | ||
562 | } | ||
563 | } | ||
564 | } | ||
565 | |||
566 | struct winch { | ||
567 | struct list_head list; | ||
568 | int fd; | ||
569 | int tty_fd; | ||
570 | int pid; | ||
571 | struct tty_struct *tty; | ||
572 | }; | ||
573 | |||
574 | irqreturn_t winch_interrupt(int irq, void *data, struct pt_regs *unused) | ||
575 | { | ||
576 | struct winch *winch = data; | ||
577 | struct tty_struct *tty; | ||
578 | struct line *line; | ||
579 | int err; | ||
580 | char c; | ||
581 | |||
582 | if(winch->fd != -1){ | ||
583 | err = generic_read(winch->fd, &c, NULL); | ||
584 | if(err < 0){ | ||
585 | if(err != -EAGAIN){ | ||
586 | printk("winch_interrupt : read failed, " | ||
587 | "errno = %d\n", -err); | ||
588 | printk("fd %d is losing SIGWINCH support\n", | ||
589 | winch->tty_fd); | ||
590 | return(IRQ_HANDLED); | ||
591 | } | ||
592 | goto out; | ||
593 | } | ||
594 | } | ||
595 | tty = winch->tty; | ||
596 | if (tty != NULL) { | ||
597 | line = tty->driver_data; | ||
598 | chan_window_size(&line->chan_list, | ||
599 | &tty->winsize.ws_row, | ||
600 | &tty->winsize.ws_col); | ||
601 | kill_pg(tty->pgrp, SIGWINCH, 1); | ||
602 | } | ||
603 | out: | ||
604 | if(winch->fd != -1) | ||
605 | reactivate_fd(winch->fd, WINCH_IRQ); | ||
606 | return(IRQ_HANDLED); | ||
607 | } | ||
608 | |||
609 | DECLARE_MUTEX(winch_handler_sem); | ||
610 | LIST_HEAD(winch_handlers); | ||
611 | |||
612 | void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty) | ||
613 | { | ||
614 | struct winch *winch; | ||
615 | |||
616 | down(&winch_handler_sem); | ||
617 | winch = kmalloc(sizeof(*winch), GFP_KERNEL); | ||
618 | if (winch == NULL) { | ||
619 | printk("register_winch_irq - kmalloc failed\n"); | ||
620 | goto out; | ||
621 | } | ||
622 | *winch = ((struct winch) { .list = LIST_HEAD_INIT(winch->list), | ||
623 | .fd = fd, | ||
624 | .tty_fd = tty_fd, | ||
625 | .pid = pid, | ||
626 | .tty = tty }); | ||
627 | list_add(&winch->list, &winch_handlers); | ||
628 | if(um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, | ||
629 | SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, | ||
630 | "winch", winch) < 0) | ||
631 | printk("register_winch_irq - failed to register IRQ\n"); | ||
632 | out: | ||
633 | up(&winch_handler_sem); | ||
634 | } | ||
635 | |||
636 | static void winch_cleanup(void) | ||
637 | { | ||
638 | struct list_head *ele; | ||
639 | struct winch *winch; | ||
640 | |||
641 | list_for_each(ele, &winch_handlers){ | ||
642 | winch = list_entry(ele, struct winch, list); | ||
643 | if(winch->fd != -1){ | ||
644 | deactivate_fd(winch->fd, WINCH_IRQ); | ||
645 | os_close_file(winch->fd); | ||
646 | } | ||
647 | if(winch->pid != -1) | ||
648 | os_kill_process(winch->pid, 1); | ||
649 | } | ||
650 | } | ||
651 | __uml_exitcall(winch_cleanup); | ||
652 | |||
653 | char *add_xterm_umid(char *base) | ||
654 | { | ||
655 | char *umid, *title; | ||
656 | int len; | ||
657 | |||
658 | umid = get_umid(1); | ||
659 | if(umid == NULL) return(base); | ||
660 | |||
661 | len = strlen(base) + strlen(" ()") + strlen(umid) + 1; | ||
662 | title = kmalloc(len, GFP_KERNEL); | ||
663 | if(title == NULL){ | ||
664 | printk("Failed to allocate buffer for xterm title\n"); | ||
665 | return(base); | ||
666 | } | ||
667 | |||
668 | snprintf(title, len, "%s (%s)", base, umid); | ||
669 | return(title); | ||
670 | } | ||
671 | |||
672 | /* | ||
673 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
674 | * Emacs will notice this stuff at the end of the file and automatically | ||
675 | * adjust the settings for this buffer only. This must remain at the end | ||
676 | * of the file. | ||
677 | * --------------------------------------------------------------------------- | ||
678 | * Local variables: | ||
679 | * c-file-style: "linux" | ||
680 | * End: | ||
681 | */ | ||
diff --git a/arch/um/drivers/mcast.h b/arch/um/drivers/mcast.h new file mode 100644 index 000000000000..a2c6db243458 --- /dev/null +++ b/arch/um/drivers/mcast.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "net_user.h" | ||
7 | |||
8 | struct mcast_data { | ||
9 | char *addr; | ||
10 | unsigned short port; | ||
11 | void *mcast_addr; | ||
12 | int ttl; | ||
13 | void *dev; | ||
14 | }; | ||
15 | |||
16 | extern struct net_user_info mcast_user_info; | ||
17 | |||
18 | extern int mcast_user_write(int fd, void *buf, int len, | ||
19 | struct mcast_data *pri); | ||
20 | |||
21 | /* | ||
22 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
23 | * Emacs will notice this stuff at the end of the file and automatically | ||
24 | * adjust the settings for this buffer only. This must remain at the end | ||
25 | * of the file. | ||
26 | * --------------------------------------------------------------------------- | ||
27 | * Local variables: | ||
28 | * c-file-style: "linux" | ||
29 | * End: | ||
30 | */ | ||
diff --git a/arch/um/drivers/mcast_kern.c b/arch/um/drivers/mcast_kern.c new file mode 100644 index 000000000000..faf714e87b5b --- /dev/null +++ b/arch/um/drivers/mcast_kern.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * user-mode-linux networking multicast transport | ||
3 | * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org> | ||
4 | * | ||
5 | * based on the existing uml-networking code, which is | ||
6 | * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and | ||
7 | * James Leu (jleu@mindspring.net). | ||
8 | * Copyright (C) 2001 by various other people who didn't put their name here. | ||
9 | * | ||
10 | * Licensed under the GPL. | ||
11 | */ | ||
12 | |||
13 | #include "linux/kernel.h" | ||
14 | #include "linux/init.h" | ||
15 | #include "linux/netdevice.h" | ||
16 | #include "linux/etherdevice.h" | ||
17 | #include "linux/in.h" | ||
18 | #include "linux/inet.h" | ||
19 | #include "net_kern.h" | ||
20 | #include "net_user.h" | ||
21 | #include "mcast.h" | ||
22 | |||
23 | struct mcast_init { | ||
24 | char *addr; | ||
25 | int port; | ||
26 | int ttl; | ||
27 | }; | ||
28 | |||
29 | void mcast_init(struct net_device *dev, void *data) | ||
30 | { | ||
31 | struct uml_net_private *pri; | ||
32 | struct mcast_data *dpri; | ||
33 | struct mcast_init *init = data; | ||
34 | |||
35 | pri = dev->priv; | ||
36 | dpri = (struct mcast_data *) pri->user; | ||
37 | dpri->addr = init->addr; | ||
38 | dpri->port = init->port; | ||
39 | dpri->ttl = init->ttl; | ||
40 | dpri->dev = dev; | ||
41 | |||
42 | printk("mcast backend "); | ||
43 | printk("multicast adddress: %s:%u, TTL:%u ", | ||
44 | dpri->addr, dpri->port, dpri->ttl); | ||
45 | |||
46 | printk("\n"); | ||
47 | } | ||
48 | |||
49 | static int mcast_read(int fd, struct sk_buff **skb, struct uml_net_private *lp) | ||
50 | { | ||
51 | *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); | ||
52 | if(*skb == NULL) return(-ENOMEM); | ||
53 | return(net_recvfrom(fd, (*skb)->mac.raw, | ||
54 | (*skb)->dev->mtu + ETH_HEADER_OTHER)); | ||
55 | } | ||
56 | |||
57 | static int mcast_write(int fd, struct sk_buff **skb, | ||
58 | struct uml_net_private *lp) | ||
59 | { | ||
60 | return mcast_user_write(fd, (*skb)->data, (*skb)->len, | ||
61 | (struct mcast_data *) &lp->user); | ||
62 | } | ||
63 | |||
64 | static struct net_kern_info mcast_kern_info = { | ||
65 | .init = mcast_init, | ||
66 | .protocol = eth_protocol, | ||
67 | .read = mcast_read, | ||
68 | .write = mcast_write, | ||
69 | }; | ||
70 | |||
71 | int mcast_setup(char *str, char **mac_out, void *data) | ||
72 | { | ||
73 | struct mcast_init *init = data; | ||
74 | char *port_str = NULL, *ttl_str = NULL, *remain; | ||
75 | char *last; | ||
76 | int n; | ||
77 | |||
78 | *init = ((struct mcast_init) | ||
79 | { .addr = "239.192.168.1", | ||
80 | .port = 1102, | ||
81 | .ttl = 1 }); | ||
82 | |||
83 | remain = split_if_spec(str, mac_out, &init->addr, &port_str, &ttl_str, | ||
84 | NULL); | ||
85 | if(remain != NULL){ | ||
86 | printk(KERN_ERR "mcast_setup - Extra garbage on " | ||
87 | "specification : '%s'\n", remain); | ||
88 | return(0); | ||
89 | } | ||
90 | |||
91 | if(port_str != NULL){ | ||
92 | n = simple_strtoul(port_str, &last, 10); | ||
93 | if((*last != '\0') || (last == port_str)){ | ||
94 | printk(KERN_ERR "mcast_setup - Bad port : '%s'\n", | ||
95 | port_str); | ||
96 | return(0); | ||
97 | } | ||
98 | init->port = htons(n); | ||
99 | } | ||
100 | |||
101 | if(ttl_str != NULL){ | ||
102 | init->ttl = simple_strtoul(ttl_str, &last, 10); | ||
103 | if((*last != '\0') || (last == ttl_str)){ | ||
104 | printk(KERN_ERR "mcast_setup - Bad ttl : '%s'\n", | ||
105 | ttl_str); | ||
106 | return(0); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | printk(KERN_INFO "Configured mcast device: %s:%u-%u\n", init->addr, | ||
111 | init->port, init->ttl); | ||
112 | |||
113 | return(1); | ||
114 | } | ||
115 | |||
116 | static struct transport mcast_transport = { | ||
117 | .list = LIST_HEAD_INIT(mcast_transport.list), | ||
118 | .name = "mcast", | ||
119 | .setup = mcast_setup, | ||
120 | .user = &mcast_user_info, | ||
121 | .kern = &mcast_kern_info, | ||
122 | .private_size = sizeof(struct mcast_data), | ||
123 | .setup_size = sizeof(struct mcast_init), | ||
124 | }; | ||
125 | |||
126 | static int register_mcast(void) | ||
127 | { | ||
128 | register_transport(&mcast_transport); | ||
129 | return(1); | ||
130 | } | ||
131 | |||
132 | __initcall(register_mcast); | ||
133 | |||
134 | /* | ||
135 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
136 | * Emacs will notice this stuff at the end of the file and automatically | ||
137 | * adjust the settings for this buffer only. This must remain at the end | ||
138 | * of the file. | ||
139 | * --------------------------------------------------------------------------- | ||
140 | * Local variables: | ||
141 | * c-file-style: "linux" | ||
142 | * End: | ||
143 | */ | ||
diff --git a/arch/um/drivers/mcast_user.c b/arch/um/drivers/mcast_user.c new file mode 100644 index 000000000000..0fe1d9fa9139 --- /dev/null +++ b/arch/um/drivers/mcast_user.c | |||
@@ -0,0 +1,177 @@ | |||
1 | /* | ||
2 | * user-mode-linux networking multicast transport | ||
3 | * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org> | ||
4 | * | ||
5 | * based on the existing uml-networking code, which is | ||
6 | * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and | ||
7 | * James Leu (jleu@mindspring.net). | ||
8 | * Copyright (C) 2001 by various other people who didn't put their name here. | ||
9 | * | ||
10 | * Licensed under the GPL. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <errno.h> | ||
15 | #include <unistd.h> | ||
16 | #include <linux/inet.h> | ||
17 | #include <sys/socket.h> | ||
18 | #include <sys/un.h> | ||
19 | #include <sys/time.h> | ||
20 | #include <netinet/in.h> | ||
21 | #include "net_user.h" | ||
22 | #include "mcast.h" | ||
23 | #include "kern_util.h" | ||
24 | #include "user_util.h" | ||
25 | #include "user.h" | ||
26 | #include "os.h" | ||
27 | |||
28 | #define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER) | ||
29 | |||
30 | static struct sockaddr_in *new_addr(char *addr, unsigned short port) | ||
31 | { | ||
32 | struct sockaddr_in *sin; | ||
33 | |||
34 | sin = um_kmalloc(sizeof(struct sockaddr_in)); | ||
35 | if(sin == NULL){ | ||
36 | printk("new_addr: allocation of sockaddr_in failed\n"); | ||
37 | return(NULL); | ||
38 | } | ||
39 | sin->sin_family = AF_INET; | ||
40 | sin->sin_addr.s_addr = in_aton(addr); | ||
41 | sin->sin_port = port; | ||
42 | return(sin); | ||
43 | } | ||
44 | |||
45 | static void mcast_user_init(void *data, void *dev) | ||
46 | { | ||
47 | struct mcast_data *pri = data; | ||
48 | |||
49 | pri->mcast_addr = new_addr(pri->addr, pri->port); | ||
50 | pri->dev = dev; | ||
51 | } | ||
52 | |||
53 | static int mcast_open(void *data) | ||
54 | { | ||
55 | struct mcast_data *pri = data; | ||
56 | struct sockaddr_in *sin = pri->mcast_addr; | ||
57 | struct ip_mreq mreq; | ||
58 | int fd, yes = 1; | ||
59 | |||
60 | |||
61 | if ((sin->sin_addr.s_addr == 0) || (sin->sin_port == 0)) { | ||
62 | fd = -EINVAL; | ||
63 | goto out; | ||
64 | } | ||
65 | |||
66 | fd = socket(AF_INET, SOCK_DGRAM, 0); | ||
67 | if (fd < 0){ | ||
68 | printk("mcast_open : data socket failed, errno = %d\n", | ||
69 | errno); | ||
70 | fd = -ENOMEM; | ||
71 | goto out; | ||
72 | } | ||
73 | |||
74 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { | ||
75 | printk("mcast_open: SO_REUSEADDR failed, errno = %d\n", | ||
76 | errno); | ||
77 | os_close_file(fd); | ||
78 | fd = -EINVAL; | ||
79 | goto out; | ||
80 | } | ||
81 | |||
82 | /* set ttl according to config */ | ||
83 | if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl, | ||
84 | sizeof(pri->ttl)) < 0) { | ||
85 | printk("mcast_open: IP_MULTICAST_TTL failed, error = %d\n", | ||
86 | errno); | ||
87 | os_close_file(fd); | ||
88 | fd = -EINVAL; | ||
89 | goto out; | ||
90 | } | ||
91 | |||
92 | /* set LOOP, so data does get fed back to local sockets */ | ||
93 | if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { | ||
94 | printk("mcast_open: IP_MULTICAST_LOOP failed, error = %d\n", | ||
95 | errno); | ||
96 | os_close_file(fd); | ||
97 | fd = -EINVAL; | ||
98 | goto out; | ||
99 | } | ||
100 | |||
101 | /* bind socket to mcast address */ | ||
102 | if (bind(fd, (struct sockaddr *) sin, sizeof(*sin)) < 0) { | ||
103 | printk("mcast_open : data bind failed, errno = %d\n", errno); | ||
104 | os_close_file(fd); | ||
105 | fd = -EINVAL; | ||
106 | goto out; | ||
107 | } | ||
108 | |||
109 | /* subscribe to the multicast group */ | ||
110 | mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr; | ||
111 | mreq.imr_interface.s_addr = 0; | ||
112 | if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, | ||
113 | &mreq, sizeof(mreq)) < 0) { | ||
114 | printk("mcast_open: IP_ADD_MEMBERSHIP failed, error = %d\n", | ||
115 | errno); | ||
116 | printk("There appears not to be a multicast-capable network " | ||
117 | "interface on the host.\n"); | ||
118 | printk("eth0 should be configured in order to use the " | ||
119 | "multicast transport.\n"); | ||
120 | os_close_file(fd); | ||
121 | fd = -EINVAL; | ||
122 | } | ||
123 | |||
124 | out: | ||
125 | return(fd); | ||
126 | } | ||
127 | |||
128 | static void mcast_close(int fd, void *data) | ||
129 | { | ||
130 | struct ip_mreq mreq; | ||
131 | struct mcast_data *pri = data; | ||
132 | struct sockaddr_in *sin = pri->mcast_addr; | ||
133 | |||
134 | mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr; | ||
135 | mreq.imr_interface.s_addr = 0; | ||
136 | if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP, | ||
137 | &mreq, sizeof(mreq)) < 0) { | ||
138 | printk("mcast_open: IP_DROP_MEMBERSHIP failed, error = %d\n", | ||
139 | errno); | ||
140 | } | ||
141 | |||
142 | os_close_file(fd); | ||
143 | } | ||
144 | |||
145 | int mcast_user_write(int fd, void *buf, int len, struct mcast_data *pri) | ||
146 | { | ||
147 | struct sockaddr_in *data_addr = pri->mcast_addr; | ||
148 | |||
149 | return(net_sendto(fd, buf, len, data_addr, sizeof(*data_addr))); | ||
150 | } | ||
151 | |||
152 | static int mcast_set_mtu(int mtu, void *data) | ||
153 | { | ||
154 | return(mtu); | ||
155 | } | ||
156 | |||
157 | struct net_user_info mcast_user_info = { | ||
158 | .init = mcast_user_init, | ||
159 | .open = mcast_open, | ||
160 | .close = mcast_close, | ||
161 | .remove = NULL, | ||
162 | .set_mtu = mcast_set_mtu, | ||
163 | .add_address = NULL, | ||
164 | .delete_address = NULL, | ||
165 | .max_packet = MAX_PACKET - ETH_HEADER_OTHER | ||
166 | }; | ||
167 | |||
168 | /* | ||
169 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
170 | * Emacs will notice this stuff at the end of the file and automatically | ||
171 | * adjust the settings for this buffer only. This must remain at the end | ||
172 | * of the file. | ||
173 | * --------------------------------------------------------------------------- | ||
174 | * Local variables: | ||
175 | * c-file-style: "linux" | ||
176 | * End: | ||
177 | */ | ||
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c new file mode 100644 index 000000000000..d7c7adcc0a67 --- /dev/null +++ b/arch/um/drivers/mconsole_kern.c | |||
@@ -0,0 +1,619 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) | ||
3 | * Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com) | ||
4 | * Licensed under the GPL | ||
5 | */ | ||
6 | |||
7 | #include "linux/kernel.h" | ||
8 | #include "linux/slab.h" | ||
9 | #include "linux/init.h" | ||
10 | #include "linux/notifier.h" | ||
11 | #include "linux/reboot.h" | ||
12 | #include "linux/utsname.h" | ||
13 | #include "linux/ctype.h" | ||
14 | #include "linux/interrupt.h" | ||
15 | #include "linux/sysrq.h" | ||
16 | #include "linux/workqueue.h" | ||
17 | #include "linux/module.h" | ||
18 | #include "linux/file.h" | ||
19 | #include "linux/fs.h" | ||
20 | #include "linux/namei.h" | ||
21 | #include "linux/proc_fs.h" | ||
22 | #include "linux/syscalls.h" | ||
23 | #include "asm/irq.h" | ||
24 | #include "asm/uaccess.h" | ||
25 | #include "user_util.h" | ||
26 | #include "kern_util.h" | ||
27 | #include "kern.h" | ||
28 | #include "mconsole.h" | ||
29 | #include "mconsole_kern.h" | ||
30 | #include "irq_user.h" | ||
31 | #include "init.h" | ||
32 | #include "os.h" | ||
33 | #include "umid.h" | ||
34 | #include "irq_kern.h" | ||
35 | |||
36 | static int do_unlink_socket(struct notifier_block *notifier, | ||
37 | unsigned long what, void *data) | ||
38 | { | ||
39 | return(mconsole_unlink_socket()); | ||
40 | } | ||
41 | |||
42 | |||
43 | static struct notifier_block reboot_notifier = { | ||
44 | .notifier_call = do_unlink_socket, | ||
45 | .priority = 0, | ||
46 | }; | ||
47 | |||
48 | /* Safe without explicit locking for now. Tasklets provide their own | ||
49 | * locking, and the interrupt handler is safe because it can't interrupt | ||
50 | * itself and it can only happen on CPU 0. | ||
51 | */ | ||
52 | |||
53 | LIST_HEAD(mc_requests); | ||
54 | |||
55 | static void mc_work_proc(void *unused) | ||
56 | { | ||
57 | struct mconsole_entry *req; | ||
58 | unsigned long flags; | ||
59 | |||
60 | while(!list_empty(&mc_requests)){ | ||
61 | local_save_flags(flags); | ||
62 | req = list_entry(mc_requests.next, struct mconsole_entry, | ||
63 | list); | ||
64 | list_del(&req->list); | ||
65 | local_irq_restore(flags); | ||
66 | req->request.cmd->handler(&req->request); | ||
67 | kfree(req); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | DECLARE_WORK(mconsole_work, mc_work_proc, NULL); | ||
72 | |||
73 | static irqreturn_t mconsole_interrupt(int irq, void *dev_id, | ||
74 | struct pt_regs *regs) | ||
75 | { | ||
76 | /* long to avoid size mismatch warnings from gcc */ | ||
77 | long fd; | ||
78 | struct mconsole_entry *new; | ||
79 | struct mc_request req; | ||
80 | |||
81 | fd = (long) dev_id; | ||
82 | while (mconsole_get_request(fd, &req)){ | ||
83 | if(req.cmd->context == MCONSOLE_INTR) | ||
84 | (*req.cmd->handler)(&req); | ||
85 | else { | ||
86 | new = kmalloc(sizeof(*new), GFP_ATOMIC); | ||
87 | if(new == NULL) | ||
88 | mconsole_reply(&req, "Out of memory", 1, 0); | ||
89 | else { | ||
90 | new->request = req; | ||
91 | list_add(&new->list, &mc_requests); | ||
92 | } | ||
93 | } | ||
94 | } | ||
95 | if(!list_empty(&mc_requests)) | ||
96 | schedule_work(&mconsole_work); | ||
97 | reactivate_fd(fd, MCONSOLE_IRQ); | ||
98 | return(IRQ_HANDLED); | ||
99 | } | ||
100 | |||
101 | void mconsole_version(struct mc_request *req) | ||
102 | { | ||
103 | char version[256]; | ||
104 | |||
105 | sprintf(version, "%s %s %s %s %s", system_utsname.sysname, | ||
106 | system_utsname.nodename, system_utsname.release, | ||
107 | system_utsname.version, system_utsname.machine); | ||
108 | mconsole_reply(req, version, 0, 0); | ||
109 | } | ||
110 | |||
111 | void mconsole_log(struct mc_request *req) | ||
112 | { | ||
113 | int len; | ||
114 | char *ptr = req->request.data; | ||
115 | |||
116 | ptr += strlen("log "); | ||
117 | |||
118 | len = req->len - (ptr - req->request.data); | ||
119 | printk("%.*s", len, ptr); | ||
120 | mconsole_reply(req, "", 0, 0); | ||
121 | } | ||
122 | |||
123 | /* This is a more convoluted version of mconsole_proc, which has some stability | ||
124 | * problems; however, we need it fixed, because it is expected that UML users | ||
125 | * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still | ||
126 | * show the real procfs content, not the ones from hppfs.*/ | ||
127 | #if 0 | ||
128 | void mconsole_proc(struct mc_request *req) | ||
129 | { | ||
130 | struct nameidata nd; | ||
131 | struct file_system_type *proc; | ||
132 | struct super_block *super; | ||
133 | struct file *file; | ||
134 | int n, err; | ||
135 | char *ptr = req->request.data, *buf; | ||
136 | |||
137 | ptr += strlen("proc"); | ||
138 | while(isspace(*ptr)) ptr++; | ||
139 | |||
140 | proc = get_fs_type("proc"); | ||
141 | if(proc == NULL){ | ||
142 | mconsole_reply(req, "procfs not registered", 1, 0); | ||
143 | goto out; | ||
144 | } | ||
145 | |||
146 | super = (*proc->get_sb)(proc, 0, NULL, NULL); | ||
147 | put_filesystem(proc); | ||
148 | if(super == NULL){ | ||
149 | mconsole_reply(req, "Failed to get procfs superblock", 1, 0); | ||
150 | goto out; | ||
151 | } | ||
152 | up_write(&super->s_umount); | ||
153 | |||
154 | nd.dentry = super->s_root; | ||
155 | nd.mnt = NULL; | ||
156 | nd.flags = O_RDONLY + 1; | ||
157 | nd.last_type = LAST_ROOT; | ||
158 | |||
159 | /* START: it was experienced that the stability problems are closed | ||
160 | * if commenting out these two calls + the below read cycle. To | ||
161 | * make UML crash again, it was enough to readd either one.*/ | ||
162 | err = link_path_walk(ptr, &nd); | ||
163 | if(err){ | ||
164 | mconsole_reply(req, "Failed to look up file", 1, 0); | ||
165 | goto out_kill; | ||
166 | } | ||
167 | |||
168 | file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); | ||
169 | if(IS_ERR(file)){ | ||
170 | mconsole_reply(req, "Failed to open file", 1, 0); | ||
171 | goto out_kill; | ||
172 | } | ||
173 | /*END*/ | ||
174 | |||
175 | buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
176 | if(buf == NULL){ | ||
177 | mconsole_reply(req, "Failed to allocate buffer", 1, 0); | ||
178 | goto out_fput; | ||
179 | } | ||
180 | |||
181 | if((file->f_op != NULL) && (file->f_op->read != NULL)){ | ||
182 | do { | ||
183 | n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1, | ||
184 | &file->f_pos); | ||
185 | if(n >= 0){ | ||
186 | buf[n] = '\0'; | ||
187 | mconsole_reply(req, buf, 0, (n > 0)); | ||
188 | } | ||
189 | else { | ||
190 | mconsole_reply(req, "Read of file failed", | ||
191 | 1, 0); | ||
192 | goto out_free; | ||
193 | } | ||
194 | } while(n > 0); | ||
195 | } | ||
196 | else mconsole_reply(req, "", 0, 0); | ||
197 | |||
198 | out_free: | ||
199 | kfree(buf); | ||
200 | out_fput: | ||
201 | fput(file); | ||
202 | out_kill: | ||
203 | deactivate_super(super); | ||
204 | out: ; | ||
205 | } | ||
206 | #endif | ||
207 | |||
208 | void mconsole_proc(struct mc_request *req) | ||
209 | { | ||
210 | char path[64]; | ||
211 | char *buf; | ||
212 | int len; | ||
213 | int fd; | ||
214 | int first_chunk = 1; | ||
215 | char *ptr = req->request.data; | ||
216 | |||
217 | ptr += strlen("proc"); | ||
218 | while(isspace(*ptr)) ptr++; | ||
219 | snprintf(path, sizeof(path), "/proc/%s", ptr); | ||
220 | |||
221 | fd = sys_open(path, 0, 0); | ||
222 | if (fd < 0) { | ||
223 | mconsole_reply(req, "Failed to open file", 1, 0); | ||
224 | printk("open %s: %d\n",path,fd); | ||
225 | goto out; | ||
226 | } | ||
227 | |||
228 | buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
229 | if(buf == NULL){ | ||
230 | mconsole_reply(req, "Failed to allocate buffer", 1, 0); | ||
231 | goto out_close; | ||
232 | } | ||
233 | |||
234 | for (;;) { | ||
235 | len = sys_read(fd, buf, PAGE_SIZE-1); | ||
236 | if (len < 0) { | ||
237 | mconsole_reply(req, "Read of file failed", 1, 0); | ||
238 | goto out_free; | ||
239 | } | ||
240 | /*Begin the file content on his own line.*/ | ||
241 | if (first_chunk) { | ||
242 | mconsole_reply(req, "\n", 0, 1); | ||
243 | first_chunk = 0; | ||
244 | } | ||
245 | if (len == PAGE_SIZE-1) { | ||
246 | buf[len] = '\0'; | ||
247 | mconsole_reply(req, buf, 0, 1); | ||
248 | } else { | ||
249 | buf[len] = '\0'; | ||
250 | mconsole_reply(req, buf, 0, 0); | ||
251 | break; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | out_free: | ||
256 | kfree(buf); | ||
257 | out_close: | ||
258 | sys_close(fd); | ||
259 | out: | ||
260 | /* nothing */; | ||
261 | } | ||
262 | |||
263 | #define UML_MCONSOLE_HELPTEXT \ | ||
264 | "Commands: \n\ | ||
265 | version - Get kernel version \n\ | ||
266 | help - Print this message \n\ | ||
267 | halt - Halt UML \n\ | ||
268 | reboot - Reboot UML \n\ | ||
269 | config <dev>=<config> - Add a new device to UML; \n\ | ||
270 | same syntax as command line \n\ | ||
271 | config <dev> - Query the configuration of a device \n\ | ||
272 | remove <dev> - Remove a device from UML \n\ | ||
273 | sysrq <letter> - Performs the SysRq action controlled by the letter \n\ | ||
274 | cad - invoke the Ctl-Alt-Del handler \n\ | ||
275 | stop - pause the UML; it will do nothing until it receives a 'go' \n\ | ||
276 | go - continue the UML after a 'stop' \n\ | ||
277 | log <string> - make UML enter <string> into the kernel log\n\ | ||
278 | proc <file> - returns the contents of the UML's /proc/<file>\n\ | ||
279 | " | ||
280 | |||
281 | void mconsole_help(struct mc_request *req) | ||
282 | { | ||
283 | mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0); | ||
284 | } | ||
285 | |||
286 | void mconsole_halt(struct mc_request *req) | ||
287 | { | ||
288 | mconsole_reply(req, "", 0, 0); | ||
289 | machine_halt(); | ||
290 | } | ||
291 | |||
292 | void mconsole_reboot(struct mc_request *req) | ||
293 | { | ||
294 | mconsole_reply(req, "", 0, 0); | ||
295 | machine_restart(NULL); | ||
296 | } | ||
297 | |||
298 | extern void ctrl_alt_del(void); | ||
299 | |||
300 | void mconsole_cad(struct mc_request *req) | ||
301 | { | ||
302 | mconsole_reply(req, "", 0, 0); | ||
303 | ctrl_alt_del(); | ||
304 | } | ||
305 | |||
306 | void mconsole_go(struct mc_request *req) | ||
307 | { | ||
308 | mconsole_reply(req, "Not stopped", 1, 0); | ||
309 | } | ||
310 | |||
311 | void mconsole_stop(struct mc_request *req) | ||
312 | { | ||
313 | deactivate_fd(req->originating_fd, MCONSOLE_IRQ); | ||
314 | os_set_fd_block(req->originating_fd, 1); | ||
315 | mconsole_reply(req, "", 0, 0); | ||
316 | while(mconsole_get_request(req->originating_fd, req)){ | ||
317 | if(req->cmd->handler == mconsole_go) break; | ||
318 | (*req->cmd->handler)(req); | ||
319 | } | ||
320 | os_set_fd_block(req->originating_fd, 0); | ||
321 | reactivate_fd(req->originating_fd, MCONSOLE_IRQ); | ||
322 | mconsole_reply(req, "", 0, 0); | ||
323 | } | ||
324 | |||
325 | /* This list is populated by __initcall routines. */ | ||
326 | |||
327 | LIST_HEAD(mconsole_devices); | ||
328 | |||
329 | void mconsole_register_dev(struct mc_device *new) | ||
330 | { | ||
331 | list_add(&new->list, &mconsole_devices); | ||
332 | } | ||
333 | |||
334 | static struct mc_device *mconsole_find_dev(char *name) | ||
335 | { | ||
336 | struct list_head *ele; | ||
337 | struct mc_device *dev; | ||
338 | |||
339 | list_for_each(ele, &mconsole_devices){ | ||
340 | dev = list_entry(ele, struct mc_device, list); | ||
341 | if(!strncmp(name, dev->name, strlen(dev->name))) | ||
342 | return(dev); | ||
343 | } | ||
344 | return(NULL); | ||
345 | } | ||
346 | |||
347 | #define CONFIG_BUF_SIZE 64 | ||
348 | |||
349 | static void mconsole_get_config(int (*get_config)(char *, char *, int, | ||
350 | char **), | ||
351 | struct mc_request *req, char *name) | ||
352 | { | ||
353 | char default_buf[CONFIG_BUF_SIZE], *error, *buf; | ||
354 | int n, size; | ||
355 | |||
356 | if(get_config == NULL){ | ||
357 | mconsole_reply(req, "No get_config routine defined", 1, 0); | ||
358 | return; | ||
359 | } | ||
360 | |||
361 | error = NULL; | ||
362 | size = sizeof(default_buf)/sizeof(default_buf[0]); | ||
363 | buf = default_buf; | ||
364 | |||
365 | while(1){ | ||
366 | n = (*get_config)(name, buf, size, &error); | ||
367 | if(error != NULL){ | ||
368 | mconsole_reply(req, error, 1, 0); | ||
369 | goto out; | ||
370 | } | ||
371 | |||
372 | if(n <= size){ | ||
373 | mconsole_reply(req, buf, 0, 0); | ||
374 | goto out; | ||
375 | } | ||
376 | |||
377 | if(buf != default_buf) | ||
378 | kfree(buf); | ||
379 | |||
380 | size = n; | ||
381 | buf = kmalloc(size, GFP_KERNEL); | ||
382 | if(buf == NULL){ | ||
383 | mconsole_reply(req, "Failed to allocate buffer", 1, 0); | ||
384 | return; | ||
385 | } | ||
386 | } | ||
387 | out: | ||
388 | if(buf != default_buf) | ||
389 | kfree(buf); | ||
390 | |||
391 | } | ||
392 | |||
393 | void mconsole_config(struct mc_request *req) | ||
394 | { | ||
395 | struct mc_device *dev; | ||
396 | char *ptr = req->request.data, *name; | ||
397 | int err; | ||
398 | |||
399 | ptr += strlen("config"); | ||
400 | while(isspace(*ptr)) ptr++; | ||
401 | dev = mconsole_find_dev(ptr); | ||
402 | if(dev == NULL){ | ||
403 | mconsole_reply(req, "Bad configuration option", 1, 0); | ||
404 | return; | ||
405 | } | ||
406 | |||
407 | name = &ptr[strlen(dev->name)]; | ||
408 | ptr = name; | ||
409 | while((*ptr != '=') && (*ptr != '\0')) | ||
410 | ptr++; | ||
411 | |||
412 | if(*ptr == '='){ | ||
413 | err = (*dev->config)(name); | ||
414 | mconsole_reply(req, "", err, 0); | ||
415 | } | ||
416 | else mconsole_get_config(dev->get_config, req, name); | ||
417 | } | ||
418 | |||
419 | void mconsole_remove(struct mc_request *req) | ||
420 | { | ||
421 | struct mc_device *dev; | ||
422 | char *ptr = req->request.data; | ||
423 | int err; | ||
424 | |||
425 | ptr += strlen("remove"); | ||
426 | while(isspace(*ptr)) ptr++; | ||
427 | dev = mconsole_find_dev(ptr); | ||
428 | if(dev == NULL){ | ||
429 | mconsole_reply(req, "Bad remove option", 1, 0); | ||
430 | return; | ||
431 | } | ||
432 | err = (*dev->remove)(&ptr[strlen(dev->name)]); | ||
433 | mconsole_reply(req, "", err, 0); | ||
434 | } | ||
435 | |||
436 | #ifdef CONFIG_MAGIC_SYSRQ | ||
437 | void mconsole_sysrq(struct mc_request *req) | ||
438 | { | ||
439 | char *ptr = req->request.data; | ||
440 | |||
441 | ptr += strlen("sysrq"); | ||
442 | while(isspace(*ptr)) ptr++; | ||
443 | |||
444 | mconsole_reply(req, "", 0, 0); | ||
445 | handle_sysrq(*ptr, ¤t->thread.regs, NULL); | ||
446 | } | ||
447 | #else | ||
448 | void mconsole_sysrq(struct mc_request *req) | ||
449 | { | ||
450 | mconsole_reply(req, "Sysrq not compiled in", 1, 0); | ||
451 | } | ||
452 | #endif | ||
453 | |||
454 | /* Changed by mconsole_setup, which is __setup, and called before SMP is | ||
455 | * active. | ||
456 | */ | ||
457 | static char *notify_socket = NULL; | ||
458 | |||
459 | int mconsole_init(void) | ||
460 | { | ||
461 | /* long to avoid size mismatch warnings from gcc */ | ||
462 | long sock; | ||
463 | int err; | ||
464 | char file[256]; | ||
465 | |||
466 | if(umid_file_name("mconsole", file, sizeof(file))) return(-1); | ||
467 | snprintf(mconsole_socket_name, sizeof(file), "%s", file); | ||
468 | |||
469 | sock = os_create_unix_socket(file, sizeof(file), 1); | ||
470 | if (sock < 0){ | ||
471 | printk("Failed to initialize management console\n"); | ||
472 | return(1); | ||
473 | } | ||
474 | |||
475 | register_reboot_notifier(&reboot_notifier); | ||
476 | |||
477 | err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt, | ||
478 | SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, | ||
479 | "mconsole", (void *)sock); | ||
480 | if (err){ | ||
481 | printk("Failed to get IRQ for management console\n"); | ||
482 | return(1); | ||
483 | } | ||
484 | |||
485 | if(notify_socket != NULL){ | ||
486 | notify_socket = uml_strdup(notify_socket); | ||
487 | if(notify_socket != NULL) | ||
488 | mconsole_notify(notify_socket, MCONSOLE_SOCKET, | ||
489 | mconsole_socket_name, | ||
490 | strlen(mconsole_socket_name) + 1); | ||
491 | else printk(KERN_ERR "mconsole_setup failed to strdup " | ||
492 | "string\n"); | ||
493 | } | ||
494 | |||
495 | printk("mconsole (version %d) initialized on %s\n", | ||
496 | MCONSOLE_VERSION, mconsole_socket_name); | ||
497 | return(0); | ||
498 | } | ||
499 | |||
500 | __initcall(mconsole_init); | ||
501 | |||
502 | static int write_proc_mconsole(struct file *file, const char __user *buffer, | ||
503 | unsigned long count, void *data) | ||
504 | { | ||
505 | char *buf; | ||
506 | |||
507 | buf = kmalloc(count + 1, GFP_KERNEL); | ||
508 | if(buf == NULL) | ||
509 | return(-ENOMEM); | ||
510 | |||
511 | if(copy_from_user(buf, buffer, count)){ | ||
512 | count = -EFAULT; | ||
513 | goto out; | ||
514 | } | ||
515 | |||
516 | buf[count] = '\0'; | ||
517 | |||
518 | mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count); | ||
519 | out: | ||
520 | kfree(buf); | ||
521 | return(count); | ||
522 | } | ||
523 | |||
524 | static int create_proc_mconsole(void) | ||
525 | { | ||
526 | struct proc_dir_entry *ent; | ||
527 | |||
528 | if(notify_socket == NULL) return(0); | ||
529 | |||
530 | ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL); | ||
531 | if(ent == NULL){ | ||
532 | printk("create_proc_mconsole : create_proc_entry failed\n"); | ||
533 | return(0); | ||
534 | } | ||
535 | |||
536 | ent->read_proc = NULL; | ||
537 | ent->write_proc = write_proc_mconsole; | ||
538 | return(0); | ||
539 | } | ||
540 | |||
541 | static DEFINE_SPINLOCK(notify_spinlock); | ||
542 | |||
543 | void lock_notify(void) | ||
544 | { | ||
545 | spin_lock(¬ify_spinlock); | ||
546 | } | ||
547 | |||
548 | void unlock_notify(void) | ||
549 | { | ||
550 | spin_unlock(¬ify_spinlock); | ||
551 | } | ||
552 | |||
553 | __initcall(create_proc_mconsole); | ||
554 | |||
555 | #define NOTIFY "=notify:" | ||
556 | |||
557 | static int mconsole_setup(char *str) | ||
558 | { | ||
559 | if(!strncmp(str, NOTIFY, strlen(NOTIFY))){ | ||
560 | str += strlen(NOTIFY); | ||
561 | notify_socket = str; | ||
562 | } | ||
563 | else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str); | ||
564 | return(1); | ||
565 | } | ||
566 | |||
567 | __setup("mconsole", mconsole_setup); | ||
568 | |||
569 | __uml_help(mconsole_setup, | ||
570 | "mconsole=notify:<socket>\n" | ||
571 | " Requests that the mconsole driver send a message to the named Unix\n" | ||
572 | " socket containing the name of the mconsole socket. This also serves\n" | ||
573 | " to notify outside processes when UML has booted far enough to respond\n" | ||
574 | " to mconsole requests.\n\n" | ||
575 | ); | ||
576 | |||
577 | static int notify_panic(struct notifier_block *self, unsigned long unused1, | ||
578 | void *ptr) | ||
579 | { | ||
580 | char *message = ptr; | ||
581 | |||
582 | if(notify_socket == NULL) return(0); | ||
583 | |||
584 | mconsole_notify(notify_socket, MCONSOLE_PANIC, message, | ||
585 | strlen(message) + 1); | ||
586 | return(0); | ||
587 | } | ||
588 | |||
589 | static struct notifier_block panic_exit_notifier = { | ||
590 | .notifier_call = notify_panic, | ||
591 | .next = NULL, | ||
592 | .priority = 1 | ||
593 | }; | ||
594 | |||
595 | static int add_notifier(void) | ||
596 | { | ||
597 | notifier_chain_register(&panic_notifier_list, &panic_exit_notifier); | ||
598 | return(0); | ||
599 | } | ||
600 | |||
601 | __initcall(add_notifier); | ||
602 | |||
603 | char *mconsole_notify_socket(void) | ||
604 | { | ||
605 | return(notify_socket); | ||
606 | } | ||
607 | |||
608 | EXPORT_SYMBOL(mconsole_notify_socket); | ||
609 | |||
610 | /* | ||
611 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
612 | * Emacs will notice this stuff at the end of the file and automatically | ||
613 | * adjust the settings for this buffer only. This must remain at the end | ||
614 | * of the file. | ||
615 | * --------------------------------------------------------------------------- | ||
616 | * Local variables: | ||
617 | * c-file-style: "linux" | ||
618 | * End: | ||
619 | */ | ||
diff --git a/arch/um/drivers/mconsole_user.c b/arch/um/drivers/mconsole_user.c new file mode 100644 index 000000000000..fe5afb13252c --- /dev/null +++ b/arch/um/drivers/mconsole_user.c | |||
@@ -0,0 +1,215 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) | ||
3 | * Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com) | ||
4 | * Licensed under the GPL | ||
5 | */ | ||
6 | |||
7 | #include <stdio.h> | ||
8 | #include <stdlib.h> | ||
9 | #include <errno.h> | ||
10 | #include <signal.h> | ||
11 | #include <sys/socket.h> | ||
12 | #include <sys/types.h> | ||
13 | #include <sys/uio.h> | ||
14 | #include <sys/un.h> | ||
15 | #include <unistd.h> | ||
16 | #include "user.h" | ||
17 | #include "mconsole.h" | ||
18 | #include "umid.h" | ||
19 | |||
20 | static struct mconsole_command commands[] = { | ||
21 | { "version", mconsole_version, MCONSOLE_INTR }, | ||
22 | { "halt", mconsole_halt, MCONSOLE_PROC }, | ||
23 | { "reboot", mconsole_reboot, MCONSOLE_PROC }, | ||
24 | { "config", mconsole_config, MCONSOLE_PROC }, | ||
25 | { "remove", mconsole_remove, MCONSOLE_PROC }, | ||
26 | { "sysrq", mconsole_sysrq, MCONSOLE_INTR }, | ||
27 | { "help", mconsole_help, MCONSOLE_INTR }, | ||
28 | { "cad", mconsole_cad, MCONSOLE_INTR }, | ||
29 | { "stop", mconsole_stop, MCONSOLE_PROC }, | ||
30 | { "go", mconsole_go, MCONSOLE_INTR }, | ||
31 | { "log", mconsole_log, MCONSOLE_INTR }, | ||
32 | { "proc", mconsole_proc, MCONSOLE_PROC }, | ||
33 | }; | ||
34 | |||
35 | /* Initialized in mconsole_init, which is an initcall */ | ||
36 | char mconsole_socket_name[256]; | ||
37 | |||
38 | int mconsole_reply_v0(struct mc_request *req, char *reply) | ||
39 | { | ||
40 | struct iovec iov; | ||
41 | struct msghdr msg; | ||
42 | |||
43 | iov.iov_base = reply; | ||
44 | iov.iov_len = strlen(reply); | ||
45 | |||
46 | msg.msg_name = &(req->origin); | ||
47 | msg.msg_namelen = req->originlen; | ||
48 | msg.msg_iov = &iov; | ||
49 | msg.msg_iovlen = 1; | ||
50 | msg.msg_control = NULL; | ||
51 | msg.msg_controllen = 0; | ||
52 | msg.msg_flags = 0; | ||
53 | |||
54 | return sendmsg(req->originating_fd, &msg, 0); | ||
55 | } | ||
56 | |||
57 | static struct mconsole_command *mconsole_parse(struct mc_request *req) | ||
58 | { | ||
59 | struct mconsole_command *cmd; | ||
60 | int i; | ||
61 | |||
62 | for(i=0;i<sizeof(commands)/sizeof(commands[0]);i++){ | ||
63 | cmd = &commands[i]; | ||
64 | if(!strncmp(req->request.data, cmd->command, | ||
65 | strlen(cmd->command))){ | ||
66 | return(cmd); | ||
67 | } | ||
68 | } | ||
69 | return(NULL); | ||
70 | } | ||
71 | |||
72 | #define MIN(a,b) ((a)<(b) ? (a):(b)) | ||
73 | |||
74 | #define STRINGX(x) #x | ||
75 | #define STRING(x) STRINGX(x) | ||
76 | |||
77 | int mconsole_get_request(int fd, struct mc_request *req) | ||
78 | { | ||
79 | int len; | ||
80 | |||
81 | req->originlen = sizeof(req->origin); | ||
82 | req->len = recvfrom(fd, &req->request, sizeof(req->request), 0, | ||
83 | (struct sockaddr *) req->origin, &req->originlen); | ||
84 | if (req->len < 0) | ||
85 | return 0; | ||
86 | |||
87 | req->originating_fd = fd; | ||
88 | |||
89 | if(req->request.magic != MCONSOLE_MAGIC){ | ||
90 | /* Unversioned request */ | ||
91 | len = MIN(sizeof(req->request.data) - 1, | ||
92 | strlen((char *) &req->request)); | ||
93 | memmove(req->request.data, &req->request, len); | ||
94 | req->request.data[len] = '\0'; | ||
95 | |||
96 | req->request.magic = MCONSOLE_MAGIC; | ||
97 | req->request.version = 0; | ||
98 | req->request.len = len; | ||
99 | |||
100 | mconsole_reply_v0(req, "ERR Version 0 mconsole clients are " | ||
101 | "not supported by this driver"); | ||
102 | return(0); | ||
103 | } | ||
104 | |||
105 | if(req->request.len >= MCONSOLE_MAX_DATA){ | ||
106 | mconsole_reply(req, "Request too large", 1, 0); | ||
107 | return(0); | ||
108 | } | ||
109 | if(req->request.version != MCONSOLE_VERSION){ | ||
110 | mconsole_reply(req, "This driver only supports version " | ||
111 | STRING(MCONSOLE_VERSION) " clients", 1, 0); | ||
112 | } | ||
113 | |||
114 | req->request.data[req->request.len] = '\0'; | ||
115 | req->cmd = mconsole_parse(req); | ||
116 | if(req->cmd == NULL){ | ||
117 | mconsole_reply(req, "Unknown command", 1, 0); | ||
118 | return(0); | ||
119 | } | ||
120 | |||
121 | return(1); | ||
122 | } | ||
123 | |||
124 | int mconsole_reply(struct mc_request *req, char *str, int err, int more) | ||
125 | { | ||
126 | struct mconsole_reply reply; | ||
127 | int total, len, n; | ||
128 | |||
129 | total = strlen(str); | ||
130 | do { | ||
131 | reply.err = err; | ||
132 | |||
133 | /* err can only be true on the first packet */ | ||
134 | err = 0; | ||
135 | |||
136 | len = MIN(total, MCONSOLE_MAX_DATA - 1); | ||
137 | |||
138 | if(len == total) reply.more = more; | ||
139 | else reply.more = 1; | ||
140 | |||
141 | memcpy(reply.data, str, len); | ||
142 | reply.data[len] = '\0'; | ||
143 | total -= len; | ||
144 | str += len; | ||
145 | reply.len = len + 1; | ||
146 | |||
147 | len = sizeof(reply) + reply.len - sizeof(reply.data); | ||
148 | |||
149 | n = sendto(req->originating_fd, &reply, len, 0, | ||
150 | (struct sockaddr *) req->origin, req->originlen); | ||
151 | |||
152 | if(n < 0) return(-errno); | ||
153 | } while(total > 0); | ||
154 | return(0); | ||
155 | } | ||
156 | |||
157 | int mconsole_unlink_socket(void) | ||
158 | { | ||
159 | unlink(mconsole_socket_name); | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static int notify_sock = -1; | ||
164 | |||
165 | int mconsole_notify(char *sock_name, int type, const void *data, int len) | ||
166 | { | ||
167 | struct sockaddr_un target; | ||
168 | struct mconsole_notify packet; | ||
169 | int n, err = 0; | ||
170 | |||
171 | lock_notify(); | ||
172 | if(notify_sock < 0){ | ||
173 | notify_sock = socket(PF_UNIX, SOCK_DGRAM, 0); | ||
174 | if(notify_sock < 0){ | ||
175 | printk("mconsole_notify - socket failed, errno = %d\n", | ||
176 | errno); | ||
177 | err = -errno; | ||
178 | } | ||
179 | } | ||
180 | unlock_notify(); | ||
181 | |||
182 | if(err) | ||
183 | return(err); | ||
184 | |||
185 | target.sun_family = AF_UNIX; | ||
186 | strcpy(target.sun_path, sock_name); | ||
187 | |||
188 | packet.magic = MCONSOLE_MAGIC; | ||
189 | packet.version = MCONSOLE_VERSION; | ||
190 | packet.type = type; | ||
191 | len = (len > sizeof(packet.data)) ? sizeof(packet.data) : len; | ||
192 | packet.len = len; | ||
193 | memcpy(packet.data, data, len); | ||
194 | |||
195 | err = 0; | ||
196 | len = sizeof(packet) + packet.len - sizeof(packet.data); | ||
197 | n = sendto(notify_sock, &packet, len, 0, (struct sockaddr *) &target, | ||
198 | sizeof(target)); | ||
199 | if(n < 0){ | ||
200 | printk("mconsole_notify - sendto failed, errno = %d\n", errno); | ||
201 | err = -errno; | ||
202 | } | ||
203 | return(err); | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
208 | * Emacs will notice this stuff at the end of the file and automatically | ||
209 | * adjust the settings for this buffer only. This must remain at the end | ||
210 | * of the file. | ||
211 | * --------------------------------------------------------------------------- | ||
212 | * Local variables: | ||
213 | * c-file-style: "linux" | ||
214 | * End: | ||
215 | */ | ||
diff --git a/arch/um/drivers/mmapper_kern.c b/arch/um/drivers/mmapper_kern.c new file mode 100644 index 000000000000..a63231dffe05 --- /dev/null +++ b/arch/um/drivers/mmapper_kern.c | |||
@@ -0,0 +1,150 @@ | |||
1 | /* | ||
2 | * arch/um/drivers/mmapper_kern.c | ||
3 | * | ||
4 | * BRIEF MODULE DESCRIPTION | ||
5 | * | ||
6 | * Copyright (C) 2000 RidgeRun, Inc. | ||
7 | * Author: RidgeRun, Inc. | ||
8 | * Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/kdev_t.h> | ||
14 | #include <linux/time.h> | ||
15 | #include <linux/devfs_fs_kernel.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/mm.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/smp_lock.h> | ||
21 | #include <asm/uaccess.h> | ||
22 | #include <asm/irq.h> | ||
23 | #include <asm/pgtable.h> | ||
24 | #include "mem_user.h" | ||
25 | #include "user_util.h" | ||
26 | |||
27 | /* These are set in mmapper_init, which is called at boot time */ | ||
28 | static unsigned long mmapper_size; | ||
29 | static unsigned long p_buf = 0; | ||
30 | static char *v_buf = NULL; | ||
31 | |||
32 | static ssize_t | ||
33 | mmapper_read(struct file *file, char *buf, size_t count, loff_t *ppos) | ||
34 | { | ||
35 | if(*ppos > mmapper_size) | ||
36 | return -EINVAL; | ||
37 | |||
38 | if(count + *ppos > mmapper_size) | ||
39 | count = count + *ppos - mmapper_size; | ||
40 | |||
41 | if(count < 0) | ||
42 | return -EINVAL; | ||
43 | |||
44 | copy_to_user(buf,&v_buf[*ppos],count); | ||
45 | |||
46 | return count; | ||
47 | } | ||
48 | |||
49 | static ssize_t | ||
50 | mmapper_write(struct file *file, const char *buf, size_t count, loff_t *ppos) | ||
51 | { | ||
52 | if(*ppos > mmapper_size) | ||
53 | return -EINVAL; | ||
54 | |||
55 | if(count + *ppos > mmapper_size) | ||
56 | count = count + *ppos - mmapper_size; | ||
57 | |||
58 | if(count < 0) | ||
59 | return -EINVAL; | ||
60 | |||
61 | copy_from_user(&v_buf[*ppos],buf,count); | ||
62 | |||
63 | return count; | ||
64 | } | ||
65 | |||
66 | static int | ||
67 | mmapper_ioctl(struct inode *inode, struct file *file, unsigned int cmd, | ||
68 | unsigned long arg) | ||
69 | { | ||
70 | return(-ENOIOCTLCMD); | ||
71 | } | ||
72 | |||
73 | static int | ||
74 | mmapper_mmap(struct file *file, struct vm_area_struct * vma) | ||
75 | { | ||
76 | int ret = -EINVAL; | ||
77 | int size; | ||
78 | |||
79 | lock_kernel(); | ||
80 | if (vma->vm_pgoff != 0) | ||
81 | goto out; | ||
82 | |||
83 | size = vma->vm_end - vma->vm_start; | ||
84 | if(size > mmapper_size) return(-EFAULT); | ||
85 | |||
86 | /* XXX A comment above remap_pfn_range says it should only be | ||
87 | * called when the mm semaphore is held | ||
88 | */ | ||
89 | if (remap_pfn_range(vma, vma->vm_start, p_buf >> PAGE_SHIFT, size, | ||
90 | vma->vm_page_prot)) | ||
91 | goto out; | ||
92 | ret = 0; | ||
93 | out: | ||
94 | unlock_kernel(); | ||
95 | return ret; | ||
96 | } | ||
97 | |||
98 | static int | ||
99 | mmapper_open(struct inode *inode, struct file *file) | ||
100 | { | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int | ||
105 | mmapper_release(struct inode *inode, struct file *file) | ||
106 | { | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static struct file_operations mmapper_fops = { | ||
111 | .owner = THIS_MODULE, | ||
112 | .read = mmapper_read, | ||
113 | .write = mmapper_write, | ||
114 | .ioctl = mmapper_ioctl, | ||
115 | .mmap = mmapper_mmap, | ||
116 | .open = mmapper_open, | ||
117 | .release = mmapper_release, | ||
118 | }; | ||
119 | |||
120 | static int __init mmapper_init(void) | ||
121 | { | ||
122 | printk(KERN_INFO "Mapper v0.1\n"); | ||
123 | |||
124 | v_buf = (char *) find_iomem("mmapper", &mmapper_size); | ||
125 | if(mmapper_size == 0){ | ||
126 | printk(KERN_ERR "mmapper_init - find_iomem failed\n"); | ||
127 | return(0); | ||
128 | } | ||
129 | |||
130 | p_buf = __pa(v_buf); | ||
131 | |||
132 | devfs_mk_cdev(MKDEV(30, 0), S_IFCHR|S_IRUGO|S_IWUGO, "mmapper"); | ||
133 | return(0); | ||
134 | } | ||
135 | |||
136 | static void mmapper_exit(void) | ||
137 | { | ||
138 | } | ||
139 | |||
140 | module_init(mmapper_init); | ||
141 | module_exit(mmapper_exit); | ||
142 | |||
143 | MODULE_AUTHOR("Greg Lonnon <glonnon@ridgerun.com>"); | ||
144 | MODULE_DESCRIPTION("DSPLinux simulator mmapper driver"); | ||
145 | /* | ||
146 | * --------------------------------------------------------------------------- | ||
147 | * Local variables: | ||
148 | * c-file-style: "linux" | ||
149 | * End: | ||
150 | */ | ||
diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c new file mode 100644 index 000000000000..4eeaf88c1e97 --- /dev/null +++ b/arch/um/drivers/net_kern.c | |||
@@ -0,0 +1,896 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and | ||
3 | * James Leu (jleu@mindspring.net). | ||
4 | * Copyright (C) 2001 by various other people who didn't put their name here. | ||
5 | * Licensed under the GPL. | ||
6 | */ | ||
7 | |||
8 | #include "linux/config.h" | ||
9 | #include "linux/kernel.h" | ||
10 | #include "linux/netdevice.h" | ||
11 | #include "linux/rtnetlink.h" | ||
12 | #include "linux/skbuff.h" | ||
13 | #include "linux/socket.h" | ||
14 | #include "linux/spinlock.h" | ||
15 | #include "linux/module.h" | ||
16 | #include "linux/init.h" | ||
17 | #include "linux/etherdevice.h" | ||
18 | #include "linux/list.h" | ||
19 | #include "linux/inetdevice.h" | ||
20 | #include "linux/ctype.h" | ||
21 | #include "linux/bootmem.h" | ||
22 | #include "linux/ethtool.h" | ||
23 | #include "asm/uaccess.h" | ||
24 | #include "user_util.h" | ||
25 | #include "kern_util.h" | ||
26 | #include "net_kern.h" | ||
27 | #include "net_user.h" | ||
28 | #include "mconsole_kern.h" | ||
29 | #include "init.h" | ||
30 | #include "irq_user.h" | ||
31 | #include "irq_kern.h" | ||
32 | |||
33 | #define DRIVER_NAME "uml-netdev" | ||
34 | |||
35 | static DEFINE_SPINLOCK(opened_lock); | ||
36 | LIST_HEAD(opened); | ||
37 | |||
38 | static int uml_net_rx(struct net_device *dev) | ||
39 | { | ||
40 | struct uml_net_private *lp = dev->priv; | ||
41 | int pkt_len; | ||
42 | struct sk_buff *skb; | ||
43 | |||
44 | /* If we can't allocate memory, try again next round. */ | ||
45 | skb = dev_alloc_skb(dev->mtu); | ||
46 | if (skb == NULL) { | ||
47 | lp->stats.rx_dropped++; | ||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | skb->dev = dev; | ||
52 | skb_put(skb, dev->mtu); | ||
53 | skb->mac.raw = skb->data; | ||
54 | pkt_len = (*lp->read)(lp->fd, &skb, lp); | ||
55 | |||
56 | if (pkt_len > 0) { | ||
57 | skb_trim(skb, pkt_len); | ||
58 | skb->protocol = (*lp->protocol)(skb); | ||
59 | netif_rx(skb); | ||
60 | |||
61 | lp->stats.rx_bytes += skb->len; | ||
62 | lp->stats.rx_packets++; | ||
63 | return pkt_len; | ||
64 | } | ||
65 | |||
66 | kfree_skb(skb); | ||
67 | return pkt_len; | ||
68 | } | ||
69 | |||
70 | irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
71 | { | ||
72 | struct net_device *dev = dev_id; | ||
73 | struct uml_net_private *lp = dev->priv; | ||
74 | int err; | ||
75 | |||
76 | if(!netif_running(dev)) | ||
77 | return(IRQ_NONE); | ||
78 | |||
79 | spin_lock(&lp->lock); | ||
80 | while((err = uml_net_rx(dev)) > 0) ; | ||
81 | if(err < 0) { | ||
82 | printk(KERN_ERR | ||
83 | "Device '%s' read returned %d, shutting it down\n", | ||
84 | dev->name, err); | ||
85 | dev_close(dev); | ||
86 | goto out; | ||
87 | } | ||
88 | reactivate_fd(lp->fd, UM_ETH_IRQ); | ||
89 | |||
90 | out: | ||
91 | spin_unlock(&lp->lock); | ||
92 | return(IRQ_HANDLED); | ||
93 | } | ||
94 | |||
95 | static int uml_net_open(struct net_device *dev) | ||
96 | { | ||
97 | struct uml_net_private *lp = dev->priv; | ||
98 | char addr[sizeof("255.255.255.255\0")]; | ||
99 | int err; | ||
100 | |||
101 | spin_lock(&lp->lock); | ||
102 | |||
103 | if(lp->fd >= 0){ | ||
104 | err = -ENXIO; | ||
105 | goto out; | ||
106 | } | ||
107 | |||
108 | if(!lp->have_mac){ | ||
109 | dev_ip_addr(dev, addr, &lp->mac[2]); | ||
110 | set_ether_mac(dev, lp->mac); | ||
111 | } | ||
112 | |||
113 | lp->fd = (*lp->open)(&lp->user); | ||
114 | if(lp->fd < 0){ | ||
115 | err = lp->fd; | ||
116 | goto out; | ||
117 | } | ||
118 | |||
119 | err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt, | ||
120 | SA_INTERRUPT | SA_SHIRQ, dev->name, dev); | ||
121 | if(err != 0){ | ||
122 | printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err); | ||
123 | if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user); | ||
124 | lp->fd = -1; | ||
125 | err = -ENETUNREACH; | ||
126 | } | ||
127 | |||
128 | lp->tl.data = (unsigned long) &lp->user; | ||
129 | netif_start_queue(dev); | ||
130 | |||
131 | /* clear buffer - it can happen that the host side of the interface | ||
132 | * is full when we get here. In this case, new data is never queued, | ||
133 | * SIGIOs never arrive, and the net never works. | ||
134 | */ | ||
135 | while((err = uml_net_rx(dev)) > 0) ; | ||
136 | |||
137 | out: | ||
138 | spin_unlock(&lp->lock); | ||
139 | return(err); | ||
140 | } | ||
141 | |||
142 | static int uml_net_close(struct net_device *dev) | ||
143 | { | ||
144 | struct uml_net_private *lp = dev->priv; | ||
145 | |||
146 | netif_stop_queue(dev); | ||
147 | spin_lock(&lp->lock); | ||
148 | |||
149 | free_irq_by_irq_and_dev(dev->irq, dev); | ||
150 | free_irq(dev->irq, dev); | ||
151 | if(lp->close != NULL) | ||
152 | (*lp->close)(lp->fd, &lp->user); | ||
153 | lp->fd = -1; | ||
154 | |||
155 | spin_unlock(&lp->lock); | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev) | ||
160 | { | ||
161 | struct uml_net_private *lp = dev->priv; | ||
162 | unsigned long flags; | ||
163 | int len; | ||
164 | |||
165 | netif_stop_queue(dev); | ||
166 | |||
167 | spin_lock_irqsave(&lp->lock, flags); | ||
168 | |||
169 | len = (*lp->write)(lp->fd, &skb, lp); | ||
170 | |||
171 | if(len == skb->len) { | ||
172 | lp->stats.tx_packets++; | ||
173 | lp->stats.tx_bytes += skb->len; | ||
174 | dev->trans_start = jiffies; | ||
175 | netif_start_queue(dev); | ||
176 | |||
177 | /* this is normally done in the interrupt when tx finishes */ | ||
178 | netif_wake_queue(dev); | ||
179 | } | ||
180 | else if(len == 0){ | ||
181 | netif_start_queue(dev); | ||
182 | lp->stats.tx_dropped++; | ||
183 | } | ||
184 | else { | ||
185 | netif_start_queue(dev); | ||
186 | printk(KERN_ERR "uml_net_start_xmit: failed(%d)\n", len); | ||
187 | } | ||
188 | |||
189 | spin_unlock_irqrestore(&lp->lock, flags); | ||
190 | |||
191 | dev_kfree_skb(skb); | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static struct net_device_stats *uml_net_get_stats(struct net_device *dev) | ||
197 | { | ||
198 | struct uml_net_private *lp = dev->priv; | ||
199 | return &lp->stats; | ||
200 | } | ||
201 | |||
202 | static void uml_net_set_multicast_list(struct net_device *dev) | ||
203 | { | ||
204 | if (dev->flags & IFF_PROMISC) return; | ||
205 | else if (dev->mc_count) dev->flags |= IFF_ALLMULTI; | ||
206 | else dev->flags &= ~IFF_ALLMULTI; | ||
207 | } | ||
208 | |||
209 | static void uml_net_tx_timeout(struct net_device *dev) | ||
210 | { | ||
211 | dev->trans_start = jiffies; | ||
212 | netif_wake_queue(dev); | ||
213 | } | ||
214 | |||
215 | static int uml_net_set_mac(struct net_device *dev, void *addr) | ||
216 | { | ||
217 | struct uml_net_private *lp = dev->priv; | ||
218 | struct sockaddr *hwaddr = addr; | ||
219 | |||
220 | spin_lock(&lp->lock); | ||
221 | memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); | ||
222 | spin_unlock(&lp->lock); | ||
223 | |||
224 | return(0); | ||
225 | } | ||
226 | |||
227 | static int uml_net_change_mtu(struct net_device *dev, int new_mtu) | ||
228 | { | ||
229 | struct uml_net_private *lp = dev->priv; | ||
230 | int err = 0; | ||
231 | |||
232 | spin_lock(&lp->lock); | ||
233 | |||
234 | new_mtu = (*lp->set_mtu)(new_mtu, &lp->user); | ||
235 | if(new_mtu < 0){ | ||
236 | err = new_mtu; | ||
237 | goto out; | ||
238 | } | ||
239 | |||
240 | dev->mtu = new_mtu; | ||
241 | |||
242 | out: | ||
243 | spin_unlock(&lp->lock); | ||
244 | return err; | ||
245 | } | ||
246 | |||
247 | static int uml_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | ||
248 | { | ||
249 | static const struct ethtool_drvinfo info = { | ||
250 | .cmd = ETHTOOL_GDRVINFO, | ||
251 | .driver = DRIVER_NAME, | ||
252 | .version = "42", | ||
253 | }; | ||
254 | void *useraddr; | ||
255 | u32 ethcmd; | ||
256 | |||
257 | switch (cmd) { | ||
258 | case SIOCETHTOOL: | ||
259 | useraddr = ifr->ifr_data; | ||
260 | if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) | ||
261 | return -EFAULT; | ||
262 | switch (ethcmd) { | ||
263 | case ETHTOOL_GDRVINFO: | ||
264 | if (copy_to_user(useraddr, &info, sizeof(info))) | ||
265 | return -EFAULT; | ||
266 | return 0; | ||
267 | default: | ||
268 | return -EOPNOTSUPP; | ||
269 | } | ||
270 | default: | ||
271 | return -EINVAL; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | void uml_net_user_timer_expire(unsigned long _conn) | ||
276 | { | ||
277 | #ifdef undef | ||
278 | struct connection *conn = (struct connection *)_conn; | ||
279 | |||
280 | dprintk(KERN_INFO "uml_net_user_timer_expire [%p]\n", conn); | ||
281 | do_connect(conn); | ||
282 | #endif | ||
283 | } | ||
284 | |||
285 | static DEFINE_SPINLOCK(devices_lock); | ||
286 | static struct list_head devices = LIST_HEAD_INIT(devices); | ||
287 | |||
288 | static struct device_driver uml_net_driver = { | ||
289 | .name = DRIVER_NAME, | ||
290 | .bus = &platform_bus_type, | ||
291 | }; | ||
292 | static int driver_registered; | ||
293 | |||
294 | static int eth_configure(int n, void *init, char *mac, | ||
295 | struct transport *transport) | ||
296 | { | ||
297 | struct uml_net *device; | ||
298 | struct net_device *dev; | ||
299 | struct uml_net_private *lp; | ||
300 | int save, err, size; | ||
301 | |||
302 | size = transport->private_size + sizeof(struct uml_net_private) + | ||
303 | sizeof(((struct uml_net_private *) 0)->user); | ||
304 | |||
305 | device = kmalloc(sizeof(*device), GFP_KERNEL); | ||
306 | if (device == NULL) { | ||
307 | printk(KERN_ERR "eth_configure failed to allocate uml_net\n"); | ||
308 | return(1); | ||
309 | } | ||
310 | |||
311 | memset(device, 0, sizeof(*device)); | ||
312 | INIT_LIST_HEAD(&device->list); | ||
313 | device->index = n; | ||
314 | |||
315 | spin_lock(&devices_lock); | ||
316 | list_add(&device->list, &devices); | ||
317 | spin_unlock(&devices_lock); | ||
318 | |||
319 | if (setup_etheraddr(mac, device->mac)) | ||
320 | device->have_mac = 1; | ||
321 | |||
322 | printk(KERN_INFO "Netdevice %d ", n); | ||
323 | if (device->have_mac) | ||
324 | printk("(%02x:%02x:%02x:%02x:%02x:%02x) ", | ||
325 | device->mac[0], device->mac[1], | ||
326 | device->mac[2], device->mac[3], | ||
327 | device->mac[4], device->mac[5]); | ||
328 | printk(": "); | ||
329 | dev = alloc_etherdev(size); | ||
330 | if (dev == NULL) { | ||
331 | printk(KERN_ERR "eth_configure: failed to allocate device\n"); | ||
332 | return 1; | ||
333 | } | ||
334 | |||
335 | /* sysfs register */ | ||
336 | if (!driver_registered) { | ||
337 | driver_register(¨_net_driver); | ||
338 | driver_registered = 1; | ||
339 | } | ||
340 | device->pdev.id = n; | ||
341 | device->pdev.name = DRIVER_NAME; | ||
342 | platform_device_register(&device->pdev); | ||
343 | SET_NETDEV_DEV(dev,&device->pdev.dev); | ||
344 | |||
345 | /* If this name ends up conflicting with an existing registered | ||
346 | * netdevice, that is OK, register_netdev{,ice}() will notice this | ||
347 | * and fail. | ||
348 | */ | ||
349 | snprintf(dev->name, sizeof(dev->name), "eth%d", n); | ||
350 | device->dev = dev; | ||
351 | |||
352 | (*transport->kern->init)(dev, init); | ||
353 | |||
354 | dev->mtu = transport->user->max_packet; | ||
355 | dev->open = uml_net_open; | ||
356 | dev->hard_start_xmit = uml_net_start_xmit; | ||
357 | dev->stop = uml_net_close; | ||
358 | dev->get_stats = uml_net_get_stats; | ||
359 | dev->set_multicast_list = uml_net_set_multicast_list; | ||
360 | dev->tx_timeout = uml_net_tx_timeout; | ||
361 | dev->set_mac_address = uml_net_set_mac; | ||
362 | dev->change_mtu = uml_net_change_mtu; | ||
363 | dev->do_ioctl = uml_net_ioctl; | ||
364 | dev->watchdog_timeo = (HZ >> 1); | ||
365 | dev->irq = UM_ETH_IRQ; | ||
366 | |||
367 | rtnl_lock(); | ||
368 | err = register_netdevice(dev); | ||
369 | rtnl_unlock(); | ||
370 | if (err) { | ||
371 | device->dev = NULL; | ||
372 | /* XXX: should we call ->remove() here? */ | ||
373 | free_netdev(dev); | ||
374 | return 1; | ||
375 | } | ||
376 | lp = dev->priv; | ||
377 | |||
378 | /* lp.user is the first four bytes of the transport data, which | ||
379 | * has already been initialized. This structure assignment will | ||
380 | * overwrite that, so we make sure that .user gets overwritten with | ||
381 | * what it already has. | ||
382 | */ | ||
383 | save = lp->user[0]; | ||
384 | *lp = ((struct uml_net_private) | ||
385 | { .list = LIST_HEAD_INIT(lp->list), | ||
386 | .dev = dev, | ||
387 | .fd = -1, | ||
388 | .mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0}, | ||
389 | .have_mac = device->have_mac, | ||
390 | .protocol = transport->kern->protocol, | ||
391 | .open = transport->user->open, | ||
392 | .close = transport->user->close, | ||
393 | .remove = transport->user->remove, | ||
394 | .read = transport->kern->read, | ||
395 | .write = transport->kern->write, | ||
396 | .add_address = transport->user->add_address, | ||
397 | .delete_address = transport->user->delete_address, | ||
398 | .set_mtu = transport->user->set_mtu, | ||
399 | .user = { save } }); | ||
400 | |||
401 | init_timer(&lp->tl); | ||
402 | spin_lock_init(&lp->lock); | ||
403 | lp->tl.function = uml_net_user_timer_expire; | ||
404 | if (lp->have_mac) | ||
405 | memcpy(lp->mac, device->mac, sizeof(lp->mac)); | ||
406 | |||
407 | if (transport->user->init) | ||
408 | (*transport->user->init)(&lp->user, dev); | ||
409 | |||
410 | if (device->have_mac) | ||
411 | set_ether_mac(dev, device->mac); | ||
412 | |||
413 | spin_lock(&opened_lock); | ||
414 | list_add(&lp->list, &opened); | ||
415 | spin_unlock(&opened_lock); | ||
416 | |||
417 | return(0); | ||
418 | } | ||
419 | |||
420 | static struct uml_net *find_device(int n) | ||
421 | { | ||
422 | struct uml_net *device; | ||
423 | struct list_head *ele; | ||
424 | |||
425 | spin_lock(&devices_lock); | ||
426 | list_for_each(ele, &devices){ | ||
427 | device = list_entry(ele, struct uml_net, list); | ||
428 | if(device->index == n) | ||
429 | goto out; | ||
430 | } | ||
431 | device = NULL; | ||
432 | out: | ||
433 | spin_unlock(&devices_lock); | ||
434 | return(device); | ||
435 | } | ||
436 | |||
437 | static int eth_parse(char *str, int *index_out, char **str_out) | ||
438 | { | ||
439 | char *end; | ||
440 | int n; | ||
441 | |||
442 | n = simple_strtoul(str, &end, 0); | ||
443 | if(end == str){ | ||
444 | printk(KERN_ERR "eth_setup: Failed to parse '%s'\n", str); | ||
445 | return(1); | ||
446 | } | ||
447 | if(n < 0){ | ||
448 | printk(KERN_ERR "eth_setup: device %d is negative\n", n); | ||
449 | return(1); | ||
450 | } | ||
451 | str = end; | ||
452 | if(*str != '='){ | ||
453 | printk(KERN_ERR | ||
454 | "eth_setup: expected '=' after device number\n"); | ||
455 | return(1); | ||
456 | } | ||
457 | str++; | ||
458 | if(find_device(n)){ | ||
459 | printk(KERN_ERR "eth_setup: Device %d already configured\n", | ||
460 | n); | ||
461 | return(1); | ||
462 | } | ||
463 | if(index_out) *index_out = n; | ||
464 | *str_out = str; | ||
465 | return(0); | ||
466 | } | ||
467 | |||
468 | struct eth_init { | ||
469 | struct list_head list; | ||
470 | char *init; | ||
471 | int index; | ||
472 | }; | ||
473 | |||
474 | /* Filled in at boot time. Will need locking if the transports become | ||
475 | * modular. | ||
476 | */ | ||
477 | struct list_head transports = LIST_HEAD_INIT(transports); | ||
478 | |||
479 | /* Filled in during early boot */ | ||
480 | struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line); | ||
481 | |||
482 | static int check_transport(struct transport *transport, char *eth, int n, | ||
483 | void **init_out, char **mac_out) | ||
484 | { | ||
485 | int len; | ||
486 | |||
487 | len = strlen(transport->name); | ||
488 | if(strncmp(eth, transport->name, len)) | ||
489 | return(0); | ||
490 | |||
491 | eth += len; | ||
492 | if(*eth == ',') | ||
493 | eth++; | ||
494 | else if(*eth != '\0') | ||
495 | return(0); | ||
496 | |||
497 | *init_out = kmalloc(transport->setup_size, GFP_KERNEL); | ||
498 | if(*init_out == NULL) | ||
499 | return(1); | ||
500 | |||
501 | if(!transport->setup(eth, mac_out, *init_out)){ | ||
502 | kfree(*init_out); | ||
503 | *init_out = NULL; | ||
504 | } | ||
505 | return(1); | ||
506 | } | ||
507 | |||
508 | void register_transport(struct transport *new) | ||
509 | { | ||
510 | struct list_head *ele, *next; | ||
511 | struct eth_init *eth; | ||
512 | void *init; | ||
513 | char *mac = NULL; | ||
514 | int match; | ||
515 | |||
516 | list_add(&new->list, &transports); | ||
517 | |||
518 | list_for_each_safe(ele, next, ð_cmd_line){ | ||
519 | eth = list_entry(ele, struct eth_init, list); | ||
520 | match = check_transport(new, eth->init, eth->index, &init, | ||
521 | &mac); | ||
522 | if(!match) | ||
523 | continue; | ||
524 | else if(init != NULL){ | ||
525 | eth_configure(eth->index, init, mac, new); | ||
526 | kfree(init); | ||
527 | } | ||
528 | list_del(ð->list); | ||
529 | } | ||
530 | } | ||
531 | |||
532 | static int eth_setup_common(char *str, int index) | ||
533 | { | ||
534 | struct list_head *ele; | ||
535 | struct transport *transport; | ||
536 | void *init; | ||
537 | char *mac = NULL; | ||
538 | |||
539 | list_for_each(ele, &transports){ | ||
540 | transport = list_entry(ele, struct transport, list); | ||
541 | if(!check_transport(transport, str, index, &init, &mac)) | ||
542 | continue; | ||
543 | if(init != NULL){ | ||
544 | eth_configure(index, init, mac, transport); | ||
545 | kfree(init); | ||
546 | } | ||
547 | return(1); | ||
548 | } | ||
549 | return(0); | ||
550 | } | ||
551 | |||
552 | static int eth_setup(char *str) | ||
553 | { | ||
554 | struct eth_init *new; | ||
555 | int n, err; | ||
556 | |||
557 | err = eth_parse(str, &n, &str); | ||
558 | if(err) return(1); | ||
559 | |||
560 | new = alloc_bootmem(sizeof(new)); | ||
561 | if (new == NULL){ | ||
562 | printk("eth_init : alloc_bootmem failed\n"); | ||
563 | return(1); | ||
564 | } | ||
565 | |||
566 | INIT_LIST_HEAD(&new->list); | ||
567 | new->index = n; | ||
568 | new->init = str; | ||
569 | |||
570 | list_add_tail(&new->list, ð_cmd_line); | ||
571 | return(1); | ||
572 | } | ||
573 | |||
574 | __setup("eth", eth_setup); | ||
575 | __uml_help(eth_setup, | ||
576 | "eth[0-9]+=<transport>,<options>\n" | ||
577 | " Configure a network device.\n\n" | ||
578 | ); | ||
579 | |||
580 | #if 0 | ||
581 | static int eth_init(void) | ||
582 | { | ||
583 | struct list_head *ele, *next; | ||
584 | struct eth_init *eth; | ||
585 | |||
586 | list_for_each_safe(ele, next, ð_cmd_line){ | ||
587 | eth = list_entry(ele, struct eth_init, list); | ||
588 | |||
589 | if(eth_setup_common(eth->init, eth->index)) | ||
590 | list_del(ð->list); | ||
591 | } | ||
592 | |||
593 | return(1); | ||
594 | } | ||
595 | __initcall(eth_init); | ||
596 | #endif | ||
597 | |||
598 | static int net_config(char *str) | ||
599 | { | ||
600 | int n, err; | ||
601 | |||
602 | err = eth_parse(str, &n, &str); | ||
603 | if(err) return(err); | ||
604 | |||
605 | str = uml_strdup(str); | ||
606 | if(str == NULL){ | ||
607 | printk(KERN_ERR "net_config failed to strdup string\n"); | ||
608 | return(-1); | ||
609 | } | ||
610 | err = !eth_setup_common(str, n); | ||
611 | if(err) | ||
612 | kfree(str); | ||
613 | return(err); | ||
614 | } | ||
615 | |||
616 | static int net_remove(char *str) | ||
617 | { | ||
618 | struct uml_net *device; | ||
619 | struct net_device *dev; | ||
620 | struct uml_net_private *lp; | ||
621 | char *end; | ||
622 | int n; | ||
623 | |||
624 | n = simple_strtoul(str, &end, 0); | ||
625 | if((*end != '\0') || (end == str)) | ||
626 | return(-1); | ||
627 | |||
628 | device = find_device(n); | ||
629 | if(device == NULL) | ||
630 | return(0); | ||
631 | |||
632 | dev = device->dev; | ||
633 | lp = dev->priv; | ||
634 | if(lp->fd > 0) return(-1); | ||
635 | if(lp->remove != NULL) (*lp->remove)(&lp->user); | ||
636 | unregister_netdev(dev); | ||
637 | platform_device_unregister(&device->pdev); | ||
638 | |||
639 | list_del(&device->list); | ||
640 | kfree(device); | ||
641 | free_netdev(dev); | ||
642 | return(0); | ||
643 | } | ||
644 | |||
645 | static struct mc_device net_mc = { | ||
646 | .name = "eth", | ||
647 | .config = net_config, | ||
648 | .get_config = NULL, | ||
649 | .remove = net_remove, | ||
650 | }; | ||
651 | |||
652 | static int uml_inetaddr_event(struct notifier_block *this, unsigned long event, | ||
653 | void *ptr) | ||
654 | { | ||
655 | struct in_ifaddr *ifa = ptr; | ||
656 | u32 addr = ifa->ifa_address; | ||
657 | u32 netmask = ifa->ifa_mask; | ||
658 | struct net_device *dev = ifa->ifa_dev->dev; | ||
659 | struct uml_net_private *lp; | ||
660 | void (*proc)(unsigned char *, unsigned char *, void *); | ||
661 | unsigned char addr_buf[4], netmask_buf[4]; | ||
662 | |||
663 | if(dev->open != uml_net_open) return(NOTIFY_DONE); | ||
664 | |||
665 | lp = dev->priv; | ||
666 | |||
667 | proc = NULL; | ||
668 | switch (event){ | ||
669 | case NETDEV_UP: | ||
670 | proc = lp->add_address; | ||
671 | break; | ||
672 | case NETDEV_DOWN: | ||
673 | proc = lp->delete_address; | ||
674 | break; | ||
675 | } | ||
676 | if(proc != NULL){ | ||
677 | addr_buf[0] = addr & 0xff; | ||
678 | addr_buf[1] = (addr >> 8) & 0xff; | ||
679 | addr_buf[2] = (addr >> 16) & 0xff; | ||
680 | addr_buf[3] = addr >> 24; | ||
681 | netmask_buf[0] = netmask & 0xff; | ||
682 | netmask_buf[1] = (netmask >> 8) & 0xff; | ||
683 | netmask_buf[2] = (netmask >> 16) & 0xff; | ||
684 | netmask_buf[3] = netmask >> 24; | ||
685 | (*proc)(addr_buf, netmask_buf, &lp->user); | ||
686 | } | ||
687 | return(NOTIFY_DONE); | ||
688 | } | ||
689 | |||
690 | struct notifier_block uml_inetaddr_notifier = { | ||
691 | .notifier_call = uml_inetaddr_event, | ||
692 | }; | ||
693 | |||
694 | static int uml_net_init(void) | ||
695 | { | ||
696 | struct list_head *ele; | ||
697 | struct uml_net_private *lp; | ||
698 | struct in_device *ip; | ||
699 | struct in_ifaddr *in; | ||
700 | |||
701 | mconsole_register_dev(&net_mc); | ||
702 | register_inetaddr_notifier(¨_inetaddr_notifier); | ||
703 | |||
704 | /* Devices may have been opened already, so the uml_inetaddr_notifier | ||
705 | * didn't get a chance to run for them. This fakes it so that | ||
706 | * addresses which have already been set up get handled properly. | ||
707 | */ | ||
708 | list_for_each(ele, &opened){ | ||
709 | lp = list_entry(ele, struct uml_net_private, list); | ||
710 | ip = lp->dev->ip_ptr; | ||
711 | if(ip == NULL) continue; | ||
712 | in = ip->ifa_list; | ||
713 | while(in != NULL){ | ||
714 | uml_inetaddr_event(NULL, NETDEV_UP, in); | ||
715 | in = in->ifa_next; | ||
716 | } | ||
717 | } | ||
718 | |||
719 | return(0); | ||
720 | } | ||
721 | |||
722 | __initcall(uml_net_init); | ||
723 | |||
724 | static void close_devices(void) | ||
725 | { | ||
726 | struct list_head *ele; | ||
727 | struct uml_net_private *lp; | ||
728 | |||
729 | list_for_each(ele, &opened){ | ||
730 | lp = list_entry(ele, struct uml_net_private, list); | ||
731 | if((lp->close != NULL) && (lp->fd >= 0)) | ||
732 | (*lp->close)(lp->fd, &lp->user); | ||
733 | if(lp->remove != NULL) (*lp->remove)(&lp->user); | ||
734 | } | ||
735 | } | ||
736 | |||
737 | __uml_exitcall(close_devices); | ||
738 | |||
739 | int setup_etheraddr(char *str, unsigned char *addr) | ||
740 | { | ||
741 | char *end; | ||
742 | int i; | ||
743 | |||
744 | if(str == NULL) | ||
745 | return(0); | ||
746 | for(i=0;i<6;i++){ | ||
747 | addr[i] = simple_strtoul(str, &end, 16); | ||
748 | if((end == str) || | ||
749 | ((*end != ':') && (*end != ',') && (*end != '\0'))){ | ||
750 | printk(KERN_ERR | ||
751 | "setup_etheraddr: failed to parse '%s' " | ||
752 | "as an ethernet address\n", str); | ||
753 | return(0); | ||
754 | } | ||
755 | str = end + 1; | ||
756 | } | ||
757 | if(addr[0] & 1){ | ||
758 | printk(KERN_ERR | ||
759 | "Attempt to assign a broadcast ethernet address to a " | ||
760 | "device disallowed\n"); | ||
761 | return(0); | ||
762 | } | ||
763 | return(1); | ||
764 | } | ||
765 | |||
766 | void dev_ip_addr(void *d, char *buf, char *bin_buf) | ||
767 | { | ||
768 | struct net_device *dev = d; | ||
769 | struct in_device *ip = dev->ip_ptr; | ||
770 | struct in_ifaddr *in; | ||
771 | u32 addr; | ||
772 | |||
773 | if((ip == NULL) || ((in = ip->ifa_list) == NULL)){ | ||
774 | printk(KERN_WARNING "dev_ip_addr - device not assigned an " | ||
775 | "IP address\n"); | ||
776 | return; | ||
777 | } | ||
778 | addr = in->ifa_address; | ||
779 | sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff, | ||
780 | (addr >> 16) & 0xff, addr >> 24); | ||
781 | if(bin_buf){ | ||
782 | bin_buf[0] = addr & 0xff; | ||
783 | bin_buf[1] = (addr >> 8) & 0xff; | ||
784 | bin_buf[2] = (addr >> 16) & 0xff; | ||
785 | bin_buf[3] = addr >> 24; | ||
786 | } | ||
787 | } | ||
788 | |||
789 | void set_ether_mac(void *d, unsigned char *addr) | ||
790 | { | ||
791 | struct net_device *dev = d; | ||
792 | |||
793 | memcpy(dev->dev_addr, addr, ETH_ALEN); | ||
794 | } | ||
795 | |||
796 | struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra) | ||
797 | { | ||
798 | if((skb != NULL) && (skb_tailroom(skb) < extra)){ | ||
799 | struct sk_buff *skb2; | ||
800 | |||
801 | skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC); | ||
802 | dev_kfree_skb(skb); | ||
803 | skb = skb2; | ||
804 | } | ||
805 | if(skb != NULL) skb_put(skb, extra); | ||
806 | return(skb); | ||
807 | } | ||
808 | |||
809 | void iter_addresses(void *d, void (*cb)(unsigned char *, unsigned char *, | ||
810 | void *), | ||
811 | void *arg) | ||
812 | { | ||
813 | struct net_device *dev = d; | ||
814 | struct in_device *ip = dev->ip_ptr; | ||
815 | struct in_ifaddr *in; | ||
816 | unsigned char address[4], netmask[4]; | ||
817 | |||
818 | if(ip == NULL) return; | ||
819 | in = ip->ifa_list; | ||
820 | while(in != NULL){ | ||
821 | address[0] = in->ifa_address & 0xff; | ||
822 | address[1] = (in->ifa_address >> 8) & 0xff; | ||
823 | address[2] = (in->ifa_address >> 16) & 0xff; | ||
824 | address[3] = in->ifa_address >> 24; | ||
825 | netmask[0] = in->ifa_mask & 0xff; | ||
826 | netmask[1] = (in->ifa_mask >> 8) & 0xff; | ||
827 | netmask[2] = (in->ifa_mask >> 16) & 0xff; | ||
828 | netmask[3] = in->ifa_mask >> 24; | ||
829 | (*cb)(address, netmask, arg); | ||
830 | in = in->ifa_next; | ||
831 | } | ||
832 | } | ||
833 | |||
834 | int dev_netmask(void *d, void *m) | ||
835 | { | ||
836 | struct net_device *dev = d; | ||
837 | struct in_device *ip = dev->ip_ptr; | ||
838 | struct in_ifaddr *in; | ||
839 | __u32 *mask_out = m; | ||
840 | |||
841 | if(ip == NULL) | ||
842 | return(1); | ||
843 | |||
844 | in = ip->ifa_list; | ||
845 | if(in == NULL) | ||
846 | return(1); | ||
847 | |||
848 | *mask_out = in->ifa_mask; | ||
849 | return(0); | ||
850 | } | ||
851 | |||
852 | void *get_output_buffer(int *len_out) | ||
853 | { | ||
854 | void *ret; | ||
855 | |||
856 | ret = (void *) __get_free_pages(GFP_KERNEL, 0); | ||
857 | if(ret) *len_out = PAGE_SIZE; | ||
858 | else *len_out = 0; | ||
859 | return(ret); | ||
860 | } | ||
861 | |||
862 | void free_output_buffer(void *buffer) | ||
863 | { | ||
864 | free_pages((unsigned long) buffer, 0); | ||
865 | } | ||
866 | |||
867 | int tap_setup_common(char *str, char *type, char **dev_name, char **mac_out, | ||
868 | char **gate_addr) | ||
869 | { | ||
870 | char *remain; | ||
871 | |||
872 | remain = split_if_spec(str, dev_name, mac_out, gate_addr, NULL); | ||
873 | if(remain != NULL){ | ||
874 | printk("tap_setup_common - Extra garbage on specification : " | ||
875 | "'%s'\n", remain); | ||
876 | return(1); | ||
877 | } | ||
878 | |||
879 | return(0); | ||
880 | } | ||
881 | |||
882 | unsigned short eth_protocol(struct sk_buff *skb) | ||
883 | { | ||
884 | return(eth_type_trans(skb, skb->dev)); | ||
885 | } | ||
886 | |||
887 | /* | ||
888 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
889 | * Emacs will notice this stuff at the end of the file and automatically | ||
890 | * adjust the settings for this buffer only. This must remain at the end | ||
891 | * of the file. | ||
892 | * --------------------------------------------------------------------------- | ||
893 | * Local variables: | ||
894 | * c-file-style: "linux" | ||
895 | * End: | ||
896 | */ | ||
diff --git a/arch/um/drivers/net_user.c b/arch/um/drivers/net_user.c new file mode 100644 index 000000000000..47229fe4a813 --- /dev/null +++ b/arch/um/drivers/net_user.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stddef.h> | ||
7 | #include <stdarg.h> | ||
8 | #include <unistd.h> | ||
9 | #include <stdio.h> | ||
10 | #include <errno.h> | ||
11 | #include <stdlib.h> | ||
12 | #include <string.h> | ||
13 | #include <sys/socket.h> | ||
14 | #include <sys/wait.h> | ||
15 | #include "user.h" | ||
16 | #include "user_util.h" | ||
17 | #include "kern_util.h" | ||
18 | #include "net_user.h" | ||
19 | #include "helper.h" | ||
20 | #include "os.h" | ||
21 | |||
22 | int tap_open_common(void *dev, char *gate_addr) | ||
23 | { | ||
24 | int tap_addr[4]; | ||
25 | |||
26 | if(gate_addr == NULL) return(0); | ||
27 | if(sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], | ||
28 | &tap_addr[1], &tap_addr[2], &tap_addr[3]) != 4){ | ||
29 | printk("Invalid tap IP address - '%s'\n", gate_addr); | ||
30 | return(-EINVAL); | ||
31 | } | ||
32 | return(0); | ||
33 | } | ||
34 | |||
35 | void tap_check_ips(char *gate_addr, char *eth_addr) | ||
36 | { | ||
37 | int tap_addr[4]; | ||
38 | |||
39 | if((gate_addr != NULL) && | ||
40 | (sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], | ||
41 | &tap_addr[1], &tap_addr[2], &tap_addr[3]) == 4) && | ||
42 | (eth_addr[0] == tap_addr[0]) && | ||
43 | (eth_addr[1] == tap_addr[1]) && | ||
44 | (eth_addr[2] == tap_addr[2]) && | ||
45 | (eth_addr[3] == tap_addr[3])){ | ||
46 | printk("The tap IP address and the UML eth IP address" | ||
47 | " must be different\n"); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | void read_output(int fd, char *output, int len) | ||
52 | { | ||
53 | int remain, n, actual; | ||
54 | char c; | ||
55 | |||
56 | if(output == NULL){ | ||
57 | output = &c; | ||
58 | len = sizeof(c); | ||
59 | } | ||
60 | |||
61 | *output = '\0'; | ||
62 | n = os_read_file(fd, &remain, sizeof(remain)); | ||
63 | if(n != sizeof(remain)){ | ||
64 | printk("read_output - read of length failed, err = %d\n", -n); | ||
65 | return; | ||
66 | } | ||
67 | |||
68 | while(remain != 0){ | ||
69 | n = (remain < len) ? remain : len; | ||
70 | actual = os_read_file(fd, output, n); | ||
71 | if(actual != n){ | ||
72 | printk("read_output - read of data failed, " | ||
73 | "err = %d\n", -actual); | ||
74 | return; | ||
75 | } | ||
76 | remain -= actual; | ||
77 | } | ||
78 | return; | ||
79 | } | ||
80 | |||
81 | int net_read(int fd, void *buf, int len) | ||
82 | { | ||
83 | int n; | ||
84 | |||
85 | n = os_read_file(fd, buf, len); | ||
86 | |||
87 | if(n == -EAGAIN) | ||
88 | return(0); | ||
89 | else if(n == 0) | ||
90 | return(-ENOTCONN); | ||
91 | return(n); | ||
92 | } | ||
93 | |||
94 | int net_recvfrom(int fd, void *buf, int len) | ||
95 | { | ||
96 | int n; | ||
97 | |||
98 | while(((n = recvfrom(fd, buf, len, 0, NULL, NULL)) < 0) && | ||
99 | (errno == EINTR)) ; | ||
100 | |||
101 | if(n < 0){ | ||
102 | if(errno == EAGAIN) return(0); | ||
103 | return(-errno); | ||
104 | } | ||
105 | else if(n == 0) return(-ENOTCONN); | ||
106 | return(n); | ||
107 | } | ||
108 | |||
109 | int net_write(int fd, void *buf, int len) | ||
110 | { | ||
111 | int n; | ||
112 | |||
113 | n = os_write_file(fd, buf, len); | ||
114 | |||
115 | if(n == -EAGAIN) | ||
116 | return(0); | ||
117 | else if(n == 0) | ||
118 | return(-ENOTCONN); | ||
119 | return(n); | ||
120 | } | ||
121 | |||
122 | int net_send(int fd, void *buf, int len) | ||
123 | { | ||
124 | int n; | ||
125 | |||
126 | while(((n = send(fd, buf, len, 0)) < 0) && (errno == EINTR)) ; | ||
127 | if(n < 0){ | ||
128 | if(errno == EAGAIN) return(0); | ||
129 | return(-errno); | ||
130 | } | ||
131 | else if(n == 0) return(-ENOTCONN); | ||
132 | return(n); | ||
133 | } | ||
134 | |||
135 | int net_sendto(int fd, void *buf, int len, void *to, int sock_len) | ||
136 | { | ||
137 | int n; | ||
138 | |||
139 | while(((n = sendto(fd, buf, len, 0, (struct sockaddr *) to, | ||
140 | sock_len)) < 0) && (errno == EINTR)) ; | ||
141 | if(n < 0){ | ||
142 | if(errno == EAGAIN) return(0); | ||
143 | return(-errno); | ||
144 | } | ||
145 | else if(n == 0) return(-ENOTCONN); | ||
146 | return(n); | ||
147 | } | ||
148 | |||
149 | struct change_pre_exec_data { | ||
150 | int close_me; | ||
151 | int stdout; | ||
152 | }; | ||
153 | |||
154 | static void change_pre_exec(void *arg) | ||
155 | { | ||
156 | struct change_pre_exec_data *data = arg; | ||
157 | |||
158 | os_close_file(data->close_me); | ||
159 | dup2(data->stdout, 1); | ||
160 | } | ||
161 | |||
162 | static int change_tramp(char **argv, char *output, int output_len) | ||
163 | { | ||
164 | int pid, fds[2], err; | ||
165 | struct change_pre_exec_data pe_data; | ||
166 | |||
167 | err = os_pipe(fds, 1, 0); | ||
168 | if(err < 0){ | ||
169 | printk("change_tramp - pipe failed, err = %d\n", -err); | ||
170 | return(err); | ||
171 | } | ||
172 | pe_data.close_me = fds[0]; | ||
173 | pe_data.stdout = fds[1]; | ||
174 | pid = run_helper(change_pre_exec, &pe_data, argv, NULL); | ||
175 | |||
176 | read_output(fds[0], output, output_len); | ||
177 | os_close_file(fds[0]); | ||
178 | os_close_file(fds[1]); | ||
179 | |||
180 | if (pid > 0) | ||
181 | CATCH_EINTR(err = waitpid(pid, NULL, 0)); | ||
182 | return(pid); | ||
183 | } | ||
184 | |||
185 | static void change(char *dev, char *what, unsigned char *addr, | ||
186 | unsigned char *netmask) | ||
187 | { | ||
188 | char addr_buf[sizeof("255.255.255.255\0")]; | ||
189 | char netmask_buf[sizeof("255.255.255.255\0")]; | ||
190 | char version[sizeof("nnnnn\0")]; | ||
191 | char *argv[] = { "uml_net", version, what, dev, addr_buf, | ||
192 | netmask_buf, NULL }; | ||
193 | char *output; | ||
194 | int output_len, pid; | ||
195 | |||
196 | sprintf(version, "%d", UML_NET_VERSION); | ||
197 | sprintf(addr_buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); | ||
198 | sprintf(netmask_buf, "%d.%d.%d.%d", netmask[0], netmask[1], | ||
199 | netmask[2], netmask[3]); | ||
200 | |||
201 | output_len = page_size(); | ||
202 | output = um_kmalloc(output_len); | ||
203 | if(output == NULL) | ||
204 | printk("change : failed to allocate output buffer\n"); | ||
205 | |||
206 | pid = change_tramp(argv, output, output_len); | ||
207 | if(pid < 0) return; | ||
208 | |||
209 | if(output != NULL){ | ||
210 | printk("%s", output); | ||
211 | kfree(output); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | void open_addr(unsigned char *addr, unsigned char *netmask, void *arg) | ||
216 | { | ||
217 | change(arg, "add", addr, netmask); | ||
218 | } | ||
219 | |||
220 | void close_addr(unsigned char *addr, unsigned char *netmask, void *arg) | ||
221 | { | ||
222 | change(arg, "del", addr, netmask); | ||
223 | } | ||
224 | |||
225 | char *split_if_spec(char *str, ...) | ||
226 | { | ||
227 | char **arg, *end; | ||
228 | va_list ap; | ||
229 | |||
230 | va_start(ap, str); | ||
231 | while((arg = va_arg(ap, char **)) != NULL){ | ||
232 | if(*str == '\0') | ||
233 | return(NULL); | ||
234 | end = strchr(str, ','); | ||
235 | if(end != str) | ||
236 | *arg = str; | ||
237 | if(end == NULL) | ||
238 | return(NULL); | ||
239 | *end++ = '\0'; | ||
240 | str = end; | ||
241 | } | ||
242 | va_end(ap); | ||
243 | return(str); | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
248 | * Emacs will notice this stuff at the end of the file and automatically | ||
249 | * adjust the settings for this buffer only. This must remain at the end | ||
250 | * of the file. | ||
251 | * --------------------------------------------------------------------------- | ||
252 | * Local variables: | ||
253 | * c-file-style: "linux" | ||
254 | * End: | ||
255 | */ | ||
diff --git a/arch/um/drivers/null.c b/arch/um/drivers/null.c new file mode 100644 index 000000000000..14cc5f78398a --- /dev/null +++ b/arch/um/drivers/null.c | |||
@@ -0,0 +1,56 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdlib.h> | ||
7 | #include <errno.h> | ||
8 | #include "chan_user.h" | ||
9 | #include "os.h" | ||
10 | |||
11 | static int null_chan; | ||
12 | |||
13 | static void *null_init(char *str, int device, struct chan_opts *opts) | ||
14 | { | ||
15 | return(&null_chan); | ||
16 | } | ||
17 | |||
18 | static int null_open(int input, int output, int primary, void *d, | ||
19 | char **dev_out) | ||
20 | { | ||
21 | *dev_out = NULL; | ||
22 | return(os_open_file(DEV_NULL, of_rdwr(OPENFLAGS()), 0)); | ||
23 | } | ||
24 | |||
25 | static int null_read(int fd, char *c_out, void *unused) | ||
26 | { | ||
27 | return(-ENODEV); | ||
28 | } | ||
29 | |||
30 | static void null_free(void *data) | ||
31 | { | ||
32 | } | ||
33 | |||
34 | struct chan_ops null_ops = { | ||
35 | .type = "null", | ||
36 | .init = null_init, | ||
37 | .open = null_open, | ||
38 | .close = generic_close, | ||
39 | .read = null_read, | ||
40 | .write = generic_write, | ||
41 | .console_write = generic_console_write, | ||
42 | .window_size = generic_window_size, | ||
43 | .free = null_free, | ||
44 | .winch = 0, | ||
45 | }; | ||
46 | |||
47 | /* | ||
48 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
49 | * Emacs will notice this stuff at the end of the file and automatically | ||
50 | * adjust the settings for this buffer only. This must remain at the end | ||
51 | * of the file. | ||
52 | * --------------------------------------------------------------------------- | ||
53 | * Local variables: | ||
54 | * c-file-style: "linux" | ||
55 | * End: | ||
56 | */ | ||
diff --git a/arch/um/drivers/pcap_kern.c b/arch/um/drivers/pcap_kern.c new file mode 100644 index 000000000000..07c80f2156ef --- /dev/null +++ b/arch/um/drivers/pcap_kern.c | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike <jdike@karaya.com> | ||
3 | * Licensed under the GPL. | ||
4 | */ | ||
5 | |||
6 | #include "linux/init.h" | ||
7 | #include "linux/netdevice.h" | ||
8 | #include "linux/etherdevice.h" | ||
9 | #include "net_kern.h" | ||
10 | #include "net_user.h" | ||
11 | #include "pcap_user.h" | ||
12 | |||
13 | struct pcap_init { | ||
14 | char *host_if; | ||
15 | int promisc; | ||
16 | int optimize; | ||
17 | char *filter; | ||
18 | }; | ||
19 | |||
20 | void pcap_init(struct net_device *dev, void *data) | ||
21 | { | ||
22 | struct uml_net_private *pri; | ||
23 | struct pcap_data *ppri; | ||
24 | struct pcap_init *init = data; | ||
25 | |||
26 | pri = dev->priv; | ||
27 | ppri = (struct pcap_data *) pri->user; | ||
28 | ppri->host_if = init->host_if; | ||
29 | ppri->promisc = init->promisc; | ||
30 | ppri->optimize = init->optimize; | ||
31 | ppri->filter = init->filter; | ||
32 | } | ||
33 | |||
34 | static int pcap_read(int fd, struct sk_buff **skb, | ||
35 | struct uml_net_private *lp) | ||
36 | { | ||
37 | *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); | ||
38 | if(*skb == NULL) return(-ENOMEM); | ||
39 | return(pcap_user_read(fd, (*skb)->mac.raw, | ||
40 | (*skb)->dev->mtu + ETH_HEADER_OTHER, | ||
41 | (struct pcap_data *) &lp->user)); | ||
42 | } | ||
43 | |||
44 | static int pcap_write(int fd, struct sk_buff **skb, struct uml_net_private *lp) | ||
45 | { | ||
46 | return(-EPERM); | ||
47 | } | ||
48 | |||
49 | static struct net_kern_info pcap_kern_info = { | ||
50 | .init = pcap_init, | ||
51 | .protocol = eth_protocol, | ||
52 | .read = pcap_read, | ||
53 | .write = pcap_write, | ||
54 | }; | ||
55 | |||
56 | int pcap_setup(char *str, char **mac_out, void *data) | ||
57 | { | ||
58 | struct pcap_init *init = data; | ||
59 | char *remain, *host_if = NULL, *options[2] = { NULL, NULL }; | ||
60 | int i; | ||
61 | |||
62 | *init = ((struct pcap_init) | ||
63 | { .host_if = "eth0", | ||
64 | .promisc = 1, | ||
65 | .optimize = 0, | ||
66 | .filter = NULL }); | ||
67 | |||
68 | remain = split_if_spec(str, &host_if, &init->filter, | ||
69 | &options[0], &options[1], NULL); | ||
70 | if(remain != NULL){ | ||
71 | printk(KERN_ERR "pcap_setup - Extra garbage on " | ||
72 | "specification : '%s'\n", remain); | ||
73 | return(0); | ||
74 | } | ||
75 | |||
76 | if(host_if != NULL) | ||
77 | init->host_if = host_if; | ||
78 | |||
79 | for(i = 0; i < sizeof(options)/sizeof(options[0]); i++){ | ||
80 | if(options[i] == NULL) | ||
81 | continue; | ||
82 | if(!strcmp(options[i], "promisc")) | ||
83 | init->promisc = 1; | ||
84 | else if(!strcmp(options[i], "nopromisc")) | ||
85 | init->promisc = 0; | ||
86 | else if(!strcmp(options[i], "optimize")) | ||
87 | init->optimize = 1; | ||
88 | else if(!strcmp(options[i], "nooptimize")) | ||
89 | init->optimize = 0; | ||
90 | else printk("pcap_setup : bad option - '%s'\n", options[i]); | ||
91 | } | ||
92 | |||
93 | return(1); | ||
94 | } | ||
95 | |||
96 | static struct transport pcap_transport = { | ||
97 | .list = LIST_HEAD_INIT(pcap_transport.list), | ||
98 | .name = "pcap", | ||
99 | .setup = pcap_setup, | ||
100 | .user = &pcap_user_info, | ||
101 | .kern = &pcap_kern_info, | ||
102 | .private_size = sizeof(struct pcap_data), | ||
103 | .setup_size = sizeof(struct pcap_init), | ||
104 | }; | ||
105 | |||
106 | static int register_pcap(void) | ||
107 | { | ||
108 | register_transport(&pcap_transport); | ||
109 | return(1); | ||
110 | } | ||
111 | |||
112 | __initcall(register_pcap); | ||
113 | |||
114 | /* | ||
115 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
116 | * Emacs will notice this stuff at the end of the file and automatically | ||
117 | * adjust the settings for this buffer only. This must remain at the end | ||
118 | * of the file. | ||
119 | * --------------------------------------------------------------------------- | ||
120 | * Local variables: | ||
121 | * c-file-style: "linux" | ||
122 | * End: | ||
123 | */ | ||
diff --git a/arch/um/drivers/pcap_user.c b/arch/um/drivers/pcap_user.c new file mode 100644 index 000000000000..edfcb29273e1 --- /dev/null +++ b/arch/um/drivers/pcap_user.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike <jdike@karaya.com> | ||
3 | * Licensed under the GPL. | ||
4 | */ | ||
5 | |||
6 | #include <unistd.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <string.h> | ||
9 | #include <errno.h> | ||
10 | #include <pcap.h> | ||
11 | #include <asm/types.h> | ||
12 | #include "net_user.h" | ||
13 | #include "pcap_user.h" | ||
14 | #include "user.h" | ||
15 | |||
16 | #define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER) | ||
17 | |||
18 | #define PCAP_FD(p) (*(int *)(p)) | ||
19 | |||
20 | static void pcap_user_init(void *data, void *dev) | ||
21 | { | ||
22 | struct pcap_data *pri = data; | ||
23 | pcap_t *p; | ||
24 | char errors[PCAP_ERRBUF_SIZE]; | ||
25 | |||
26 | p = pcap_open_live(pri->host_if, MAX_PACKET, pri->promisc, 0, errors); | ||
27 | if(p == NULL){ | ||
28 | printk("pcap_user_init : pcap_open_live failed - '%s'\n", | ||
29 | errors); | ||
30 | return; | ||
31 | } | ||
32 | |||
33 | pri->dev = dev; | ||
34 | pri->pcap = p; | ||
35 | } | ||
36 | |||
37 | static int pcap_open(void *data) | ||
38 | { | ||
39 | struct pcap_data *pri = data; | ||
40 | __u32 netmask; | ||
41 | int err; | ||
42 | |||
43 | if(pri->pcap == NULL) | ||
44 | return(-ENODEV); | ||
45 | |||
46 | if(pri->filter != NULL){ | ||
47 | err = dev_netmask(pri->dev, &netmask); | ||
48 | if(err < 0){ | ||
49 | printk("pcap_open : dev_netmask failed\n"); | ||
50 | return(-EIO); | ||
51 | } | ||
52 | |||
53 | pri->compiled = um_kmalloc(sizeof(struct bpf_program)); | ||
54 | if(pri->compiled == NULL){ | ||
55 | printk("pcap_open : kmalloc failed\n"); | ||
56 | return(-ENOMEM); | ||
57 | } | ||
58 | |||
59 | err = pcap_compile(pri->pcap, | ||
60 | (struct bpf_program *) pri->compiled, | ||
61 | pri->filter, pri->optimize, netmask); | ||
62 | if(err < 0){ | ||
63 | printk("pcap_open : pcap_compile failed - '%s'\n", | ||
64 | pcap_geterr(pri->pcap)); | ||
65 | return(-EIO); | ||
66 | } | ||
67 | |||
68 | err = pcap_setfilter(pri->pcap, pri->compiled); | ||
69 | if(err < 0){ | ||
70 | printk("pcap_open : pcap_setfilter failed - '%s'\n", | ||
71 | pcap_geterr(pri->pcap)); | ||
72 | return(-EIO); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | return(PCAP_FD(pri->pcap)); | ||
77 | } | ||
78 | |||
79 | static void pcap_remove(void *data) | ||
80 | { | ||
81 | struct pcap_data *pri = data; | ||
82 | |||
83 | if(pri->compiled != NULL) | ||
84 | pcap_freecode(pri->compiled); | ||
85 | |||
86 | pcap_close(pri->pcap); | ||
87 | } | ||
88 | |||
89 | struct pcap_handler_data { | ||
90 | char *buffer; | ||
91 | int len; | ||
92 | }; | ||
93 | |||
94 | static void handler(u_char *data, const struct pcap_pkthdr *header, | ||
95 | const u_char *packet) | ||
96 | { | ||
97 | int len; | ||
98 | |||
99 | struct pcap_handler_data *hdata = (struct pcap_handler_data *) data; | ||
100 | |||
101 | len = hdata->len < header->caplen ? hdata->len : header->caplen; | ||
102 | memcpy(hdata->buffer, packet, len); | ||
103 | hdata->len = len; | ||
104 | } | ||
105 | |||
106 | int pcap_user_read(int fd, void *buffer, int len, struct pcap_data *pri) | ||
107 | { | ||
108 | struct pcap_handler_data hdata = ((struct pcap_handler_data) | ||
109 | { .buffer = buffer, | ||
110 | .len = len }); | ||
111 | int n; | ||
112 | |||
113 | n = pcap_dispatch(pri->pcap, 1, handler, (u_char *) &hdata); | ||
114 | if(n < 0){ | ||
115 | printk("pcap_dispatch failed - %s\n", pcap_geterr(pri->pcap)); | ||
116 | return(-EIO); | ||
117 | } | ||
118 | else if(n == 0) | ||
119 | return(0); | ||
120 | return(hdata.len); | ||
121 | } | ||
122 | |||
123 | struct net_user_info pcap_user_info = { | ||
124 | .init = pcap_user_init, | ||
125 | .open = pcap_open, | ||
126 | .close = NULL, | ||
127 | .remove = pcap_remove, | ||
128 | .set_mtu = NULL, | ||
129 | .add_address = NULL, | ||
130 | .delete_address = NULL, | ||
131 | .max_packet = MAX_PACKET - ETH_HEADER_OTHER | ||
132 | }; | ||
133 | |||
134 | /* | ||
135 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
136 | * Emacs will notice this stuff at the end of the file and automatically | ||
137 | * adjust the settings for this buffer only. This must remain at the end | ||
138 | * of the file. | ||
139 | * --------------------------------------------------------------------------- | ||
140 | * Local variables: | ||
141 | * c-file-style: "linux" | ||
142 | * End: | ||
143 | */ | ||
diff --git a/arch/um/drivers/pcap_user.h b/arch/um/drivers/pcap_user.h new file mode 100644 index 000000000000..58f9f6a1420f --- /dev/null +++ b/arch/um/drivers/pcap_user.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "net_user.h" | ||
7 | |||
8 | struct pcap_data { | ||
9 | char *host_if; | ||
10 | int promisc; | ||
11 | int optimize; | ||
12 | char *filter; | ||
13 | void *compiled; | ||
14 | void *pcap; | ||
15 | void *dev; | ||
16 | }; | ||
17 | |||
18 | extern struct net_user_info pcap_user_info; | ||
19 | |||
20 | extern int pcap_user_read(int fd, void *buf, int len, struct pcap_data *pri); | ||
21 | |||
22 | /* | ||
23 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
24 | * Emacs will notice this stuff at the end of the file and automatically | ||
25 | * adjust the settings for this buffer only. This must remain at the end | ||
26 | * of the file. | ||
27 | * --------------------------------------------------------------------------- | ||
28 | * Local variables: | ||
29 | * c-file-style: "linux" | ||
30 | * End: | ||
31 | */ | ||
diff --git a/arch/um/drivers/port.h b/arch/um/drivers/port.h new file mode 100644 index 000000000000..9117609a575d --- /dev/null +++ b/arch/um/drivers/port.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __PORT_H__ | ||
7 | #define __PORT_H__ | ||
8 | |||
9 | extern void *port_data(int port); | ||
10 | extern int port_wait(void *data); | ||
11 | extern void port_kern_close(void *d); | ||
12 | extern int port_connection(int fd, int *socket_out, int *pid_out); | ||
13 | extern int port_listen_fd(int port); | ||
14 | extern void port_read(int fd, void *data); | ||
15 | extern void port_kern_free(void *d); | ||
16 | extern int port_rcv_fd(int fd); | ||
17 | extern void port_remove_dev(void *d); | ||
18 | |||
19 | #endif | ||
20 | |||
21 | /* | ||
22 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
23 | * Emacs will notice this stuff at the end of the file and automatically | ||
24 | * adjust the settings for this buffer only. This must remain at the end | ||
25 | * of the file. | ||
26 | * --------------------------------------------------------------------------- | ||
27 | * Local variables: | ||
28 | * c-file-style: "linux" | ||
29 | * End: | ||
30 | */ | ||
diff --git a/arch/um/drivers/port_kern.c b/arch/um/drivers/port_kern.c new file mode 100644 index 000000000000..b5ee07472f79 --- /dev/null +++ b/arch/um/drivers/port_kern.c | |||
@@ -0,0 +1,309 @@ | |||
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 | |||
24 | struct 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 | |||
36 | struct port_dev { | ||
37 | struct port_list *port; | ||
38 | int helper_pid; | ||
39 | int telnetd_pid; | ||
40 | }; | ||
41 | |||
42 | struct 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 | |||
51 | static 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 | |||
82 | static 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 | |||
133 | DECLARE_MUTEX(ports_sem); | ||
134 | struct list_head ports = LIST_HEAD_INIT(ports); | ||
135 | |||
136 | void 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 | |||
154 | DECLARE_WORK(port_work, port_work_proc, NULL); | ||
155 | |||
156 | static 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 | |||
165 | void *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 | |||
229 | int 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 | |||
277 | void 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 | |||
289 | void port_kern_free(void *d) | ||
290 | { | ||
291 | struct port_dev *dev = d; | ||
292 | |||
293 | port_remove_dev(dev); | ||
294 | kfree(dev); | ||
295 | } | ||
296 | |||
297 | static 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); | ||
diff --git a/arch/um/drivers/port_user.c b/arch/um/drivers/port_user.c new file mode 100644 index 000000000000..14dd2002d2da --- /dev/null +++ b/arch/um/drivers/port_user.c | |||
@@ -0,0 +1,225 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <stddef.h> | ||
8 | #include <stdlib.h> | ||
9 | #include <string.h> | ||
10 | #include <errno.h> | ||
11 | #include <unistd.h> | ||
12 | #include <termios.h> | ||
13 | #include <sys/socket.h> | ||
14 | #include <sys/un.h> | ||
15 | #include <netinet/in.h> | ||
16 | #include "user_util.h" | ||
17 | #include "kern_util.h" | ||
18 | #include "user.h" | ||
19 | #include "chan_user.h" | ||
20 | #include "port.h" | ||
21 | #include "helper.h" | ||
22 | #include "os.h" | ||
23 | |||
24 | struct port_chan { | ||
25 | int raw; | ||
26 | struct termios tt; | ||
27 | void *kernel_data; | ||
28 | char dev[sizeof("32768\0")]; | ||
29 | }; | ||
30 | |||
31 | static void *port_init(char *str, int device, struct chan_opts *opts) | ||
32 | { | ||
33 | struct port_chan *data; | ||
34 | void *kern_data; | ||
35 | char *end; | ||
36 | int port; | ||
37 | |||
38 | if(*str != ':'){ | ||
39 | printk("port_init : channel type 'port' must specify a " | ||
40 | "port number\n"); | ||
41 | return(NULL); | ||
42 | } | ||
43 | str++; | ||
44 | port = strtoul(str, &end, 0); | ||
45 | if((*end != '\0') || (end == str)){ | ||
46 | printk("port_init : couldn't parse port '%s'\n", str); | ||
47 | return(NULL); | ||
48 | } | ||
49 | |||
50 | kern_data = port_data(port); | ||
51 | if(kern_data == NULL) | ||
52 | return(NULL); | ||
53 | |||
54 | data = um_kmalloc(sizeof(*data)); | ||
55 | if(data == NULL) | ||
56 | goto err; | ||
57 | |||
58 | *data = ((struct port_chan) { .raw = opts->raw, | ||
59 | .kernel_data = kern_data }); | ||
60 | sprintf(data->dev, "%d", port); | ||
61 | |||
62 | return(data); | ||
63 | err: | ||
64 | port_kern_free(kern_data); | ||
65 | return(NULL); | ||
66 | } | ||
67 | |||
68 | static void port_free(void *d) | ||
69 | { | ||
70 | struct port_chan *data = d; | ||
71 | |||
72 | port_kern_free(data->kernel_data); | ||
73 | kfree(data); | ||
74 | } | ||
75 | |||
76 | static int port_open(int input, int output, int primary, void *d, | ||
77 | char **dev_out) | ||
78 | { | ||
79 | struct port_chan *data = d; | ||
80 | int fd, err; | ||
81 | |||
82 | fd = port_wait(data->kernel_data); | ||
83 | if((fd >= 0) && data->raw){ | ||
84 | CATCH_EINTR(err = tcgetattr(fd, &data->tt)); | ||
85 | if(err) | ||
86 | return(err); | ||
87 | |||
88 | err = raw(fd); | ||
89 | if(err) | ||
90 | return(err); | ||
91 | } | ||
92 | *dev_out = data->dev; | ||
93 | return(fd); | ||
94 | } | ||
95 | |||
96 | static void port_close(int fd, void *d) | ||
97 | { | ||
98 | struct port_chan *data = d; | ||
99 | |||
100 | port_remove_dev(data->kernel_data); | ||
101 | os_close_file(fd); | ||
102 | } | ||
103 | |||
104 | static int port_console_write(int fd, const char *buf, int n, void *d) | ||
105 | { | ||
106 | struct port_chan *data = d; | ||
107 | |||
108 | return(generic_console_write(fd, buf, n, &data->tt)); | ||
109 | } | ||
110 | |||
111 | struct chan_ops port_ops = { | ||
112 | .type = "port", | ||
113 | .init = port_init, | ||
114 | .open = port_open, | ||
115 | .close = port_close, | ||
116 | .read = generic_read, | ||
117 | .write = generic_write, | ||
118 | .console_write = port_console_write, | ||
119 | .window_size = generic_window_size, | ||
120 | .free = port_free, | ||
121 | .winch = 1, | ||
122 | }; | ||
123 | |||
124 | int port_listen_fd(int port) | ||
125 | { | ||
126 | struct sockaddr_in addr; | ||
127 | int fd, err, arg; | ||
128 | |||
129 | fd = socket(PF_INET, SOCK_STREAM, 0); | ||
130 | if(fd == -1) | ||
131 | return(-errno); | ||
132 | |||
133 | arg = 1; | ||
134 | if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0){ | ||
135 | err = -errno; | ||
136 | goto out; | ||
137 | } | ||
138 | |||
139 | addr.sin_family = AF_INET; | ||
140 | addr.sin_port = htons(port); | ||
141 | addr.sin_addr.s_addr = htonl(INADDR_ANY); | ||
142 | if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0){ | ||
143 | err = -errno; | ||
144 | goto out; | ||
145 | } | ||
146 | |||
147 | if(listen(fd, 1) < 0){ | ||
148 | err = -errno; | ||
149 | goto out; | ||
150 | } | ||
151 | |||
152 | err = os_set_fd_block(fd, 0); | ||
153 | if(err < 0) | ||
154 | goto out; | ||
155 | |||
156 | return(fd); | ||
157 | out: | ||
158 | os_close_file(fd); | ||
159 | return(err); | ||
160 | } | ||
161 | |||
162 | struct port_pre_exec_data { | ||
163 | int sock_fd; | ||
164 | int pipe_fd; | ||
165 | }; | ||
166 | |||
167 | void port_pre_exec(void *arg) | ||
168 | { | ||
169 | struct port_pre_exec_data *data = arg; | ||
170 | |||
171 | dup2(data->sock_fd, 0); | ||
172 | dup2(data->sock_fd, 1); | ||
173 | dup2(data->sock_fd, 2); | ||
174 | os_close_file(data->sock_fd); | ||
175 | dup2(data->pipe_fd, 3); | ||
176 | os_shutdown_socket(3, 1, 0); | ||
177 | os_close_file(data->pipe_fd); | ||
178 | } | ||
179 | |||
180 | int port_connection(int fd, int *socket, int *pid_out) | ||
181 | { | ||
182 | int new, err; | ||
183 | char *argv[] = { "/usr/sbin/in.telnetd", "-L", | ||
184 | "/usr/lib/uml/port-helper", NULL }; | ||
185 | struct port_pre_exec_data data; | ||
186 | |||
187 | new = os_accept_connection(fd); | ||
188 | if(new < 0) | ||
189 | return(new); | ||
190 | |||
191 | err = os_pipe(socket, 0, 0); | ||
192 | if(err < 0) | ||
193 | goto out_close; | ||
194 | |||
195 | data = ((struct port_pre_exec_data) | ||
196 | { .sock_fd = new, | ||
197 | .pipe_fd = socket[1] }); | ||
198 | |||
199 | err = run_helper(port_pre_exec, &data, argv, NULL); | ||
200 | if(err < 0) | ||
201 | goto out_shutdown; | ||
202 | |||
203 | *pid_out = err; | ||
204 | return(new); | ||
205 | |||
206 | out_shutdown: | ||
207 | os_shutdown_socket(socket[0], 1, 1); | ||
208 | os_close_file(socket[0]); | ||
209 | os_shutdown_socket(socket[1], 1, 1); | ||
210 | os_close_file(socket[1]); | ||
211 | out_close: | ||
212 | os_close_file(new); | ||
213 | return(err); | ||
214 | } | ||
215 | |||
216 | /* | ||
217 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
218 | * Emacs will notice this stuff at the end of the file and automatically | ||
219 | * adjust the settings for this buffer only. This must remain at the end | ||
220 | * of the file. | ||
221 | * --------------------------------------------------------------------------- | ||
222 | * Local variables: | ||
223 | * c-file-style: "linux" | ||
224 | * End: | ||
225 | */ | ||
diff --git a/arch/um/drivers/pty.c b/arch/um/drivers/pty.c new file mode 100644 index 000000000000..ed84d01df6cc --- /dev/null +++ b/arch/um/drivers/pty.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <unistd.h> | ||
8 | #include <string.h> | ||
9 | #include <errno.h> | ||
10 | #include <termios.h> | ||
11 | #include "chan_user.h" | ||
12 | #include "user.h" | ||
13 | #include "user_util.h" | ||
14 | #include "kern_util.h" | ||
15 | #include "os.h" | ||
16 | |||
17 | struct pty_chan { | ||
18 | void (*announce)(char *dev_name, int dev); | ||
19 | int dev; | ||
20 | int raw; | ||
21 | struct termios tt; | ||
22 | char dev_name[sizeof("/dev/pts/0123456\0")]; | ||
23 | }; | ||
24 | |||
25 | static void *pty_chan_init(char *str, int device, struct chan_opts *opts) | ||
26 | { | ||
27 | struct pty_chan *data; | ||
28 | |||
29 | data = um_kmalloc(sizeof(*data)); | ||
30 | if(data == NULL) return(NULL); | ||
31 | *data = ((struct pty_chan) { .announce = opts->announce, | ||
32 | .dev = device, | ||
33 | .raw = opts->raw }); | ||
34 | return(data); | ||
35 | } | ||
36 | |||
37 | static int pts_open(int input, int output, int primary, void *d, | ||
38 | char **dev_out) | ||
39 | { | ||
40 | struct pty_chan *data = d; | ||
41 | char *dev; | ||
42 | int fd, err; | ||
43 | |||
44 | fd = get_pty(); | ||
45 | if(fd < 0){ | ||
46 | printk("open_pts : Failed to open pts\n"); | ||
47 | return(-errno); | ||
48 | } | ||
49 | if(data->raw){ | ||
50 | CATCH_EINTR(err = tcgetattr(fd, &data->tt)); | ||
51 | if(err) | ||
52 | return(err); | ||
53 | |||
54 | err = raw(fd); | ||
55 | if(err) | ||
56 | return(err); | ||
57 | } | ||
58 | |||
59 | dev = ptsname(fd); | ||
60 | sprintf(data->dev_name, "%s", dev); | ||
61 | *dev_out = data->dev_name; | ||
62 | if (data->announce) | ||
63 | (*data->announce)(dev, data->dev); | ||
64 | return(fd); | ||
65 | } | ||
66 | |||
67 | static int getmaster(char *line) | ||
68 | { | ||
69 | char *pty, *bank, *cp; | ||
70 | int master, err; | ||
71 | |||
72 | pty = &line[strlen("/dev/ptyp")]; | ||
73 | for (bank = "pqrs"; *bank; bank++) { | ||
74 | line[strlen("/dev/pty")] = *bank; | ||
75 | *pty = '0'; | ||
76 | if (os_stat_file(line, NULL) < 0) | ||
77 | break; | ||
78 | for (cp = "0123456789abcdef"; *cp; cp++) { | ||
79 | *pty = *cp; | ||
80 | master = os_open_file(line, of_rdwr(OPENFLAGS()), 0); | ||
81 | if (master >= 0) { | ||
82 | char *tp = &line[strlen("/dev/")]; | ||
83 | |||
84 | /* verify slave side is usable */ | ||
85 | *tp = 't'; | ||
86 | err = os_access(line, OS_ACC_RW_OK); | ||
87 | *tp = 'p'; | ||
88 | if(err == 0) return(master); | ||
89 | (void) os_close_file(master); | ||
90 | } | ||
91 | } | ||
92 | } | ||
93 | return(-1); | ||
94 | } | ||
95 | |||
96 | static int pty_open(int input, int output, int primary, void *d, | ||
97 | char **dev_out) | ||
98 | { | ||
99 | struct pty_chan *data = d; | ||
100 | int fd, err; | ||
101 | char dev[sizeof("/dev/ptyxx\0")] = "/dev/ptyxx"; | ||
102 | |||
103 | fd = getmaster(dev); | ||
104 | if(fd < 0) | ||
105 | return(-errno); | ||
106 | |||
107 | if(data->raw){ | ||
108 | err = raw(fd); | ||
109 | if(err) | ||
110 | return(err); | ||
111 | } | ||
112 | |||
113 | if(data->announce) (*data->announce)(dev, data->dev); | ||
114 | |||
115 | sprintf(data->dev_name, "%s", dev); | ||
116 | *dev_out = data->dev_name; | ||
117 | return(fd); | ||
118 | } | ||
119 | |||
120 | static int pty_console_write(int fd, const char *buf, int n, void *d) | ||
121 | { | ||
122 | struct pty_chan *data = d; | ||
123 | |||
124 | return(generic_console_write(fd, buf, n, &data->tt)); | ||
125 | } | ||
126 | |||
127 | struct chan_ops pty_ops = { | ||
128 | .type = "pty", | ||
129 | .init = pty_chan_init, | ||
130 | .open = pty_open, | ||
131 | .close = generic_close, | ||
132 | .read = generic_read, | ||
133 | .write = generic_write, | ||
134 | .console_write = pty_console_write, | ||
135 | .window_size = generic_window_size, | ||
136 | .free = generic_free, | ||
137 | .winch = 0, | ||
138 | }; | ||
139 | |||
140 | struct chan_ops pts_ops = { | ||
141 | .type = "pts", | ||
142 | .init = pty_chan_init, | ||
143 | .open = pts_open, | ||
144 | .close = generic_close, | ||
145 | .read = generic_read, | ||
146 | .write = generic_write, | ||
147 | .console_write = pty_console_write, | ||
148 | .window_size = generic_window_size, | ||
149 | .free = generic_free, | ||
150 | .winch = 0, | ||
151 | }; | ||
152 | |||
153 | /* | ||
154 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
155 | * Emacs will notice this stuff at the end of the file and automatically | ||
156 | * adjust the settings for this buffer only. This must remain at the end | ||
157 | * of the file. | ||
158 | * --------------------------------------------------------------------------- | ||
159 | * Local variables: | ||
160 | * c-file-style: "linux" | ||
161 | * End: | ||
162 | */ | ||
diff --git a/arch/um/drivers/random.c b/arch/um/drivers/random.c new file mode 100644 index 000000000000..d43e9fab05a7 --- /dev/null +++ b/arch/um/drivers/random.c | |||
@@ -0,0 +1,122 @@ | |||
1 | /* Much of this ripped from hw_random.c */ | ||
2 | |||
3 | #include <linux/module.h> | ||
4 | #include <linux/fs.h> | ||
5 | #include <linux/miscdevice.h> | ||
6 | #include <linux/delay.h> | ||
7 | #include <asm/uaccess.h> | ||
8 | #include "os.h" | ||
9 | |||
10 | /* | ||
11 | * core module and version information | ||
12 | */ | ||
13 | #define RNG_VERSION "1.0.0" | ||
14 | #define RNG_MODULE_NAME "random" | ||
15 | #define RNG_DRIVER_NAME RNG_MODULE_NAME " virtual driver " RNG_VERSION | ||
16 | #define PFX RNG_MODULE_NAME ": " | ||
17 | |||
18 | #define RNG_MISCDEV_MINOR 183 /* official */ | ||
19 | |||
20 | static int random_fd = -1; | ||
21 | |||
22 | static int rng_dev_open (struct inode *inode, struct file *filp) | ||
23 | { | ||
24 | /* enforce read-only access to this chrdev */ | ||
25 | if ((filp->f_mode & FMODE_READ) == 0) | ||
26 | return -EINVAL; | ||
27 | if (filp->f_mode & FMODE_WRITE) | ||
28 | return -EINVAL; | ||
29 | |||
30 | return 0; | ||
31 | } | ||
32 | |||
33 | static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size, | ||
34 | loff_t * offp) | ||
35 | { | ||
36 | u32 data; | ||
37 | int n, ret = 0, have_data; | ||
38 | |||
39 | while(size){ | ||
40 | n = os_read_file(random_fd, &data, sizeof(data)); | ||
41 | if(n > 0){ | ||
42 | have_data = n; | ||
43 | while (have_data && size) { | ||
44 | if (put_user((u8)data, buf++)) { | ||
45 | ret = ret ? : -EFAULT; | ||
46 | break; | ||
47 | } | ||
48 | size--; | ||
49 | ret++; | ||
50 | have_data--; | ||
51 | data>>=8; | ||
52 | } | ||
53 | } | ||
54 | else if(n == -EAGAIN){ | ||
55 | if (filp->f_flags & O_NONBLOCK) | ||
56 | return ret ? : -EAGAIN; | ||
57 | |||
58 | if(need_resched()){ | ||
59 | current->state = TASK_INTERRUPTIBLE; | ||
60 | schedule_timeout(1); | ||
61 | } | ||
62 | } | ||
63 | else return n; | ||
64 | if (signal_pending (current)) | ||
65 | return ret ? : -ERESTARTSYS; | ||
66 | } | ||
67 | return ret; | ||
68 | } | ||
69 | |||
70 | static struct file_operations rng_chrdev_ops = { | ||
71 | .owner = THIS_MODULE, | ||
72 | .open = rng_dev_open, | ||
73 | .read = rng_dev_read, | ||
74 | }; | ||
75 | |||
76 | static struct miscdevice rng_miscdev = { | ||
77 | RNG_MISCDEV_MINOR, | ||
78 | RNG_MODULE_NAME, | ||
79 | &rng_chrdev_ops, | ||
80 | }; | ||
81 | |||
82 | /* | ||
83 | * rng_init - initialize RNG module | ||
84 | */ | ||
85 | static int __init rng_init (void) | ||
86 | { | ||
87 | int err; | ||
88 | |||
89 | err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0); | ||
90 | if(err < 0) | ||
91 | goto out; | ||
92 | |||
93 | random_fd = err; | ||
94 | |||
95 | err = os_set_fd_block(random_fd, 0); | ||
96 | if(err) | ||
97 | goto err_out_cleanup_hw; | ||
98 | |||
99 | err = misc_register (&rng_miscdev); | ||
100 | if (err) { | ||
101 | printk (KERN_ERR PFX "misc device register failed\n"); | ||
102 | goto err_out_cleanup_hw; | ||
103 | } | ||
104 | |||
105 | out: | ||
106 | return err; | ||
107 | |||
108 | err_out_cleanup_hw: | ||
109 | random_fd = -1; | ||
110 | goto out; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * rng_cleanup - shutdown RNG module | ||
115 | */ | ||
116 | static void __exit rng_cleanup (void) | ||
117 | { | ||
118 | misc_deregister (&rng_miscdev); | ||
119 | } | ||
120 | |||
121 | module_init (rng_init); | ||
122 | module_exit (rng_cleanup); | ||
diff --git a/arch/um/drivers/slip.h b/arch/um/drivers/slip.h new file mode 100644 index 000000000000..495f2f1b1420 --- /dev/null +++ b/arch/um/drivers/slip.h | |||
@@ -0,0 +1,39 @@ | |||
1 | #ifndef __UM_SLIP_H | ||
2 | #define __UM_SLIP_H | ||
3 | |||
4 | #define BUF_SIZE 1500 | ||
5 | /* two bytes each for a (pathological) max packet of escaped chars + * | ||
6 | * terminating END char + initial END char */ | ||
7 | #define ENC_BUF_SIZE (2 * BUF_SIZE + 2) | ||
8 | |||
9 | struct slip_data { | ||
10 | void *dev; | ||
11 | char name[sizeof("slnnnnn\0")]; | ||
12 | char *addr; | ||
13 | char *gate_addr; | ||
14 | int slave; | ||
15 | char ibuf[ENC_BUF_SIZE]; | ||
16 | char obuf[ENC_BUF_SIZE]; | ||
17 | int more; /* more data: do not read fd until ibuf has been drained */ | ||
18 | int pos; | ||
19 | int esc; | ||
20 | }; | ||
21 | |||
22 | extern struct net_user_info slip_user_info; | ||
23 | |||
24 | extern int set_umn_addr(int fd, char *addr, char *ptp_addr); | ||
25 | extern int slip_user_read(int fd, void *buf, int len, struct slip_data *pri); | ||
26 | extern int slip_user_write(int fd, void *buf, int len, struct slip_data *pri); | ||
27 | |||
28 | #endif | ||
29 | |||
30 | /* | ||
31 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
32 | * Emacs will notice this stuff at the end of the file and automatically | ||
33 | * adjust the settings for this buffer only. This must remain at the end | ||
34 | * of the file. | ||
35 | * --------------------------------------------------------------------------- | ||
36 | * Local variables: | ||
37 | * c-file-style: "linux" | ||
38 | * End: | ||
39 | */ | ||
diff --git a/arch/um/drivers/slip_kern.c b/arch/um/drivers/slip_kern.c new file mode 100644 index 000000000000..0886eedba213 --- /dev/null +++ b/arch/um/drivers/slip_kern.c | |||
@@ -0,0 +1,109 @@ | |||
1 | #include "linux/config.h" | ||
2 | #include "linux/kernel.h" | ||
3 | #include "linux/stddef.h" | ||
4 | #include "linux/init.h" | ||
5 | #include "linux/netdevice.h" | ||
6 | #include "linux/if_arp.h" | ||
7 | #include "net_kern.h" | ||
8 | #include "net_user.h" | ||
9 | #include "kern.h" | ||
10 | #include "slip.h" | ||
11 | |||
12 | struct slip_init { | ||
13 | char *gate_addr; | ||
14 | }; | ||
15 | |||
16 | void slip_init(struct net_device *dev, void *data) | ||
17 | { | ||
18 | struct uml_net_private *private; | ||
19 | struct slip_data *spri; | ||
20 | struct slip_init *init = data; | ||
21 | |||
22 | private = dev->priv; | ||
23 | spri = (struct slip_data *) private->user; | ||
24 | *spri = ((struct slip_data) | ||
25 | { .name = { '\0' }, | ||
26 | .addr = NULL, | ||
27 | .gate_addr = init->gate_addr, | ||
28 | .slave = -1, | ||
29 | .ibuf = { '\0' }, | ||
30 | .obuf = { '\0' }, | ||
31 | .pos = 0, | ||
32 | .esc = 0, | ||
33 | .dev = dev }); | ||
34 | |||
35 | dev->init = NULL; | ||
36 | dev->hard_header_len = 0; | ||
37 | dev->addr_len = 4; | ||
38 | dev->type = ARPHRD_ETHER; | ||
39 | dev->tx_queue_len = 256; | ||
40 | dev->flags = IFF_NOARP; | ||
41 | printk("SLIP backend - SLIP IP = %s\n", spri->gate_addr); | ||
42 | } | ||
43 | |||
44 | static unsigned short slip_protocol(struct sk_buff *skbuff) | ||
45 | { | ||
46 | return(htons(ETH_P_IP)); | ||
47 | } | ||
48 | |||
49 | static int slip_read(int fd, struct sk_buff **skb, | ||
50 | struct uml_net_private *lp) | ||
51 | { | ||
52 | return(slip_user_read(fd, (*skb)->mac.raw, (*skb)->dev->mtu, | ||
53 | (struct slip_data *) &lp->user)); | ||
54 | } | ||
55 | |||
56 | static int slip_write(int fd, struct sk_buff **skb, | ||
57 | struct uml_net_private *lp) | ||
58 | { | ||
59 | return(slip_user_write(fd, (*skb)->data, (*skb)->len, | ||
60 | (struct slip_data *) &lp->user)); | ||
61 | } | ||
62 | |||
63 | struct net_kern_info slip_kern_info = { | ||
64 | .init = slip_init, | ||
65 | .protocol = slip_protocol, | ||
66 | .read = slip_read, | ||
67 | .write = slip_write, | ||
68 | }; | ||
69 | |||
70 | static int slip_setup(char *str, char **mac_out, void *data) | ||
71 | { | ||
72 | struct slip_init *init = data; | ||
73 | |||
74 | *init = ((struct slip_init) | ||
75 | { .gate_addr = NULL }); | ||
76 | |||
77 | if(str[0] != '\0') | ||
78 | init->gate_addr = str; | ||
79 | return(1); | ||
80 | } | ||
81 | |||
82 | static struct transport slip_transport = { | ||
83 | .list = LIST_HEAD_INIT(slip_transport.list), | ||
84 | .name = "slip", | ||
85 | .setup = slip_setup, | ||
86 | .user = &slip_user_info, | ||
87 | .kern = &slip_kern_info, | ||
88 | .private_size = sizeof(struct slip_data), | ||
89 | .setup_size = sizeof(struct slip_init), | ||
90 | }; | ||
91 | |||
92 | static int register_slip(void) | ||
93 | { | ||
94 | register_transport(&slip_transport); | ||
95 | return(1); | ||
96 | } | ||
97 | |||
98 | __initcall(register_slip); | ||
99 | |||
100 | /* | ||
101 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
102 | * Emacs will notice this stuff at the end of the file and automatically | ||
103 | * adjust the settings for this buffer only. This must remain at the end | ||
104 | * of the file. | ||
105 | * --------------------------------------------------------------------------- | ||
106 | * Local variables: | ||
107 | * c-file-style: "linux" | ||
108 | * End: | ||
109 | */ | ||
diff --git a/arch/um/drivers/slip_proto.h b/arch/um/drivers/slip_proto.h new file mode 100644 index 000000000000..7206361ace45 --- /dev/null +++ b/arch/um/drivers/slip_proto.h | |||
@@ -0,0 +1,93 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __UM_SLIP_PROTO_H__ | ||
7 | #define __UM_SLIP_PROTO_H__ | ||
8 | |||
9 | /* SLIP protocol characters. */ | ||
10 | #define SLIP_END 0300 /* indicates end of frame */ | ||
11 | #define SLIP_ESC 0333 /* indicates byte stuffing */ | ||
12 | #define SLIP_ESC_END 0334 /* ESC ESC_END means END 'data' */ | ||
13 | #define SLIP_ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ | ||
14 | |||
15 | static inline int slip_unesc(unsigned char c,char *buf,int *pos, int *esc) | ||
16 | { | ||
17 | int ret; | ||
18 | |||
19 | switch(c){ | ||
20 | case SLIP_END: | ||
21 | *esc = 0; | ||
22 | ret=*pos; | ||
23 | *pos=0; | ||
24 | return(ret); | ||
25 | case SLIP_ESC: | ||
26 | *esc = 1; | ||
27 | return(0); | ||
28 | case SLIP_ESC_ESC: | ||
29 | if(*esc){ | ||
30 | *esc = 0; | ||
31 | c = SLIP_ESC; | ||
32 | } | ||
33 | break; | ||
34 | case SLIP_ESC_END: | ||
35 | if(*esc){ | ||
36 | *esc = 0; | ||
37 | c = SLIP_END; | ||
38 | } | ||
39 | break; | ||
40 | } | ||
41 | buf[(*pos)++] = c; | ||
42 | return(0); | ||
43 | } | ||
44 | |||
45 | static inline int slip_esc(unsigned char *s, unsigned char *d, int len) | ||
46 | { | ||
47 | unsigned char *ptr = d; | ||
48 | unsigned char c; | ||
49 | |||
50 | /* | ||
51 | * Send an initial END character to flush out any | ||
52 | * data that may have accumulated in the receiver | ||
53 | * due to line noise. | ||
54 | */ | ||
55 | |||
56 | *ptr++ = SLIP_END; | ||
57 | |||
58 | /* | ||
59 | * For each byte in the packet, send the appropriate | ||
60 | * character sequence, according to the SLIP protocol. | ||
61 | */ | ||
62 | |||
63 | while (len-- > 0) { | ||
64 | switch(c = *s++) { | ||
65 | case SLIP_END: | ||
66 | *ptr++ = SLIP_ESC; | ||
67 | *ptr++ = SLIP_ESC_END; | ||
68 | break; | ||
69 | case SLIP_ESC: | ||
70 | *ptr++ = SLIP_ESC; | ||
71 | *ptr++ = SLIP_ESC_ESC; | ||
72 | break; | ||
73 | default: | ||
74 | *ptr++ = c; | ||
75 | break; | ||
76 | } | ||
77 | } | ||
78 | *ptr++ = SLIP_END; | ||
79 | return (ptr - d); | ||
80 | } | ||
81 | |||
82 | #endif | ||
83 | |||
84 | /* | ||
85 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
86 | * Emacs will notice this stuff at the end of the file and automatically | ||
87 | * adjust the settings for this buffer only. This must remain at the end | ||
88 | * of the file. | ||
89 | * --------------------------------------------------------------------------- | ||
90 | * Local variables: | ||
91 | * c-file-style: "linux" | ||
92 | * End: | ||
93 | */ | ||
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 | */ | ||
diff --git a/arch/um/drivers/slirp.h b/arch/um/drivers/slirp.h new file mode 100644 index 000000000000..04e407d1e44a --- /dev/null +++ b/arch/um/drivers/slirp.h | |||
@@ -0,0 +1,51 @@ | |||
1 | #ifndef __UM_SLIRP_H | ||
2 | #define __UM_SLIRP_H | ||
3 | |||
4 | #define BUF_SIZE 1500 | ||
5 | /* two bytes each for a (pathological) max packet of escaped chars + * | ||
6 | * terminating END char + initial END char */ | ||
7 | #define ENC_BUF_SIZE (2 * BUF_SIZE + 2) | ||
8 | |||
9 | #define SLIRP_MAX_ARGS 100 | ||
10 | /* | ||
11 | * XXX this next definition is here because I don't understand why this | ||
12 | * initializer doesn't work in slirp_kern.c: | ||
13 | * | ||
14 | * argv : { init->argv[ 0 ... SLIRP_MAX_ARGS-1 ] }, | ||
15 | * | ||
16 | * or why I can't typecast like this: | ||
17 | * | ||
18 | * argv : (char* [SLIRP_MAX_ARGS])(init->argv), | ||
19 | */ | ||
20 | struct arg_list_dummy_wrapper { char *argv[SLIRP_MAX_ARGS]; }; | ||
21 | |||
22 | struct slirp_data { | ||
23 | void *dev; | ||
24 | struct arg_list_dummy_wrapper argw; | ||
25 | int pid; | ||
26 | int slave; | ||
27 | char ibuf[ENC_BUF_SIZE]; | ||
28 | char obuf[ENC_BUF_SIZE]; | ||
29 | int more; /* more data: do not read fd until ibuf has been drained */ | ||
30 | int pos; | ||
31 | int esc; | ||
32 | }; | ||
33 | |||
34 | extern struct net_user_info slirp_user_info; | ||
35 | |||
36 | extern int set_umn_addr(int fd, char *addr, char *ptp_addr); | ||
37 | extern int slirp_user_read(int fd, void *buf, int len, struct slirp_data *pri); | ||
38 | extern int slirp_user_write(int fd, void *buf, int len, struct slirp_data *pri); | ||
39 | |||
40 | #endif | ||
41 | |||
42 | /* | ||
43 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
44 | * Emacs will notice this stuff at the end of the file and automatically | ||
45 | * adjust the settings for this buffer only. This must remain at the end | ||
46 | * of the file. | ||
47 | * --------------------------------------------------------------------------- | ||
48 | * Local variables: | ||
49 | * c-file-style: "linux" | ||
50 | * End: | ||
51 | */ | ||
diff --git a/arch/um/drivers/slirp_kern.c b/arch/um/drivers/slirp_kern.c new file mode 100644 index 000000000000..c9d6b52a831d --- /dev/null +++ b/arch/um/drivers/slirp_kern.c | |||
@@ -0,0 +1,135 @@ | |||
1 | #include "linux/kernel.h" | ||
2 | #include "linux/stddef.h" | ||
3 | #include "linux/init.h" | ||
4 | #include "linux/netdevice.h" | ||
5 | #include "linux/if_arp.h" | ||
6 | #include "net_kern.h" | ||
7 | #include "net_user.h" | ||
8 | #include "kern.h" | ||
9 | #include "slirp.h" | ||
10 | |||
11 | struct slirp_init { | ||
12 | struct arg_list_dummy_wrapper argw; /* XXX should be simpler... */ | ||
13 | }; | ||
14 | |||
15 | void slirp_init(struct net_device *dev, void *data) | ||
16 | { | ||
17 | struct uml_net_private *private; | ||
18 | struct slirp_data *spri; | ||
19 | struct slirp_init *init = data; | ||
20 | int i; | ||
21 | |||
22 | private = dev->priv; | ||
23 | spri = (struct slirp_data *) private->user; | ||
24 | *spri = ((struct slirp_data) | ||
25 | { .argw = init->argw, | ||
26 | .pid = -1, | ||
27 | .slave = -1, | ||
28 | .ibuf = { '\0' }, | ||
29 | .obuf = { '\0' }, | ||
30 | .pos = 0, | ||
31 | .esc = 0, | ||
32 | .dev = dev }); | ||
33 | |||
34 | dev->init = NULL; | ||
35 | dev->hard_header_len = 0; | ||
36 | dev->header_cache_update = NULL; | ||
37 | dev->hard_header_cache = NULL; | ||
38 | dev->hard_header = NULL; | ||
39 | dev->addr_len = 0; | ||
40 | dev->type = ARPHRD_SLIP; | ||
41 | dev->tx_queue_len = 256; | ||
42 | dev->flags = IFF_NOARP; | ||
43 | printk("SLIRP backend - command line:"); | ||
44 | for(i=0;spri->argw.argv[i]!=NULL;i++) { | ||
45 | printk(" '%s'",spri->argw.argv[i]); | ||
46 | } | ||
47 | printk("\n"); | ||
48 | } | ||
49 | |||
50 | static unsigned short slirp_protocol(struct sk_buff *skbuff) | ||
51 | { | ||
52 | return(htons(ETH_P_IP)); | ||
53 | } | ||
54 | |||
55 | static int slirp_read(int fd, struct sk_buff **skb, | ||
56 | struct uml_net_private *lp) | ||
57 | { | ||
58 | return(slirp_user_read(fd, (*skb)->mac.raw, (*skb)->dev->mtu, | ||
59 | (struct slirp_data *) &lp->user)); | ||
60 | } | ||
61 | |||
62 | static int slirp_write(int fd, struct sk_buff **skb, | ||
63 | struct uml_net_private *lp) | ||
64 | { | ||
65 | return(slirp_user_write(fd, (*skb)->data, (*skb)->len, | ||
66 | (struct slirp_data *) &lp->user)); | ||
67 | } | ||
68 | |||
69 | struct net_kern_info slirp_kern_info = { | ||
70 | .init = slirp_init, | ||
71 | .protocol = slirp_protocol, | ||
72 | .read = slirp_read, | ||
73 | .write = slirp_write, | ||
74 | }; | ||
75 | |||
76 | static int slirp_setup(char *str, char **mac_out, void *data) | ||
77 | { | ||
78 | struct slirp_init *init = data; | ||
79 | int i=0; | ||
80 | |||
81 | *init = ((struct slirp_init) | ||
82 | { argw : { { "slirp", NULL } } }); | ||
83 | |||
84 | str = split_if_spec(str, mac_out, NULL); | ||
85 | |||
86 | if(str == NULL) { /* no command line given after MAC addr */ | ||
87 | return(1); | ||
88 | } | ||
89 | |||
90 | do { | ||
91 | if(i>=SLIRP_MAX_ARGS-1) { | ||
92 | printk("slirp_setup: truncating slirp arguments\n"); | ||
93 | break; | ||
94 | } | ||
95 | init->argw.argv[i++] = str; | ||
96 | while(*str && *str!=',') { | ||
97 | if(*str=='_') *str=' '; | ||
98 | str++; | ||
99 | } | ||
100 | if(*str!=',') | ||
101 | break; | ||
102 | *str++='\0'; | ||
103 | } while(1); | ||
104 | init->argw.argv[i]=NULL; | ||
105 | return(1); | ||
106 | } | ||
107 | |||
108 | static struct transport slirp_transport = { | ||
109 | .list = LIST_HEAD_INIT(slirp_transport.list), | ||
110 | .name = "slirp", | ||
111 | .setup = slirp_setup, | ||
112 | .user = &slirp_user_info, | ||
113 | .kern = &slirp_kern_info, | ||
114 | .private_size = sizeof(struct slirp_data), | ||
115 | .setup_size = sizeof(struct slirp_init), | ||
116 | }; | ||
117 | |||
118 | static int register_slirp(void) | ||
119 | { | ||
120 | register_transport(&slirp_transport); | ||
121 | return(1); | ||
122 | } | ||
123 | |||
124 | __initcall(register_slirp); | ||
125 | |||
126 | /* | ||
127 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
128 | * Emacs will notice this stuff at the end of the file and automatically | ||
129 | * adjust the settings for this buffer only. This must remain at the end | ||
130 | * of the file. | ||
131 | * --------------------------------------------------------------------------- | ||
132 | * Local variables: | ||
133 | * c-file-style: "linux" | ||
134 | * End: | ||
135 | */ | ||
diff --git a/arch/um/drivers/slirp_user.c b/arch/um/drivers/slirp_user.c new file mode 100644 index 000000000000..c322515c71cc --- /dev/null +++ b/arch/um/drivers/slirp_user.c | |||
@@ -0,0 +1,201 @@ | |||
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/wait.h> | ||
9 | #include <sys/signal.h> | ||
10 | #include "user_util.h" | ||
11 | #include "kern_util.h" | ||
12 | #include "user.h" | ||
13 | #include "net_user.h" | ||
14 | #include "slirp.h" | ||
15 | #include "slip_proto.h" | ||
16 | #include "helper.h" | ||
17 | #include "os.h" | ||
18 | |||
19 | void slirp_user_init(void *data, void *dev) | ||
20 | { | ||
21 | struct slirp_data *pri = data; | ||
22 | |||
23 | pri->dev = dev; | ||
24 | } | ||
25 | |||
26 | struct slirp_pre_exec_data { | ||
27 | int stdin; | ||
28 | int stdout; | ||
29 | }; | ||
30 | |||
31 | static void slirp_pre_exec(void *arg) | ||
32 | { | ||
33 | struct slirp_pre_exec_data *data = arg; | ||
34 | |||
35 | if(data->stdin != -1) dup2(data->stdin, 0); | ||
36 | if(data->stdout != -1) dup2(data->stdout, 1); | ||
37 | } | ||
38 | |||
39 | static int slirp_tramp(char **argv, int fd) | ||
40 | { | ||
41 | struct slirp_pre_exec_data pe_data; | ||
42 | int pid; | ||
43 | |||
44 | pe_data.stdin = fd; | ||
45 | pe_data.stdout = fd; | ||
46 | pid = run_helper(slirp_pre_exec, &pe_data, argv, NULL); | ||
47 | |||
48 | return(pid); | ||
49 | } | ||
50 | |||
51 | /* XXX This is just a trivial wrapper around os_pipe */ | ||
52 | static int slirp_datachan(int *mfd, int *sfd) | ||
53 | { | ||
54 | int fds[2], err; | ||
55 | |||
56 | err = os_pipe(fds, 1, 1); | ||
57 | if(err < 0){ | ||
58 | printk("slirp_datachan: Failed to open pipe, err = %d\n", -err); | ||
59 | return(err); | ||
60 | } | ||
61 | |||
62 | *mfd = fds[0]; | ||
63 | *sfd = fds[1]; | ||
64 | return(0); | ||
65 | } | ||
66 | |||
67 | static int slirp_open(void *data) | ||
68 | { | ||
69 | struct slirp_data *pri = data; | ||
70 | int sfd, mfd, pid, err; | ||
71 | |||
72 | err = slirp_datachan(&mfd, &sfd); | ||
73 | if(err) | ||
74 | return(err); | ||
75 | |||
76 | pid = slirp_tramp(pri->argw.argv, sfd); | ||
77 | |||
78 | if(pid < 0){ | ||
79 | printk("slirp_tramp failed - errno = %d\n", -pid); | ||
80 | os_close_file(sfd); | ||
81 | os_close_file(mfd); | ||
82 | return(pid); | ||
83 | } | ||
84 | |||
85 | pri->slave = sfd; | ||
86 | pri->pos = 0; | ||
87 | pri->esc = 0; | ||
88 | |||
89 | pri->pid = pid; | ||
90 | |||
91 | return(mfd); | ||
92 | } | ||
93 | |||
94 | static void slirp_close(int fd, void *data) | ||
95 | { | ||
96 | struct slirp_data *pri = data; | ||
97 | int status,err; | ||
98 | |||
99 | os_close_file(fd); | ||
100 | os_close_file(pri->slave); | ||
101 | |||
102 | pri->slave = -1; | ||
103 | |||
104 | if(pri->pid<1) { | ||
105 | printk("slirp_close: no child process to shut down\n"); | ||
106 | return; | ||
107 | } | ||
108 | |||
109 | #if 0 | ||
110 | if(kill(pri->pid, SIGHUP)<0) { | ||
111 | printk("slirp_close: sending hangup to %d failed (%d)\n", | ||
112 | pri->pid, errno); | ||
113 | } | ||
114 | #endif | ||
115 | |||
116 | CATCH_EINTR(err = waitpid(pri->pid, &status, WNOHANG)); | ||
117 | if(err < 0) { | ||
118 | printk("slirp_close: waitpid returned %d\n", errno); | ||
119 | return; | ||
120 | } | ||
121 | |||
122 | if(err == 0) { | ||
123 | printk("slirp_close: process %d has not exited\n"); | ||
124 | return; | ||
125 | } | ||
126 | |||
127 | pri->pid = -1; | ||
128 | } | ||
129 | |||
130 | int slirp_user_read(int fd, void *buf, int len, struct slirp_data *pri) | ||
131 | { | ||
132 | int i, n, size, start; | ||
133 | |||
134 | if(pri->more>0) { | ||
135 | i = 0; | ||
136 | while(i < pri->more) { | ||
137 | size = slip_unesc(pri->ibuf[i++], | ||
138 | pri->ibuf,&pri->pos,&pri->esc); | ||
139 | if(size){ | ||
140 | memcpy(buf, pri->ibuf, size); | ||
141 | memmove(pri->ibuf, &pri->ibuf[i], pri->more-i); | ||
142 | pri->more=pri->more-i; | ||
143 | return(size); | ||
144 | } | ||
145 | } | ||
146 | pri->more=0; | ||
147 | } | ||
148 | |||
149 | n = net_read(fd, &pri->ibuf[pri->pos], sizeof(pri->ibuf) - pri->pos); | ||
150 | if(n <= 0) return(n); | ||
151 | |||
152 | start = pri->pos; | ||
153 | for(i = 0; i < n; i++){ | ||
154 | size = slip_unesc(pri->ibuf[start + i], | ||
155 | pri->ibuf,&pri->pos,&pri->esc); | ||
156 | if(size){ | ||
157 | memcpy(buf, pri->ibuf, size); | ||
158 | memmove(pri->ibuf, &pri->ibuf[start+i+1], n-(i+1)); | ||
159 | pri->more=n-(i+1); | ||
160 | return(size); | ||
161 | } | ||
162 | } | ||
163 | return(0); | ||
164 | } | ||
165 | |||
166 | int slirp_user_write(int fd, void *buf, int len, struct slirp_data *pri) | ||
167 | { | ||
168 | int actual, n; | ||
169 | |||
170 | actual = slip_esc(buf, pri->obuf, len); | ||
171 | n = net_write(fd, pri->obuf, actual); | ||
172 | if(n < 0) return(n); | ||
173 | else return(len); | ||
174 | } | ||
175 | |||
176 | static int slirp_set_mtu(int mtu, void *data) | ||
177 | { | ||
178 | return(mtu); | ||
179 | } | ||
180 | |||
181 | struct net_user_info slirp_user_info = { | ||
182 | .init = slirp_user_init, | ||
183 | .open = slirp_open, | ||
184 | .close = slirp_close, | ||
185 | .remove = NULL, | ||
186 | .set_mtu = slirp_set_mtu, | ||
187 | .add_address = NULL, | ||
188 | .delete_address = NULL, | ||
189 | .max_packet = BUF_SIZE | ||
190 | }; | ||
191 | |||
192 | /* | ||
193 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
194 | * Emacs will notice this stuff at the end of the file and automatically | ||
195 | * adjust the settings for this buffer only. This must remain at the end | ||
196 | * of the file. | ||
197 | * --------------------------------------------------------------------------- | ||
198 | * Local variables: | ||
199 | * c-file-style: "linux" | ||
200 | * End: | ||
201 | */ | ||
diff --git a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c new file mode 100644 index 000000000000..c5839c3141f8 --- /dev/null +++ b/arch/um/drivers/ssl.c | |||
@@ -0,0 +1,251 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/config.h" | ||
7 | #include "linux/fs.h" | ||
8 | #include "linux/tty.h" | ||
9 | #include "linux/tty_driver.h" | ||
10 | #include "linux/major.h" | ||
11 | #include "linux/mm.h" | ||
12 | #include "linux/init.h" | ||
13 | #include "linux/console.h" | ||
14 | #include "asm/termbits.h" | ||
15 | #include "asm/irq.h" | ||
16 | #include "line.h" | ||
17 | #include "ssl.h" | ||
18 | #include "chan_kern.h" | ||
19 | #include "user_util.h" | ||
20 | #include "kern_util.h" | ||
21 | #include "kern.h" | ||
22 | #include "init.h" | ||
23 | #include "irq_user.h" | ||
24 | #include "mconsole_kern.h" | ||
25 | #include "2_5compat.h" | ||
26 | |||
27 | static int ssl_version = 1; | ||
28 | |||
29 | /* Referenced only by tty_driver below - presumably it's locked correctly | ||
30 | * by the tty driver. | ||
31 | */ | ||
32 | |||
33 | static struct tty_driver *ssl_driver; | ||
34 | |||
35 | #define NR_PORTS 64 | ||
36 | |||
37 | void ssl_announce(char *dev_name, int dev) | ||
38 | { | ||
39 | printk(KERN_INFO "Serial line %d assigned device '%s'\n", dev, | ||
40 | dev_name); | ||
41 | } | ||
42 | |||
43 | static struct chan_opts opts = { | ||
44 | .announce = ssl_announce, | ||
45 | .xterm_title = "Serial Line #%d", | ||
46 | .raw = 1, | ||
47 | .tramp_stack = 0, | ||
48 | .in_kernel = 1, | ||
49 | }; | ||
50 | |||
51 | static int ssl_config(char *str); | ||
52 | static int ssl_get_config(char *dev, char *str, int size, char **error_out); | ||
53 | static int ssl_remove(char *str); | ||
54 | |||
55 | static struct line_driver driver = { | ||
56 | .name = "UML serial line", | ||
57 | .device_name = "ttyS", | ||
58 | .devfs_name = "tts/", | ||
59 | .major = TTY_MAJOR, | ||
60 | .minor_start = 64, | ||
61 | .type = TTY_DRIVER_TYPE_SERIAL, | ||
62 | .subtype = 0, | ||
63 | .read_irq = SSL_IRQ, | ||
64 | .read_irq_name = "ssl", | ||
65 | .write_irq = SSL_WRITE_IRQ, | ||
66 | .write_irq_name = "ssl-write", | ||
67 | .symlink_from = "serial", | ||
68 | .symlink_to = "tts", | ||
69 | .mc = { | ||
70 | .name = "ssl", | ||
71 | .config = ssl_config, | ||
72 | .get_config = ssl_get_config, | ||
73 | .remove = ssl_remove, | ||
74 | }, | ||
75 | }; | ||
76 | |||
77 | /* The array is initialized by line_init, which is an initcall. The | ||
78 | * individual elements are protected by individual semaphores. | ||
79 | */ | ||
80 | static struct line serial_lines[NR_PORTS] = | ||
81 | { [0 ... NR_PORTS - 1] = LINE_INIT(CONFIG_SSL_CHAN, &driver) }; | ||
82 | |||
83 | static struct lines lines = LINES_INIT(NR_PORTS); | ||
84 | |||
85 | static int ssl_config(char *str) | ||
86 | { | ||
87 | return(line_config(serial_lines, | ||
88 | sizeof(serial_lines)/sizeof(serial_lines[0]), str)); | ||
89 | } | ||
90 | |||
91 | static int ssl_get_config(char *dev, char *str, int size, char **error_out) | ||
92 | { | ||
93 | return(line_get_config(dev, serial_lines, | ||
94 | sizeof(serial_lines)/sizeof(serial_lines[0]), | ||
95 | str, size, error_out)); | ||
96 | } | ||
97 | |||
98 | static int ssl_remove(char *str) | ||
99 | { | ||
100 | return(line_remove(serial_lines, | ||
101 | sizeof(serial_lines)/sizeof(serial_lines[0]), str)); | ||
102 | } | ||
103 | |||
104 | int ssl_open(struct tty_struct *tty, struct file *filp) | ||
105 | { | ||
106 | return line_open(serial_lines, tty, &opts); | ||
107 | } | ||
108 | |||
109 | #if 0 | ||
110 | static int ssl_chars_in_buffer(struct tty_struct *tty) | ||
111 | { | ||
112 | return(0); | ||
113 | } | ||
114 | |||
115 | static void ssl_flush_buffer(struct tty_struct *tty) | ||
116 | { | ||
117 | return; | ||
118 | } | ||
119 | |||
120 | static void ssl_throttle(struct tty_struct * tty) | ||
121 | { | ||
122 | printk(KERN_ERR "Someone should implement ssl_throttle\n"); | ||
123 | } | ||
124 | |||
125 | static void ssl_unthrottle(struct tty_struct * tty) | ||
126 | { | ||
127 | printk(KERN_ERR "Someone should implement ssl_unthrottle\n"); | ||
128 | } | ||
129 | |||
130 | static void ssl_stop(struct tty_struct *tty) | ||
131 | { | ||
132 | printk(KERN_ERR "Someone should implement ssl_stop\n"); | ||
133 | } | ||
134 | |||
135 | static void ssl_start(struct tty_struct *tty) | ||
136 | { | ||
137 | printk(KERN_ERR "Someone should implement ssl_start\n"); | ||
138 | } | ||
139 | |||
140 | void ssl_hangup(struct tty_struct *tty) | ||
141 | { | ||
142 | } | ||
143 | #endif | ||
144 | |||
145 | static struct tty_operations ssl_ops = { | ||
146 | .open = ssl_open, | ||
147 | .close = line_close, | ||
148 | .write = line_write, | ||
149 | .put_char = line_put_char, | ||
150 | .write_room = line_write_room, | ||
151 | .chars_in_buffer = line_chars_in_buffer, | ||
152 | .set_termios = line_set_termios, | ||
153 | .ioctl = line_ioctl, | ||
154 | #if 0 | ||
155 | .flush_chars = ssl_flush_chars, | ||
156 | .flush_buffer = ssl_flush_buffer, | ||
157 | .throttle = ssl_throttle, | ||
158 | .unthrottle = ssl_unthrottle, | ||
159 | .stop = ssl_stop, | ||
160 | .start = ssl_start, | ||
161 | .hangup = ssl_hangup, | ||
162 | #endif | ||
163 | }; | ||
164 | |||
165 | /* Changed by ssl_init and referenced by ssl_exit, which are both serialized | ||
166 | * by being an initcall and exitcall, respectively. | ||
167 | */ | ||
168 | static int ssl_init_done = 0; | ||
169 | |||
170 | static void ssl_console_write(struct console *c, const char *string, | ||
171 | unsigned len) | ||
172 | { | ||
173 | struct line *line = &serial_lines[c->index]; | ||
174 | |||
175 | down(&line->sem); | ||
176 | console_write_chan(&line->chan_list, string, len); | ||
177 | up(&line->sem); | ||
178 | } | ||
179 | |||
180 | static struct tty_driver *ssl_console_device(struct console *c, int *index) | ||
181 | { | ||
182 | *index = c->index; | ||
183 | return ssl_driver; | ||
184 | } | ||
185 | |||
186 | static int ssl_console_setup(struct console *co, char *options) | ||
187 | { | ||
188 | struct line *line = &serial_lines[co->index]; | ||
189 | |||
190 | return console_open_chan(line,co,&opts); | ||
191 | } | ||
192 | |||
193 | static struct console ssl_cons = { | ||
194 | .name = "ttyS", | ||
195 | .write = ssl_console_write, | ||
196 | .device = ssl_console_device, | ||
197 | .setup = ssl_console_setup, | ||
198 | .flags = CON_PRINTBUFFER, | ||
199 | .index = -1, | ||
200 | }; | ||
201 | |||
202 | int ssl_init(void) | ||
203 | { | ||
204 | char *new_title; | ||
205 | |||
206 | printk(KERN_INFO "Initializing software serial port version %d\n", | ||
207 | ssl_version); | ||
208 | ssl_driver = line_register_devfs(&lines, &driver, &ssl_ops, | ||
209 | serial_lines, ARRAY_SIZE(serial_lines)); | ||
210 | |||
211 | lines_init(serial_lines, sizeof(serial_lines)/sizeof(serial_lines[0])); | ||
212 | |||
213 | new_title = add_xterm_umid(opts.xterm_title); | ||
214 | if (new_title != NULL) | ||
215 | opts.xterm_title = new_title; | ||
216 | |||
217 | ssl_init_done = 1; | ||
218 | register_console(&ssl_cons); | ||
219 | return(0); | ||
220 | } | ||
221 | late_initcall(ssl_init); | ||
222 | |||
223 | static void ssl_exit(void) | ||
224 | { | ||
225 | if (!ssl_init_done) | ||
226 | return; | ||
227 | close_lines(serial_lines, | ||
228 | sizeof(serial_lines)/sizeof(serial_lines[0])); | ||
229 | } | ||
230 | __uml_exitcall(ssl_exit); | ||
231 | |||
232 | static int ssl_chan_setup(char *str) | ||
233 | { | ||
234 | return(line_setup(serial_lines, | ||
235 | sizeof(serial_lines)/sizeof(serial_lines[0]), | ||
236 | str, 1)); | ||
237 | } | ||
238 | |||
239 | __setup("ssl", ssl_chan_setup); | ||
240 | __channel_help(ssl_chan_setup, "ssl"); | ||
241 | |||
242 | /* | ||
243 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
244 | * Emacs will notice this stuff at the end of the file and automatically | ||
245 | * adjust the settings for this buffer only. This must remain at the end | ||
246 | * of the file. | ||
247 | * --------------------------------------------------------------------------- | ||
248 | * Local variables: | ||
249 | * c-file-style: "linux" | ||
250 | * End: | ||
251 | */ | ||
diff --git a/arch/um/drivers/ssl.h b/arch/um/drivers/ssl.h new file mode 100644 index 000000000000..98412aa66607 --- /dev/null +++ b/arch/um/drivers/ssl.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __SSL_H__ | ||
7 | #define __SSL_H__ | ||
8 | |||
9 | extern int ssl_read(int fd, int line); | ||
10 | extern void ssl_receive_char(int line, char ch); | ||
11 | |||
12 | #endif | ||
13 | |||
14 | /* | ||
15 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
16 | * Emacs will notice this stuff at the end of the file and automatically | ||
17 | * adjust the settings for this buffer only. This must remain at the end | ||
18 | * of the file. | ||
19 | * --------------------------------------------------------------------------- | ||
20 | * Local variables: | ||
21 | * c-file-style: "linux" | ||
22 | * End: | ||
23 | */ | ||
diff --git a/arch/um/drivers/stderr_console.c b/arch/um/drivers/stderr_console.c new file mode 100644 index 000000000000..98565b53d170 --- /dev/null +++ b/arch/um/drivers/stderr_console.c | |||
@@ -0,0 +1,45 @@ | |||
1 | #include <linux/init.h> | ||
2 | #include <linux/console.h> | ||
3 | |||
4 | #include "chan_user.h" | ||
5 | |||
6 | /* ----------------------------------------------------------------------------- */ | ||
7 | /* trivial console driver -- simply dump everything to stderr */ | ||
8 | |||
9 | /* | ||
10 | * Don't register by default -- as this registeres very early in the | ||
11 | * boot process it becomes the default console. And as this isn't a | ||
12 | * real tty driver init isn't able to open /dev/console then. | ||
13 | * | ||
14 | * In most cases this isn't what you want ... | ||
15 | */ | ||
16 | static int use_stderr_console = 0; | ||
17 | |||
18 | static void stderr_console_write(struct console *console, const char *string, | ||
19 | unsigned len) | ||
20 | { | ||
21 | generic_write(2 /* stderr */, string, len, NULL); | ||
22 | } | ||
23 | |||
24 | static struct console stderr_console = { | ||
25 | .name "stderr", | ||
26 | .write stderr_console_write, | ||
27 | .flags CON_PRINTBUFFER, | ||
28 | }; | ||
29 | |||
30 | static int __init stderr_console_init(void) | ||
31 | { | ||
32 | if (use_stderr_console) | ||
33 | register_console(&stderr_console); | ||
34 | return 0; | ||
35 | } | ||
36 | console_initcall(stderr_console_init); | ||
37 | |||
38 | static int stderr_setup(char *str) | ||
39 | { | ||
40 | if (!str) | ||
41 | return 0; | ||
42 | use_stderr_console = simple_strtoul(str,&str,0); | ||
43 | return 1; | ||
44 | } | ||
45 | __setup("stderr=", stderr_setup); | ||
diff --git a/arch/um/drivers/stdio_console.c b/arch/um/drivers/stdio_console.c new file mode 100644 index 000000000000..e604d7c87695 --- /dev/null +++ b/arch/um/drivers/stdio_console.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/config.h" | ||
7 | #include "linux/posix_types.h" | ||
8 | #include "linux/tty.h" | ||
9 | #include "linux/tty_flip.h" | ||
10 | #include "linux/types.h" | ||
11 | #include "linux/major.h" | ||
12 | #include "linux/kdev_t.h" | ||
13 | #include "linux/console.h" | ||
14 | #include "linux/string.h" | ||
15 | #include "linux/sched.h" | ||
16 | #include "linux/list.h" | ||
17 | #include "linux/init.h" | ||
18 | #include "linux/interrupt.h" | ||
19 | #include "linux/slab.h" | ||
20 | #include "linux/hardirq.h" | ||
21 | #include "asm/current.h" | ||
22 | #include "asm/irq.h" | ||
23 | #include "stdio_console.h" | ||
24 | #include "line.h" | ||
25 | #include "chan_kern.h" | ||
26 | #include "user_util.h" | ||
27 | #include "kern_util.h" | ||
28 | #include "irq_user.h" | ||
29 | #include "mconsole_kern.h" | ||
30 | #include "init.h" | ||
31 | #include "2_5compat.h" | ||
32 | |||
33 | #define MAX_TTYS (16) | ||
34 | |||
35 | /* ----------------------------------------------------------------------------- */ | ||
36 | |||
37 | /* Referenced only by tty_driver below - presumably it's locked correctly | ||
38 | * by the tty driver. | ||
39 | */ | ||
40 | |||
41 | static struct tty_driver *console_driver; | ||
42 | |||
43 | void stdio_announce(char *dev_name, int dev) | ||
44 | { | ||
45 | printk(KERN_INFO "Virtual console %d assigned device '%s'\n", dev, | ||
46 | dev_name); | ||
47 | } | ||
48 | |||
49 | static struct chan_opts opts = { | ||
50 | .announce = stdio_announce, | ||
51 | .xterm_title = "Virtual Console #%d", | ||
52 | .raw = 1, | ||
53 | .tramp_stack = 0, | ||
54 | .in_kernel = 1, | ||
55 | }; | ||
56 | |||
57 | static int con_config(char *str); | ||
58 | static int con_get_config(char *dev, char *str, int size, char **error_out); | ||
59 | static int con_remove(char *str); | ||
60 | |||
61 | static struct line_driver driver = { | ||
62 | .name = "UML console", | ||
63 | .device_name = "tty", | ||
64 | .devfs_name = "vc/", | ||
65 | .major = TTY_MAJOR, | ||
66 | .minor_start = 0, | ||
67 | .type = TTY_DRIVER_TYPE_CONSOLE, | ||
68 | .subtype = SYSTEM_TYPE_CONSOLE, | ||
69 | .read_irq = CONSOLE_IRQ, | ||
70 | .read_irq_name = "console", | ||
71 | .write_irq = CONSOLE_WRITE_IRQ, | ||
72 | .write_irq_name = "console-write", | ||
73 | .symlink_from = "ttys", | ||
74 | .symlink_to = "vc", | ||
75 | .mc = { | ||
76 | .name = "con", | ||
77 | .config = con_config, | ||
78 | .get_config = con_get_config, | ||
79 | .remove = con_remove, | ||
80 | }, | ||
81 | }; | ||
82 | |||
83 | static struct lines console_lines = LINES_INIT(MAX_TTYS); | ||
84 | |||
85 | /* The array is initialized by line_init, which is an initcall. The | ||
86 | * individual elements are protected by individual semaphores. | ||
87 | */ | ||
88 | struct line vts[MAX_TTYS] = { LINE_INIT(CONFIG_CON_ZERO_CHAN, &driver), | ||
89 | [ 1 ... MAX_TTYS - 1 ] = | ||
90 | LINE_INIT(CONFIG_CON_CHAN, &driver) }; | ||
91 | |||
92 | static int con_config(char *str) | ||
93 | { | ||
94 | return(line_config(vts, sizeof(vts)/sizeof(vts[0]), str)); | ||
95 | } | ||
96 | |||
97 | static int con_get_config(char *dev, char *str, int size, char **error_out) | ||
98 | { | ||
99 | return(line_get_config(dev, vts, sizeof(vts)/sizeof(vts[0]), str, | ||
100 | size, error_out)); | ||
101 | } | ||
102 | |||
103 | static int con_remove(char *str) | ||
104 | { | ||
105 | return(line_remove(vts, sizeof(vts)/sizeof(vts[0]), str)); | ||
106 | } | ||
107 | |||
108 | static int con_open(struct tty_struct *tty, struct file *filp) | ||
109 | { | ||
110 | return line_open(vts, tty, &opts); | ||
111 | } | ||
112 | |||
113 | static int con_init_done = 0; | ||
114 | |||
115 | static struct tty_operations console_ops = { | ||
116 | .open = con_open, | ||
117 | .close = line_close, | ||
118 | .write = line_write, | ||
119 | .write_room = line_write_room, | ||
120 | .chars_in_buffer = line_chars_in_buffer, | ||
121 | .set_termios = line_set_termios, | ||
122 | .ioctl = line_ioctl, | ||
123 | }; | ||
124 | |||
125 | static void uml_console_write(struct console *console, const char *string, | ||
126 | unsigned len) | ||
127 | { | ||
128 | struct line *line = &vts[console->index]; | ||
129 | |||
130 | down(&line->sem); | ||
131 | console_write_chan(&line->chan_list, string, len); | ||
132 | up(&line->sem); | ||
133 | } | ||
134 | |||
135 | static struct tty_driver *uml_console_device(struct console *c, int *index) | ||
136 | { | ||
137 | *index = c->index; | ||
138 | return console_driver; | ||
139 | } | ||
140 | |||
141 | static int uml_console_setup(struct console *co, char *options) | ||
142 | { | ||
143 | struct line *line = &vts[co->index]; | ||
144 | |||
145 | return console_open_chan(line,co,&opts); | ||
146 | } | ||
147 | |||
148 | static struct console stdiocons = { | ||
149 | .name = "tty", | ||
150 | .write = uml_console_write, | ||
151 | .device = uml_console_device, | ||
152 | .setup = uml_console_setup, | ||
153 | .flags = CON_PRINTBUFFER, | ||
154 | .index = -1, | ||
155 | .data = &vts, | ||
156 | }; | ||
157 | |||
158 | int stdio_init(void) | ||
159 | { | ||
160 | char *new_title; | ||
161 | |||
162 | console_driver = line_register_devfs(&console_lines, &driver, | ||
163 | &console_ops, vts, | ||
164 | ARRAY_SIZE(vts)); | ||
165 | if (NULL == console_driver) | ||
166 | return -1; | ||
167 | printk(KERN_INFO "Initialized stdio console driver\n"); | ||
168 | |||
169 | lines_init(vts, sizeof(vts)/sizeof(vts[0])); | ||
170 | |||
171 | new_title = add_xterm_umid(opts.xterm_title); | ||
172 | if(new_title != NULL) | ||
173 | opts.xterm_title = new_title; | ||
174 | |||
175 | con_init_done = 1; | ||
176 | register_console(&stdiocons); | ||
177 | return(0); | ||
178 | } | ||
179 | late_initcall(stdio_init); | ||
180 | |||
181 | static void console_exit(void) | ||
182 | { | ||
183 | if (!con_init_done) | ||
184 | return; | ||
185 | close_lines(vts, sizeof(vts)/sizeof(vts[0])); | ||
186 | } | ||
187 | __uml_exitcall(console_exit); | ||
188 | |||
189 | static int console_chan_setup(char *str) | ||
190 | { | ||
191 | return(line_setup(vts, sizeof(vts)/sizeof(vts[0]), str, 1)); | ||
192 | } | ||
193 | __setup("con", console_chan_setup); | ||
194 | __channel_help(console_chan_setup, "con"); | ||
195 | |||
196 | /* | ||
197 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
198 | * Emacs will notice this stuff at the end of the file and automatically | ||
199 | * adjust the settings for this buffer only. This must remain at the end | ||
200 | * of the file. | ||
201 | * --------------------------------------------------------------------------- | ||
202 | * Local variables: | ||
203 | * c-file-style: "linux" | ||
204 | * End: | ||
205 | */ | ||
diff --git a/arch/um/drivers/stdio_console.h b/arch/um/drivers/stdio_console.h new file mode 100644 index 000000000000..505a3d5bea5e --- /dev/null +++ b/arch/um/drivers/stdio_console.h | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __STDIO_CONSOLE_H | ||
7 | #define __STDIO_CONSOLE_H | ||
8 | |||
9 | extern void save_console_flags(void); | ||
10 | #endif | ||
11 | |||
12 | /* | ||
13 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
14 | * Emacs will notice this stuff at the end of the file and automatically | ||
15 | * adjust the settings for this buffer only. This must remain at the end | ||
16 | * of the file. | ||
17 | * --------------------------------------------------------------------------- | ||
18 | * Local variables: | ||
19 | * c-file-style: "linux" | ||
20 | * End: | ||
21 | */ | ||
diff --git a/arch/um/drivers/tty.c b/arch/um/drivers/tty.c new file mode 100644 index 000000000000..6fbb670ee274 --- /dev/null +++ b/arch/um/drivers/tty.c | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <termios.h> | ||
8 | #include <errno.h> | ||
9 | #include <unistd.h> | ||
10 | #include "chan_user.h" | ||
11 | #include "user_util.h" | ||
12 | #include "user.h" | ||
13 | #include "os.h" | ||
14 | |||
15 | struct tty_chan { | ||
16 | char *dev; | ||
17 | int raw; | ||
18 | struct termios tt; | ||
19 | }; | ||
20 | |||
21 | static void *tty_chan_init(char *str, int device, struct chan_opts *opts) | ||
22 | { | ||
23 | struct tty_chan *data; | ||
24 | |||
25 | if(*str != ':'){ | ||
26 | printk("tty_init : channel type 'tty' must specify " | ||
27 | "a device\n"); | ||
28 | return(NULL); | ||
29 | } | ||
30 | str++; | ||
31 | |||
32 | data = um_kmalloc(sizeof(*data)); | ||
33 | if(data == NULL) | ||
34 | return(NULL); | ||
35 | *data = ((struct tty_chan) { .dev = str, | ||
36 | .raw = opts->raw }); | ||
37 | |||
38 | return(data); | ||
39 | } | ||
40 | |||
41 | static int tty_open(int input, int output, int primary, void *d, | ||
42 | char **dev_out) | ||
43 | { | ||
44 | struct tty_chan *data = d; | ||
45 | int fd, err; | ||
46 | |||
47 | fd = os_open_file(data->dev, of_set_rw(OPENFLAGS(), input, output), 0); | ||
48 | if(fd < 0) return(fd); | ||
49 | if(data->raw){ | ||
50 | CATCH_EINTR(err = tcgetattr(fd, &data->tt)); | ||
51 | if(err) | ||
52 | return(err); | ||
53 | |||
54 | err = raw(fd); | ||
55 | if(err) | ||
56 | return(err); | ||
57 | } | ||
58 | |||
59 | *dev_out = data->dev; | ||
60 | return(fd); | ||
61 | } | ||
62 | |||
63 | static int tty_console_write(int fd, const char *buf, int n, void *d) | ||
64 | { | ||
65 | struct tty_chan *data = d; | ||
66 | |||
67 | return(generic_console_write(fd, buf, n, &data->tt)); | ||
68 | } | ||
69 | |||
70 | struct chan_ops tty_ops = { | ||
71 | .type = "tty", | ||
72 | .init = tty_chan_init, | ||
73 | .open = tty_open, | ||
74 | .close = generic_close, | ||
75 | .read = generic_read, | ||
76 | .write = generic_write, | ||
77 | .console_write = tty_console_write, | ||
78 | .window_size = generic_window_size, | ||
79 | .free = generic_free, | ||
80 | .winch = 0, | ||
81 | }; | ||
82 | |||
83 | /* | ||
84 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
85 | * Emacs will notice this stuff at the end of the file and automatically | ||
86 | * adjust the settings for this buffer only. This must remain at the end | ||
87 | * of the file. | ||
88 | * --------------------------------------------------------------------------- | ||
89 | * Local variables: | ||
90 | * c-file-style: "linux" | ||
91 | * End: | ||
92 | */ | ||
diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c new file mode 100644 index 000000000000..4d8b165bfa48 --- /dev/null +++ b/arch/um/drivers/ubd_kern.c | |||
@@ -0,0 +1,1669 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | /* 2001-09-28...2002-04-17 | ||
7 | * Partition stuff by James_McMechan@hotmail.com | ||
8 | * old style ubd by setting UBD_SHIFT to 0 | ||
9 | * 2002-09-27...2002-10-18 massive tinkering for 2.5 | ||
10 | * partitions have changed in 2.5 | ||
11 | * 2003-01-29 more tinkering for 2.5.59-1 | ||
12 | * This should now address the sysfs problems and has | ||
13 | * the symlink for devfs to allow for booting with | ||
14 | * the common /dev/ubd/discX/... names rather than | ||
15 | * only /dev/ubdN/discN this version also has lots of | ||
16 | * clean ups preparing for ubd-many. | ||
17 | * James McMechan | ||
18 | */ | ||
19 | |||
20 | #define MAJOR_NR UBD_MAJOR | ||
21 | #define UBD_SHIFT 4 | ||
22 | |||
23 | #include "linux/config.h" | ||
24 | #include "linux/module.h" | ||
25 | #include "linux/blkdev.h" | ||
26 | #include "linux/hdreg.h" | ||
27 | #include "linux/init.h" | ||
28 | #include "linux/devfs_fs_kernel.h" | ||
29 | #include "linux/cdrom.h" | ||
30 | #include "linux/proc_fs.h" | ||
31 | #include "linux/ctype.h" | ||
32 | #include "linux/capability.h" | ||
33 | #include "linux/mm.h" | ||
34 | #include "linux/vmalloc.h" | ||
35 | #include "linux/blkpg.h" | ||
36 | #include "linux/genhd.h" | ||
37 | #include "linux/spinlock.h" | ||
38 | #include "asm/segment.h" | ||
39 | #include "asm/uaccess.h" | ||
40 | #include "asm/irq.h" | ||
41 | #include "asm/types.h" | ||
42 | #include "asm/tlbflush.h" | ||
43 | #include "user_util.h" | ||
44 | #include "mem_user.h" | ||
45 | #include "kern_util.h" | ||
46 | #include "kern.h" | ||
47 | #include "mconsole_kern.h" | ||
48 | #include "init.h" | ||
49 | #include "irq_user.h" | ||
50 | #include "irq_kern.h" | ||
51 | #include "ubd_user.h" | ||
52 | #include "2_5compat.h" | ||
53 | #include "os.h" | ||
54 | #include "mem.h" | ||
55 | #include "mem_kern.h" | ||
56 | #include "cow.h" | ||
57 | |||
58 | enum ubd_req { UBD_READ, UBD_WRITE, UBD_MMAP }; | ||
59 | |||
60 | struct io_thread_req { | ||
61 | enum ubd_req op; | ||
62 | int fds[2]; | ||
63 | unsigned long offsets[2]; | ||
64 | unsigned long long offset; | ||
65 | unsigned long length; | ||
66 | char *buffer; | ||
67 | int sectorsize; | ||
68 | unsigned long sector_mask; | ||
69 | unsigned long long cow_offset; | ||
70 | unsigned long bitmap_words[2]; | ||
71 | int map_fd; | ||
72 | unsigned long long map_offset; | ||
73 | int error; | ||
74 | }; | ||
75 | |||
76 | extern int open_ubd_file(char *file, struct openflags *openflags, | ||
77 | char **backing_file_out, int *bitmap_offset_out, | ||
78 | unsigned long *bitmap_len_out, int *data_offset_out, | ||
79 | int *create_cow_out); | ||
80 | extern int create_cow_file(char *cow_file, char *backing_file, | ||
81 | struct openflags flags, int sectorsize, | ||
82 | int alignment, int *bitmap_offset_out, | ||
83 | unsigned long *bitmap_len_out, | ||
84 | int *data_offset_out); | ||
85 | extern int read_cow_bitmap(int fd, void *buf, int offset, int len); | ||
86 | extern void do_io(struct io_thread_req *req); | ||
87 | |||
88 | static inline int ubd_test_bit(__u64 bit, unsigned char *data) | ||
89 | { | ||
90 | __u64 n; | ||
91 | int bits, off; | ||
92 | |||
93 | bits = sizeof(data[0]) * 8; | ||
94 | n = bit / bits; | ||
95 | off = bit % bits; | ||
96 | return((data[n] & (1 << off)) != 0); | ||
97 | } | ||
98 | |||
99 | static inline void ubd_set_bit(__u64 bit, unsigned char *data) | ||
100 | { | ||
101 | __u64 n; | ||
102 | int bits, off; | ||
103 | |||
104 | bits = sizeof(data[0]) * 8; | ||
105 | n = bit / bits; | ||
106 | off = bit % bits; | ||
107 | data[n] |= (1 << off); | ||
108 | } | ||
109 | /*End stuff from ubd_user.h*/ | ||
110 | |||
111 | #define DRIVER_NAME "uml-blkdev" | ||
112 | |||
113 | static DEFINE_SPINLOCK(ubd_io_lock); | ||
114 | static DEFINE_SPINLOCK(ubd_lock); | ||
115 | |||
116 | static void (*do_ubd)(void); | ||
117 | |||
118 | static int ubd_open(struct inode * inode, struct file * filp); | ||
119 | static int ubd_release(struct inode * inode, struct file * file); | ||
120 | static int ubd_ioctl(struct inode * inode, struct file * file, | ||
121 | unsigned int cmd, unsigned long arg); | ||
122 | |||
123 | #define MAX_DEV (8) | ||
124 | |||
125 | /* Changed in early boot */ | ||
126 | static int ubd_do_mmap = 0; | ||
127 | #define UBD_MMAP_BLOCK_SIZE PAGE_SIZE | ||
128 | |||
129 | static struct block_device_operations ubd_blops = { | ||
130 | .owner = THIS_MODULE, | ||
131 | .open = ubd_open, | ||
132 | .release = ubd_release, | ||
133 | .ioctl = ubd_ioctl, | ||
134 | }; | ||
135 | |||
136 | /* Protected by the queue_lock */ | ||
137 | static request_queue_t *ubd_queue; | ||
138 | |||
139 | /* Protected by ubd_lock */ | ||
140 | static int fake_major = MAJOR_NR; | ||
141 | |||
142 | static struct gendisk *ubd_gendisk[MAX_DEV]; | ||
143 | static struct gendisk *fake_gendisk[MAX_DEV]; | ||
144 | |||
145 | #ifdef CONFIG_BLK_DEV_UBD_SYNC | ||
146 | #define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \ | ||
147 | .cl = 1 }) | ||
148 | #else | ||
149 | #define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \ | ||
150 | .cl = 1 }) | ||
151 | #endif | ||
152 | |||
153 | /* Not protected - changed only in ubd_setup_common and then only to | ||
154 | * to enable O_SYNC. | ||
155 | */ | ||
156 | static struct openflags global_openflags = OPEN_FLAGS; | ||
157 | |||
158 | struct cow { | ||
159 | char *file; | ||
160 | int fd; | ||
161 | unsigned long *bitmap; | ||
162 | unsigned long bitmap_len; | ||
163 | int bitmap_offset; | ||
164 | int data_offset; | ||
165 | }; | ||
166 | |||
167 | struct ubd { | ||
168 | char *file; | ||
169 | int count; | ||
170 | int fd; | ||
171 | __u64 size; | ||
172 | struct openflags boot_openflags; | ||
173 | struct openflags openflags; | ||
174 | int no_cow; | ||
175 | struct cow cow; | ||
176 | struct platform_device pdev; | ||
177 | |||
178 | int map_writes; | ||
179 | int map_reads; | ||
180 | int nomap_writes; | ||
181 | int nomap_reads; | ||
182 | int write_maps; | ||
183 | }; | ||
184 | |||
185 | #define DEFAULT_COW { \ | ||
186 | .file = NULL, \ | ||
187 | .fd = -1, \ | ||
188 | .bitmap = NULL, \ | ||
189 | .bitmap_offset = 0, \ | ||
190 | .data_offset = 0, \ | ||
191 | } | ||
192 | |||
193 | #define DEFAULT_UBD { \ | ||
194 | .file = NULL, \ | ||
195 | .count = 0, \ | ||
196 | .fd = -1, \ | ||
197 | .size = -1, \ | ||
198 | .boot_openflags = OPEN_FLAGS, \ | ||
199 | .openflags = OPEN_FLAGS, \ | ||
200 | .no_cow = 0, \ | ||
201 | .cow = DEFAULT_COW, \ | ||
202 | .map_writes = 0, \ | ||
203 | .map_reads = 0, \ | ||
204 | .nomap_writes = 0, \ | ||
205 | .nomap_reads = 0, \ | ||
206 | .write_maps = 0, \ | ||
207 | } | ||
208 | |||
209 | struct ubd ubd_dev[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD }; | ||
210 | |||
211 | static int ubd0_init(void) | ||
212 | { | ||
213 | struct ubd *dev = &ubd_dev[0]; | ||
214 | |||
215 | if(dev->file == NULL) | ||
216 | dev->file = "root_fs"; | ||
217 | return(0); | ||
218 | } | ||
219 | |||
220 | __initcall(ubd0_init); | ||
221 | |||
222 | /* Only changed by fake_ide_setup which is a setup */ | ||
223 | static int fake_ide = 0; | ||
224 | static struct proc_dir_entry *proc_ide_root = NULL; | ||
225 | static struct proc_dir_entry *proc_ide = NULL; | ||
226 | |||
227 | static void make_proc_ide(void) | ||
228 | { | ||
229 | proc_ide_root = proc_mkdir("ide", NULL); | ||
230 | proc_ide = proc_mkdir("ide0", proc_ide_root); | ||
231 | } | ||
232 | |||
233 | static int proc_ide_read_media(char *page, char **start, off_t off, int count, | ||
234 | int *eof, void *data) | ||
235 | { | ||
236 | int len; | ||
237 | |||
238 | strcpy(page, "disk\n"); | ||
239 | len = strlen("disk\n"); | ||
240 | len -= off; | ||
241 | if (len < count){ | ||
242 | *eof = 1; | ||
243 | if (len <= 0) return 0; | ||
244 | } | ||
245 | else len = count; | ||
246 | *start = page + off; | ||
247 | return len; | ||
248 | } | ||
249 | |||
250 | static void make_ide_entries(char *dev_name) | ||
251 | { | ||
252 | struct proc_dir_entry *dir, *ent; | ||
253 | char name[64]; | ||
254 | |||
255 | if(proc_ide_root == NULL) make_proc_ide(); | ||
256 | |||
257 | dir = proc_mkdir(dev_name, proc_ide); | ||
258 | if(!dir) return; | ||
259 | |||
260 | ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir); | ||
261 | if(!ent) return; | ||
262 | ent->nlink = 1; | ||
263 | ent->data = NULL; | ||
264 | ent->read_proc = proc_ide_read_media; | ||
265 | ent->write_proc = NULL; | ||
266 | sprintf(name,"ide0/%s", dev_name); | ||
267 | proc_symlink(dev_name, proc_ide_root, name); | ||
268 | } | ||
269 | |||
270 | static int fake_ide_setup(char *str) | ||
271 | { | ||
272 | fake_ide = 1; | ||
273 | return(1); | ||
274 | } | ||
275 | |||
276 | __setup("fake_ide", fake_ide_setup); | ||
277 | |||
278 | __uml_help(fake_ide_setup, | ||
279 | "fake_ide\n" | ||
280 | " Create ide0 entries that map onto ubd devices.\n\n" | ||
281 | ); | ||
282 | |||
283 | static int parse_unit(char **ptr) | ||
284 | { | ||
285 | char *str = *ptr, *end; | ||
286 | int n = -1; | ||
287 | |||
288 | if(isdigit(*str)) { | ||
289 | n = simple_strtoul(str, &end, 0); | ||
290 | if(end == str) | ||
291 | return(-1); | ||
292 | *ptr = end; | ||
293 | } | ||
294 | else if (('a' <= *str) && (*str <= 'h')) { | ||
295 | n = *str - 'a'; | ||
296 | str++; | ||
297 | *ptr = str; | ||
298 | } | ||
299 | return(n); | ||
300 | } | ||
301 | |||
302 | static int ubd_setup_common(char *str, int *index_out) | ||
303 | { | ||
304 | struct ubd *dev; | ||
305 | struct openflags flags = global_openflags; | ||
306 | char *backing_file; | ||
307 | int n, err, i; | ||
308 | |||
309 | if(index_out) *index_out = -1; | ||
310 | n = *str; | ||
311 | if(n == '='){ | ||
312 | char *end; | ||
313 | int major; | ||
314 | |||
315 | str++; | ||
316 | if(!strcmp(str, "mmap")){ | ||
317 | CHOOSE_MODE(printk("mmap not supported by the ubd " | ||
318 | "driver in tt mode\n"), | ||
319 | ubd_do_mmap = 1); | ||
320 | return(0); | ||
321 | } | ||
322 | |||
323 | if(!strcmp(str, "sync")){ | ||
324 | global_openflags = of_sync(global_openflags); | ||
325 | return(0); | ||
326 | } | ||
327 | major = simple_strtoul(str, &end, 0); | ||
328 | if((*end != '\0') || (end == str)){ | ||
329 | printk(KERN_ERR | ||
330 | "ubd_setup : didn't parse major number\n"); | ||
331 | return(1); | ||
332 | } | ||
333 | |||
334 | err = 1; | ||
335 | spin_lock(&ubd_lock); | ||
336 | if(fake_major != MAJOR_NR){ | ||
337 | printk(KERN_ERR "Can't assign a fake major twice\n"); | ||
338 | goto out1; | ||
339 | } | ||
340 | |||
341 | fake_major = major; | ||
342 | |||
343 | printk(KERN_INFO "Setting extra ubd major number to %d\n", | ||
344 | major); | ||
345 | err = 0; | ||
346 | out1: | ||
347 | spin_unlock(&ubd_lock); | ||
348 | return(err); | ||
349 | } | ||
350 | |||
351 | n = parse_unit(&str); | ||
352 | if(n < 0){ | ||
353 | printk(KERN_ERR "ubd_setup : couldn't parse unit number " | ||
354 | "'%s'\n", str); | ||
355 | return(1); | ||
356 | } | ||
357 | if(n >= MAX_DEV){ | ||
358 | printk(KERN_ERR "ubd_setup : index %d out of range " | ||
359 | "(%d devices, from 0 to %d)\n", n, MAX_DEV, MAX_DEV - 1); | ||
360 | return(1); | ||
361 | } | ||
362 | |||
363 | err = 1; | ||
364 | spin_lock(&ubd_lock); | ||
365 | |||
366 | dev = &ubd_dev[n]; | ||
367 | if(dev->file != NULL){ | ||
368 | printk(KERN_ERR "ubd_setup : device already configured\n"); | ||
369 | goto out; | ||
370 | } | ||
371 | |||
372 | if (index_out) | ||
373 | *index_out = n; | ||
374 | |||
375 | for (i = 0; i < 4; i++) { | ||
376 | switch (*str) { | ||
377 | case 'r': | ||
378 | flags.w = 0; | ||
379 | break; | ||
380 | case 's': | ||
381 | flags.s = 1; | ||
382 | break; | ||
383 | case 'd': | ||
384 | dev->no_cow = 1; | ||
385 | break; | ||
386 | case '=': | ||
387 | str++; | ||
388 | goto break_loop; | ||
389 | default: | ||
390 | printk(KERN_ERR "ubd_setup : Expected '=' or flag letter (r,s or d)\n"); | ||
391 | goto out; | ||
392 | } | ||
393 | str++; | ||
394 | } | ||
395 | |||
396 | if (*str == '=') | ||
397 | printk(KERN_ERR "ubd_setup : Too many flags specified\n"); | ||
398 | else | ||
399 | printk(KERN_ERR "ubd_setup : Expected '='\n"); | ||
400 | goto out; | ||
401 | |||
402 | break_loop: | ||
403 | err = 0; | ||
404 | backing_file = strchr(str, ','); | ||
405 | |||
406 | if (!backing_file) { | ||
407 | backing_file = strchr(str, ':'); | ||
408 | } | ||
409 | |||
410 | if(backing_file){ | ||
411 | if(dev->no_cow) | ||
412 | printk(KERN_ERR "Can't specify both 'd' and a " | ||
413 | "cow file\n"); | ||
414 | else { | ||
415 | *backing_file = '\0'; | ||
416 | backing_file++; | ||
417 | } | ||
418 | } | ||
419 | dev->file = str; | ||
420 | dev->cow.file = backing_file; | ||
421 | dev->boot_openflags = flags; | ||
422 | out: | ||
423 | spin_unlock(&ubd_lock); | ||
424 | return(err); | ||
425 | } | ||
426 | |||
427 | static int ubd_setup(char *str) | ||
428 | { | ||
429 | ubd_setup_common(str, NULL); | ||
430 | return(1); | ||
431 | } | ||
432 | |||
433 | __setup("ubd", ubd_setup); | ||
434 | __uml_help(ubd_setup, | ||
435 | "ubd<n><flags>=<filename>[(:|,)<filename2>]\n" | ||
436 | " This is used to associate a device with a file in the underlying\n" | ||
437 | " filesystem. When specifying two filenames, the first one is the\n" | ||
438 | " COW name and the second is the backing file name. As separator you can\n" | ||
439 | " use either a ':' or a ',': the first one allows writing things like;\n" | ||
440 | " ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n" | ||
441 | " while with a ',' the shell would not expand the 2nd '~'.\n" | ||
442 | " When using only one filename, UML will detect whether to thread it like\n" | ||
443 | " a COW file or a backing file. To override this detection, add the 'd'\n" | ||
444 | " flag:\n" | ||
445 | " ubd0d=BackingFile\n" | ||
446 | " Usually, there is a filesystem in the file, but \n" | ||
447 | " that's not required. Swap devices containing swap files can be\n" | ||
448 | " specified like this. Also, a file which doesn't contain a\n" | ||
449 | " filesystem can have its contents read in the virtual \n" | ||
450 | " machine by running 'dd' on the device. <n> must be in the range\n" | ||
451 | " 0 to 7. Appending an 'r' to the number will cause that device\n" | ||
452 | " to be mounted read-only. For example ubd1r=./ext_fs. Appending\n" | ||
453 | " an 's' will cause data to be written to disk on the host immediately.\n\n" | ||
454 | ); | ||
455 | |||
456 | static int udb_setup(char *str) | ||
457 | { | ||
458 | printk("udb%s specified on command line is almost certainly a ubd -> " | ||
459 | "udb TYPO\n", str); | ||
460 | return(1); | ||
461 | } | ||
462 | |||
463 | __setup("udb", udb_setup); | ||
464 | __uml_help(udb_setup, | ||
465 | "udb\n" | ||
466 | " This option is here solely to catch ubd -> udb typos, which can be\n\n" | ||
467 | " to impossible to catch visually unless you specifically look for\n\n" | ||
468 | " them. The only result of any option starting with 'udb' is an error\n\n" | ||
469 | " in the boot output.\n\n" | ||
470 | ); | ||
471 | |||
472 | static int fakehd_set = 0; | ||
473 | static int fakehd(char *str) | ||
474 | { | ||
475 | printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n"); | ||
476 | fakehd_set = 1; | ||
477 | return 1; | ||
478 | } | ||
479 | |||
480 | __setup("fakehd", fakehd); | ||
481 | __uml_help(fakehd, | ||
482 | "fakehd\n" | ||
483 | " Change the ubd device name to \"hd\".\n\n" | ||
484 | ); | ||
485 | |||
486 | static void do_ubd_request(request_queue_t * q); | ||
487 | |||
488 | /* Only changed by ubd_init, which is an initcall. */ | ||
489 | int thread_fd = -1; | ||
490 | |||
491 | /* Changed by ubd_handler, which is serialized because interrupts only | ||
492 | * happen on CPU 0. | ||
493 | */ | ||
494 | int intr_count = 0; | ||
495 | |||
496 | /* call ubd_finish if you need to serialize */ | ||
497 | static void __ubd_finish(struct request *req, int error) | ||
498 | { | ||
499 | int nsect; | ||
500 | |||
501 | if(error){ | ||
502 | end_request(req, 0); | ||
503 | return; | ||
504 | } | ||
505 | nsect = req->current_nr_sectors; | ||
506 | req->sector += nsect; | ||
507 | req->buffer += nsect << 9; | ||
508 | req->errors = 0; | ||
509 | req->nr_sectors -= nsect; | ||
510 | req->current_nr_sectors = 0; | ||
511 | end_request(req, 1); | ||
512 | } | ||
513 | |||
514 | static inline void ubd_finish(struct request *req, int error) | ||
515 | { | ||
516 | spin_lock(&ubd_io_lock); | ||
517 | __ubd_finish(req, error); | ||
518 | spin_unlock(&ubd_io_lock); | ||
519 | } | ||
520 | |||
521 | /* Called without ubd_io_lock held */ | ||
522 | static void ubd_handler(void) | ||
523 | { | ||
524 | struct io_thread_req req; | ||
525 | struct request *rq = elv_next_request(ubd_queue); | ||
526 | int n, err; | ||
527 | |||
528 | do_ubd = NULL; | ||
529 | intr_count++; | ||
530 | n = os_read_file(thread_fd, &req, sizeof(req)); | ||
531 | if(n != sizeof(req)){ | ||
532 | printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, " | ||
533 | "err = %d\n", os_getpid(), -n); | ||
534 | spin_lock(&ubd_io_lock); | ||
535 | end_request(rq, 0); | ||
536 | spin_unlock(&ubd_io_lock); | ||
537 | return; | ||
538 | } | ||
539 | |||
540 | if((req.op != UBD_MMAP) && | ||
541 | ((req.offset != ((__u64) (rq->sector)) << 9) || | ||
542 | (req.length != (rq->current_nr_sectors) << 9))) | ||
543 | panic("I/O op mismatch"); | ||
544 | |||
545 | if(req.map_fd != -1){ | ||
546 | err = physmem_subst_mapping(req.buffer, req.map_fd, | ||
547 | req.map_offset, 1); | ||
548 | if(err) | ||
549 | printk("ubd_handler - physmem_subst_mapping failed, " | ||
550 | "err = %d\n", -err); | ||
551 | } | ||
552 | |||
553 | ubd_finish(rq, req.error); | ||
554 | reactivate_fd(thread_fd, UBD_IRQ); | ||
555 | do_ubd_request(ubd_queue); | ||
556 | } | ||
557 | |||
558 | static irqreturn_t ubd_intr(int irq, void *dev, struct pt_regs *unused) | ||
559 | { | ||
560 | ubd_handler(); | ||
561 | return(IRQ_HANDLED); | ||
562 | } | ||
563 | |||
564 | /* Only changed by ubd_init, which is an initcall. */ | ||
565 | static int io_pid = -1; | ||
566 | |||
567 | void kill_io_thread(void) | ||
568 | { | ||
569 | if(io_pid != -1) | ||
570 | os_kill_process(io_pid, 1); | ||
571 | } | ||
572 | |||
573 | __uml_exitcall(kill_io_thread); | ||
574 | |||
575 | static int ubd_file_size(struct ubd *dev, __u64 *size_out) | ||
576 | { | ||
577 | char *file; | ||
578 | |||
579 | file = dev->cow.file ? dev->cow.file : dev->file; | ||
580 | return(os_file_size(file, size_out)); | ||
581 | } | ||
582 | |||
583 | static void ubd_close(struct ubd *dev) | ||
584 | { | ||
585 | if(ubd_do_mmap) | ||
586 | physmem_forget_descriptor(dev->fd); | ||
587 | os_close_file(dev->fd); | ||
588 | if(dev->cow.file == NULL) | ||
589 | return; | ||
590 | |||
591 | if(ubd_do_mmap) | ||
592 | physmem_forget_descriptor(dev->cow.fd); | ||
593 | os_close_file(dev->cow.fd); | ||
594 | vfree(dev->cow.bitmap); | ||
595 | dev->cow.bitmap = NULL; | ||
596 | } | ||
597 | |||
598 | static int ubd_open_dev(struct ubd *dev) | ||
599 | { | ||
600 | struct openflags flags; | ||
601 | char **back_ptr; | ||
602 | int err, create_cow, *create_ptr; | ||
603 | |||
604 | dev->openflags = dev->boot_openflags; | ||
605 | create_cow = 0; | ||
606 | create_ptr = (dev->cow.file != NULL) ? &create_cow : NULL; | ||
607 | back_ptr = dev->no_cow ? NULL : &dev->cow.file; | ||
608 | dev->fd = open_ubd_file(dev->file, &dev->openflags, back_ptr, | ||
609 | &dev->cow.bitmap_offset, &dev->cow.bitmap_len, | ||
610 | &dev->cow.data_offset, create_ptr); | ||
611 | |||
612 | if((dev->fd == -ENOENT) && create_cow){ | ||
613 | dev->fd = create_cow_file(dev->file, dev->cow.file, | ||
614 | dev->openflags, 1 << 9, PAGE_SIZE, | ||
615 | &dev->cow.bitmap_offset, | ||
616 | &dev->cow.bitmap_len, | ||
617 | &dev->cow.data_offset); | ||
618 | if(dev->fd >= 0){ | ||
619 | printk(KERN_INFO "Creating \"%s\" as COW file for " | ||
620 | "\"%s\"\n", dev->file, dev->cow.file); | ||
621 | } | ||
622 | } | ||
623 | |||
624 | if(dev->fd < 0){ | ||
625 | printk("Failed to open '%s', errno = %d\n", dev->file, | ||
626 | -dev->fd); | ||
627 | return(dev->fd); | ||
628 | } | ||
629 | |||
630 | if(dev->cow.file != NULL){ | ||
631 | err = -ENOMEM; | ||
632 | dev->cow.bitmap = (void *) vmalloc(dev->cow.bitmap_len); | ||
633 | if(dev->cow.bitmap == NULL){ | ||
634 | printk(KERN_ERR "Failed to vmalloc COW bitmap\n"); | ||
635 | goto error; | ||
636 | } | ||
637 | flush_tlb_kernel_vm(); | ||
638 | |||
639 | err = read_cow_bitmap(dev->fd, dev->cow.bitmap, | ||
640 | dev->cow.bitmap_offset, | ||
641 | dev->cow.bitmap_len); | ||
642 | if(err < 0) | ||
643 | goto error; | ||
644 | |||
645 | flags = dev->openflags; | ||
646 | flags.w = 0; | ||
647 | err = open_ubd_file(dev->cow.file, &flags, NULL, NULL, NULL, | ||
648 | NULL, NULL); | ||
649 | if(err < 0) goto error; | ||
650 | dev->cow.fd = err; | ||
651 | } | ||
652 | return(0); | ||
653 | error: | ||
654 | os_close_file(dev->fd); | ||
655 | return(err); | ||
656 | } | ||
657 | |||
658 | static int ubd_new_disk(int major, u64 size, int unit, | ||
659 | struct gendisk **disk_out) | ||
660 | |||
661 | { | ||
662 | struct gendisk *disk; | ||
663 | char from[sizeof("ubd/nnnnn\0")], to[sizeof("discnnnnn/disc\0")]; | ||
664 | int err; | ||
665 | |||
666 | disk = alloc_disk(1 << UBD_SHIFT); | ||
667 | if(disk == NULL) | ||
668 | return(-ENOMEM); | ||
669 | |||
670 | disk->major = major; | ||
671 | disk->first_minor = unit << UBD_SHIFT; | ||
672 | disk->fops = &ubd_blops; | ||
673 | set_capacity(disk, size / 512); | ||
674 | if(major == MAJOR_NR){ | ||
675 | sprintf(disk->disk_name, "ubd%c", 'a' + unit); | ||
676 | sprintf(disk->devfs_name, "ubd/disc%d", unit); | ||
677 | sprintf(from, "ubd/%d", unit); | ||
678 | sprintf(to, "disc%d/disc", unit); | ||
679 | err = devfs_mk_symlink(from, to); | ||
680 | if(err) | ||
681 | printk("ubd_new_disk failed to make link from %s to " | ||
682 | "%s, error = %d\n", from, to, err); | ||
683 | } | ||
684 | else { | ||
685 | sprintf(disk->disk_name, "ubd_fake%d", unit); | ||
686 | sprintf(disk->devfs_name, "ubd_fake/disc%d", unit); | ||
687 | } | ||
688 | |||
689 | /* sysfs register (not for ide fake devices) */ | ||
690 | if (major == MAJOR_NR) { | ||
691 | ubd_dev[unit].pdev.id = unit; | ||
692 | ubd_dev[unit].pdev.name = DRIVER_NAME; | ||
693 | platform_device_register(&ubd_dev[unit].pdev); | ||
694 | disk->driverfs_dev = &ubd_dev[unit].pdev.dev; | ||
695 | } | ||
696 | |||
697 | disk->private_data = &ubd_dev[unit]; | ||
698 | disk->queue = ubd_queue; | ||
699 | add_disk(disk); | ||
700 | |||
701 | *disk_out = disk; | ||
702 | return 0; | ||
703 | } | ||
704 | |||
705 | #define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9)) | ||
706 | |||
707 | static int ubd_add(int n) | ||
708 | { | ||
709 | struct ubd *dev = &ubd_dev[n]; | ||
710 | int err; | ||
711 | |||
712 | if(dev->file == NULL) | ||
713 | return(-ENODEV); | ||
714 | |||
715 | if (ubd_open_dev(dev)) | ||
716 | return(-ENODEV); | ||
717 | |||
718 | err = ubd_file_size(dev, &dev->size); | ||
719 | if(err < 0) | ||
720 | return(err); | ||
721 | |||
722 | dev->size = ROUND_BLOCK(dev->size); | ||
723 | |||
724 | err = ubd_new_disk(MAJOR_NR, dev->size, n, &ubd_gendisk[n]); | ||
725 | if(err) | ||
726 | return(err); | ||
727 | |||
728 | if(fake_major != MAJOR_NR) | ||
729 | ubd_new_disk(fake_major, dev->size, n, | ||
730 | &fake_gendisk[n]); | ||
731 | |||
732 | /* perhaps this should also be under the "if (fake_major)" above */ | ||
733 | /* using the fake_disk->disk_name and also the fakehd_set name */ | ||
734 | if (fake_ide) | ||
735 | make_ide_entries(ubd_gendisk[n]->disk_name); | ||
736 | |||
737 | ubd_close(dev); | ||
738 | return 0; | ||
739 | } | ||
740 | |||
741 | static int ubd_config(char *str) | ||
742 | { | ||
743 | int n, err; | ||
744 | |||
745 | str = uml_strdup(str); | ||
746 | if(str == NULL){ | ||
747 | printk(KERN_ERR "ubd_config failed to strdup string\n"); | ||
748 | return(1); | ||
749 | } | ||
750 | err = ubd_setup_common(str, &n); | ||
751 | if(err){ | ||
752 | kfree(str); | ||
753 | return(-1); | ||
754 | } | ||
755 | if(n == -1) return(0); | ||
756 | |||
757 | spin_lock(&ubd_lock); | ||
758 | err = ubd_add(n); | ||
759 | if(err) | ||
760 | ubd_dev[n].file = NULL; | ||
761 | spin_unlock(&ubd_lock); | ||
762 | |||
763 | return(err); | ||
764 | } | ||
765 | |||
766 | static int ubd_get_config(char *name, char *str, int size, char **error_out) | ||
767 | { | ||
768 | struct ubd *dev; | ||
769 | int n, len = 0; | ||
770 | |||
771 | n = parse_unit(&name); | ||
772 | if((n >= MAX_DEV) || (n < 0)){ | ||
773 | *error_out = "ubd_get_config : device number out of range"; | ||
774 | return(-1); | ||
775 | } | ||
776 | |||
777 | dev = &ubd_dev[n]; | ||
778 | spin_lock(&ubd_lock); | ||
779 | |||
780 | if(dev->file == NULL){ | ||
781 | CONFIG_CHUNK(str, size, len, "", 1); | ||
782 | goto out; | ||
783 | } | ||
784 | |||
785 | CONFIG_CHUNK(str, size, len, dev->file, 0); | ||
786 | |||
787 | if(dev->cow.file != NULL){ | ||
788 | CONFIG_CHUNK(str, size, len, ",", 0); | ||
789 | CONFIG_CHUNK(str, size, len, dev->cow.file, 1); | ||
790 | } | ||
791 | else CONFIG_CHUNK(str, size, len, "", 1); | ||
792 | |||
793 | out: | ||
794 | spin_unlock(&ubd_lock); | ||
795 | return(len); | ||
796 | } | ||
797 | |||
798 | static int ubd_remove(char *str) | ||
799 | { | ||
800 | struct ubd *dev; | ||
801 | int n, err = -ENODEV; | ||
802 | |||
803 | n = parse_unit(&str); | ||
804 | |||
805 | if((n < 0) || (n >= MAX_DEV)) | ||
806 | return(err); | ||
807 | |||
808 | dev = &ubd_dev[n]; | ||
809 | if(dev->count > 0) | ||
810 | return(-EBUSY); /* you cannot remove a open disk */ | ||
811 | |||
812 | err = 0; | ||
813 | spin_lock(&ubd_lock); | ||
814 | |||
815 | if(ubd_gendisk[n] == NULL) | ||
816 | goto out; | ||
817 | |||
818 | del_gendisk(ubd_gendisk[n]); | ||
819 | put_disk(ubd_gendisk[n]); | ||
820 | ubd_gendisk[n] = NULL; | ||
821 | |||
822 | if(fake_gendisk[n] != NULL){ | ||
823 | del_gendisk(fake_gendisk[n]); | ||
824 | put_disk(fake_gendisk[n]); | ||
825 | fake_gendisk[n] = NULL; | ||
826 | } | ||
827 | |||
828 | platform_device_unregister(&dev->pdev); | ||
829 | *dev = ((struct ubd) DEFAULT_UBD); | ||
830 | err = 0; | ||
831 | out: | ||
832 | spin_unlock(&ubd_lock); | ||
833 | return(err); | ||
834 | } | ||
835 | |||
836 | static struct mc_device ubd_mc = { | ||
837 | .name = "ubd", | ||
838 | .config = ubd_config, | ||
839 | .get_config = ubd_get_config, | ||
840 | .remove = ubd_remove, | ||
841 | }; | ||
842 | |||
843 | static int ubd_mc_init(void) | ||
844 | { | ||
845 | mconsole_register_dev(&ubd_mc); | ||
846 | return 0; | ||
847 | } | ||
848 | |||
849 | __initcall(ubd_mc_init); | ||
850 | |||
851 | static struct device_driver ubd_driver = { | ||
852 | .name = DRIVER_NAME, | ||
853 | .bus = &platform_bus_type, | ||
854 | }; | ||
855 | |||
856 | int ubd_init(void) | ||
857 | { | ||
858 | int i; | ||
859 | |||
860 | devfs_mk_dir("ubd"); | ||
861 | if (register_blkdev(MAJOR_NR, "ubd")) | ||
862 | return -1; | ||
863 | |||
864 | ubd_queue = blk_init_queue(do_ubd_request, &ubd_io_lock); | ||
865 | if (!ubd_queue) { | ||
866 | unregister_blkdev(MAJOR_NR, "ubd"); | ||
867 | return -1; | ||
868 | } | ||
869 | |||
870 | if (fake_major != MAJOR_NR) { | ||
871 | char name[sizeof("ubd_nnn\0")]; | ||
872 | |||
873 | snprintf(name, sizeof(name), "ubd_%d", fake_major); | ||
874 | devfs_mk_dir(name); | ||
875 | if (register_blkdev(fake_major, "ubd")) | ||
876 | return -1; | ||
877 | } | ||
878 | driver_register(&ubd_driver); | ||
879 | for (i = 0; i < MAX_DEV; i++) | ||
880 | ubd_add(i); | ||
881 | return 0; | ||
882 | } | ||
883 | |||
884 | late_initcall(ubd_init); | ||
885 | |||
886 | int ubd_driver_init(void){ | ||
887 | unsigned long stack; | ||
888 | int err; | ||
889 | |||
890 | /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/ | ||
891 | if(global_openflags.s){ | ||
892 | printk(KERN_INFO "ubd: Synchronous mode\n"); | ||
893 | /* Letting ubd=sync be like using ubd#s= instead of ubd#= is | ||
894 | * enough. So use anyway the io thread. */ | ||
895 | } | ||
896 | stack = alloc_stack(0, 0); | ||
897 | io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *), | ||
898 | &thread_fd); | ||
899 | if(io_pid < 0){ | ||
900 | printk(KERN_ERR | ||
901 | "ubd : Failed to start I/O thread (errno = %d) - " | ||
902 | "falling back to synchronous I/O\n", -io_pid); | ||
903 | io_pid = -1; | ||
904 | return(0); | ||
905 | } | ||
906 | err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr, | ||
907 | SA_INTERRUPT, "ubd", ubd_dev); | ||
908 | if(err != 0) | ||
909 | printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err); | ||
910 | return(err); | ||
911 | } | ||
912 | |||
913 | device_initcall(ubd_driver_init); | ||
914 | |||
915 | static int ubd_open(struct inode *inode, struct file *filp) | ||
916 | { | ||
917 | struct gendisk *disk = inode->i_bdev->bd_disk; | ||
918 | struct ubd *dev = disk->private_data; | ||
919 | int err = 0; | ||
920 | |||
921 | if(dev->count == 0){ | ||
922 | err = ubd_open_dev(dev); | ||
923 | if(err){ | ||
924 | printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n", | ||
925 | disk->disk_name, dev->file, -err); | ||
926 | goto out; | ||
927 | } | ||
928 | } | ||
929 | dev->count++; | ||
930 | if((filp->f_mode & FMODE_WRITE) && !dev->openflags.w){ | ||
931 | if(--dev->count == 0) ubd_close(dev); | ||
932 | err = -EROFS; | ||
933 | } | ||
934 | out: | ||
935 | return(err); | ||
936 | } | ||
937 | |||
938 | static int ubd_release(struct inode * inode, struct file * file) | ||
939 | { | ||
940 | struct gendisk *disk = inode->i_bdev->bd_disk; | ||
941 | struct ubd *dev = disk->private_data; | ||
942 | |||
943 | if(--dev->count == 0) | ||
944 | ubd_close(dev); | ||
945 | return(0); | ||
946 | } | ||
947 | |||
948 | static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask, | ||
949 | __u64 *cow_offset, unsigned long *bitmap, | ||
950 | __u64 bitmap_offset, unsigned long *bitmap_words, | ||
951 | __u64 bitmap_len) | ||
952 | { | ||
953 | __u64 sector = io_offset >> 9; | ||
954 | int i, update_bitmap = 0; | ||
955 | |||
956 | for(i = 0; i < length >> 9; i++){ | ||
957 | if(cow_mask != NULL) | ||
958 | ubd_set_bit(i, (unsigned char *) cow_mask); | ||
959 | if(ubd_test_bit(sector + i, (unsigned char *) bitmap)) | ||
960 | continue; | ||
961 | |||
962 | update_bitmap = 1; | ||
963 | ubd_set_bit(sector + i, (unsigned char *) bitmap); | ||
964 | } | ||
965 | |||
966 | if(!update_bitmap) | ||
967 | return; | ||
968 | |||
969 | *cow_offset = sector / (sizeof(unsigned long) * 8); | ||
970 | |||
971 | /* This takes care of the case where we're exactly at the end of the | ||
972 | * device, and *cow_offset + 1 is off the end. So, just back it up | ||
973 | * by one word. Thanks to Lynn Kerby for the fix and James McMechan | ||
974 | * for the original diagnosis. | ||
975 | */ | ||
976 | if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) / | ||
977 | sizeof(unsigned long) - 1)) | ||
978 | (*cow_offset)--; | ||
979 | |||
980 | bitmap_words[0] = bitmap[*cow_offset]; | ||
981 | bitmap_words[1] = bitmap[*cow_offset + 1]; | ||
982 | |||
983 | *cow_offset *= sizeof(unsigned long); | ||
984 | *cow_offset += bitmap_offset; | ||
985 | } | ||
986 | |||
987 | static void cowify_req(struct io_thread_req *req, unsigned long *bitmap, | ||
988 | __u64 bitmap_offset, __u64 bitmap_len) | ||
989 | { | ||
990 | __u64 sector = req->offset >> 9; | ||
991 | int i; | ||
992 | |||
993 | if(req->length > (sizeof(req->sector_mask) * 8) << 9) | ||
994 | panic("Operation too long"); | ||
995 | |||
996 | if(req->op == UBD_READ) { | ||
997 | for(i = 0; i < req->length >> 9; i++){ | ||
998 | if(ubd_test_bit(sector + i, (unsigned char *) bitmap)) | ||
999 | ubd_set_bit(i, (unsigned char *) | ||
1000 | &req->sector_mask); | ||
1001 | } | ||
1002 | } | ||
1003 | else cowify_bitmap(req->offset, req->length, &req->sector_mask, | ||
1004 | &req->cow_offset, bitmap, bitmap_offset, | ||
1005 | req->bitmap_words, bitmap_len); | ||
1006 | } | ||
1007 | |||
1008 | static int mmap_fd(struct request *req, struct ubd *dev, __u64 offset) | ||
1009 | { | ||
1010 | __u64 sector; | ||
1011 | unsigned char *bitmap; | ||
1012 | int bit, i; | ||
1013 | |||
1014 | /* mmap must have been requested on the command line */ | ||
1015 | if(!ubd_do_mmap) | ||
1016 | return(-1); | ||
1017 | |||
1018 | /* The buffer must be page aligned */ | ||
1019 | if(((unsigned long) req->buffer % UBD_MMAP_BLOCK_SIZE) != 0) | ||
1020 | return(-1); | ||
1021 | |||
1022 | /* The request must be a page long */ | ||
1023 | if((req->current_nr_sectors << 9) != PAGE_SIZE) | ||
1024 | return(-1); | ||
1025 | |||
1026 | if(dev->cow.file == NULL) | ||
1027 | return(dev->fd); | ||
1028 | |||
1029 | sector = offset >> 9; | ||
1030 | bitmap = (unsigned char *) dev->cow.bitmap; | ||
1031 | bit = ubd_test_bit(sector, bitmap); | ||
1032 | |||
1033 | for(i = 1; i < req->current_nr_sectors; i++){ | ||
1034 | if(ubd_test_bit(sector + i, bitmap) != bit) | ||
1035 | return(-1); | ||
1036 | } | ||
1037 | |||
1038 | if(bit || (rq_data_dir(req) == WRITE)) | ||
1039 | offset += dev->cow.data_offset; | ||
1040 | |||
1041 | /* The data on disk must be page aligned */ | ||
1042 | if((offset % UBD_MMAP_BLOCK_SIZE) != 0) | ||
1043 | return(-1); | ||
1044 | |||
1045 | return(bit ? dev->fd : dev->cow.fd); | ||
1046 | } | ||
1047 | |||
1048 | static int prepare_mmap_request(struct ubd *dev, int fd, __u64 offset, | ||
1049 | struct request *req, | ||
1050 | struct io_thread_req *io_req) | ||
1051 | { | ||
1052 | int err; | ||
1053 | |||
1054 | if(rq_data_dir(req) == WRITE){ | ||
1055 | /* Writes are almost no-ops since the new data is already in the | ||
1056 | * host page cache | ||
1057 | */ | ||
1058 | dev->map_writes++; | ||
1059 | if(dev->cow.file != NULL) | ||
1060 | cowify_bitmap(io_req->offset, io_req->length, | ||
1061 | &io_req->sector_mask, &io_req->cow_offset, | ||
1062 | dev->cow.bitmap, dev->cow.bitmap_offset, | ||
1063 | io_req->bitmap_words, | ||
1064 | dev->cow.bitmap_len); | ||
1065 | } | ||
1066 | else { | ||
1067 | int w; | ||
1068 | |||
1069 | if((dev->cow.file != NULL) && (fd == dev->cow.fd)) | ||
1070 | w = 0; | ||
1071 | else w = dev->openflags.w; | ||
1072 | |||
1073 | if((dev->cow.file != NULL) && (fd == dev->fd)) | ||
1074 | offset += dev->cow.data_offset; | ||
1075 | |||
1076 | err = physmem_subst_mapping(req->buffer, fd, offset, w); | ||
1077 | if(err){ | ||
1078 | printk("physmem_subst_mapping failed, err = %d\n", | ||
1079 | -err); | ||
1080 | return(1); | ||
1081 | } | ||
1082 | dev->map_reads++; | ||
1083 | } | ||
1084 | io_req->op = UBD_MMAP; | ||
1085 | io_req->buffer = req->buffer; | ||
1086 | return(0); | ||
1087 | } | ||
1088 | |||
1089 | /* Called with ubd_io_lock held */ | ||
1090 | static int prepare_request(struct request *req, struct io_thread_req *io_req) | ||
1091 | { | ||
1092 | struct gendisk *disk = req->rq_disk; | ||
1093 | struct ubd *dev = disk->private_data; | ||
1094 | __u64 offset; | ||
1095 | int len, fd; | ||
1096 | |||
1097 | if(req->rq_status == RQ_INACTIVE) return(1); | ||
1098 | |||
1099 | if((rq_data_dir(req) == WRITE) && !dev->openflags.w){ | ||
1100 | printk("Write attempted on readonly ubd device %s\n", | ||
1101 | disk->disk_name); | ||
1102 | end_request(req, 0); | ||
1103 | return(1); | ||
1104 | } | ||
1105 | |||
1106 | offset = ((__u64) req->sector) << 9; | ||
1107 | len = req->current_nr_sectors << 9; | ||
1108 | |||
1109 | io_req->fds[0] = (dev->cow.file != NULL) ? dev->cow.fd : dev->fd; | ||
1110 | io_req->fds[1] = dev->fd; | ||
1111 | io_req->map_fd = -1; | ||
1112 | io_req->cow_offset = -1; | ||
1113 | io_req->offset = offset; | ||
1114 | io_req->length = len; | ||
1115 | io_req->error = 0; | ||
1116 | io_req->sector_mask = 0; | ||
1117 | |||
1118 | fd = mmap_fd(req, dev, io_req->offset); | ||
1119 | if(fd > 0){ | ||
1120 | /* If mmapping is otherwise OK, but the first access to the | ||
1121 | * page is a write, then it's not mapped in yet. So we have | ||
1122 | * to write the data to disk first, then we can map the disk | ||
1123 | * page in and continue normally from there. | ||
1124 | */ | ||
1125 | if((rq_data_dir(req) == WRITE) && !is_remapped(req->buffer)){ | ||
1126 | io_req->map_fd = dev->fd; | ||
1127 | io_req->map_offset = io_req->offset + | ||
1128 | dev->cow.data_offset; | ||
1129 | dev->write_maps++; | ||
1130 | } | ||
1131 | else return(prepare_mmap_request(dev, fd, io_req->offset, req, | ||
1132 | io_req)); | ||
1133 | } | ||
1134 | |||
1135 | if(rq_data_dir(req) == READ) | ||
1136 | dev->nomap_reads++; | ||
1137 | else dev->nomap_writes++; | ||
1138 | |||
1139 | io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE; | ||
1140 | io_req->offsets[0] = 0; | ||
1141 | io_req->offsets[1] = dev->cow.data_offset; | ||
1142 | io_req->buffer = req->buffer; | ||
1143 | io_req->sectorsize = 1 << 9; | ||
1144 | |||
1145 | if(dev->cow.file != NULL) | ||
1146 | cowify_req(io_req, dev->cow.bitmap, dev->cow.bitmap_offset, | ||
1147 | dev->cow.bitmap_len); | ||
1148 | |||
1149 | return(0); | ||
1150 | } | ||
1151 | |||
1152 | /* Called with ubd_io_lock held */ | ||
1153 | static void do_ubd_request(request_queue_t *q) | ||
1154 | { | ||
1155 | struct io_thread_req io_req; | ||
1156 | struct request *req; | ||
1157 | int err, n; | ||
1158 | |||
1159 | if(thread_fd == -1){ | ||
1160 | while((req = elv_next_request(q)) != NULL){ | ||
1161 | err = prepare_request(req, &io_req); | ||
1162 | if(!err){ | ||
1163 | do_io(&io_req); | ||
1164 | __ubd_finish(req, io_req.error); | ||
1165 | } | ||
1166 | } | ||
1167 | } | ||
1168 | else { | ||
1169 | if(do_ubd || (req = elv_next_request(q)) == NULL) | ||
1170 | return; | ||
1171 | err = prepare_request(req, &io_req); | ||
1172 | if(!err){ | ||
1173 | do_ubd = ubd_handler; | ||
1174 | n = os_write_file(thread_fd, (char *) &io_req, | ||
1175 | sizeof(io_req)); | ||
1176 | if(n != sizeof(io_req)) | ||
1177 | printk("write to io thread failed, " | ||
1178 | "errno = %d\n", -n); | ||
1179 | } | ||
1180 | } | ||
1181 | } | ||
1182 | |||
1183 | static int ubd_ioctl(struct inode * inode, struct file * file, | ||
1184 | unsigned int cmd, unsigned long arg) | ||
1185 | { | ||
1186 | struct hd_geometry __user *loc = (struct hd_geometry __user *) arg; | ||
1187 | struct ubd *dev = inode->i_bdev->bd_disk->private_data; | ||
1188 | struct hd_driveid ubd_id = { | ||
1189 | .cyls = 0, | ||
1190 | .heads = 128, | ||
1191 | .sectors = 32, | ||
1192 | }; | ||
1193 | |||
1194 | switch (cmd) { | ||
1195 | struct hd_geometry g; | ||
1196 | struct cdrom_volctrl volume; | ||
1197 | case HDIO_GETGEO: | ||
1198 | if(!loc) return(-EINVAL); | ||
1199 | g.heads = 128; | ||
1200 | g.sectors = 32; | ||
1201 | g.cylinders = dev->size / (128 * 32 * 512); | ||
1202 | g.start = get_start_sect(inode->i_bdev); | ||
1203 | return(copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0); | ||
1204 | |||
1205 | case HDIO_GET_IDENTITY: | ||
1206 | ubd_id.cyls = dev->size / (128 * 32 * 512); | ||
1207 | if(copy_to_user((char __user *) arg, (char *) &ubd_id, | ||
1208 | sizeof(ubd_id))) | ||
1209 | return(-EFAULT); | ||
1210 | return(0); | ||
1211 | |||
1212 | case CDROMVOLREAD: | ||
1213 | if(copy_from_user(&volume, (char __user *) arg, sizeof(volume))) | ||
1214 | return(-EFAULT); | ||
1215 | volume.channel0 = 255; | ||
1216 | volume.channel1 = 255; | ||
1217 | volume.channel2 = 255; | ||
1218 | volume.channel3 = 255; | ||
1219 | if(copy_to_user((char __user *) arg, &volume, sizeof(volume))) | ||
1220 | return(-EFAULT); | ||
1221 | return(0); | ||
1222 | } | ||
1223 | return(-EINVAL); | ||
1224 | } | ||
1225 | |||
1226 | static int ubd_check_remapped(int fd, unsigned long address, int is_write, | ||
1227 | __u64 offset) | ||
1228 | { | ||
1229 | __u64 bitmap_offset; | ||
1230 | unsigned long new_bitmap[2]; | ||
1231 | int i, err, n; | ||
1232 | |||
1233 | /* If it's not a write access, we can't do anything about it */ | ||
1234 | if(!is_write) | ||
1235 | return(0); | ||
1236 | |||
1237 | /* We have a write */ | ||
1238 | for(i = 0; i < sizeof(ubd_dev) / sizeof(ubd_dev[0]); i++){ | ||
1239 | struct ubd *dev = &ubd_dev[i]; | ||
1240 | |||
1241 | if((dev->fd != fd) && (dev->cow.fd != fd)) | ||
1242 | continue; | ||
1243 | |||
1244 | /* It's a write to a ubd device */ | ||
1245 | |||
1246 | if(!dev->openflags.w){ | ||
1247 | /* It's a write access on a read-only device - probably | ||
1248 | * shouldn't happen. If the kernel is trying to change | ||
1249 | * something with no intention of writing it back out, | ||
1250 | * then this message will clue us in that this needs | ||
1251 | * fixing | ||
1252 | */ | ||
1253 | printk("Write access to mapped page from readonly ubd " | ||
1254 | "device %d\n", i); | ||
1255 | return(0); | ||
1256 | } | ||
1257 | |||
1258 | /* It's a write to a writeable ubd device - it must be COWed | ||
1259 | * because, otherwise, the page would have been mapped in | ||
1260 | * writeable | ||
1261 | */ | ||
1262 | |||
1263 | if(!dev->cow.file) | ||
1264 | panic("Write fault on writeable non-COW ubd device %d", | ||
1265 | i); | ||
1266 | |||
1267 | /* It should also be an access to the backing file since the | ||
1268 | * COW pages should be mapped in read-write | ||
1269 | */ | ||
1270 | |||
1271 | if(fd == dev->fd) | ||
1272 | panic("Write fault on a backing page of ubd " | ||
1273 | "device %d\n", i); | ||
1274 | |||
1275 | /* So, we do the write, copying the backing data to the COW | ||
1276 | * file... | ||
1277 | */ | ||
1278 | |||
1279 | err = os_seek_file(dev->fd, offset + dev->cow.data_offset); | ||
1280 | if(err < 0) | ||
1281 | panic("Couldn't seek to %lld in COW file of ubd " | ||
1282 | "device %d, err = %d", | ||
1283 | offset + dev->cow.data_offset, i, -err); | ||
1284 | |||
1285 | n = os_write_file(dev->fd, (void *) address, PAGE_SIZE); | ||
1286 | if(n != PAGE_SIZE) | ||
1287 | panic("Couldn't copy data to COW file of ubd " | ||
1288 | "device %d, err = %d", i, -n); | ||
1289 | |||
1290 | /* ... updating the COW bitmap... */ | ||
1291 | |||
1292 | cowify_bitmap(offset, PAGE_SIZE, NULL, &bitmap_offset, | ||
1293 | dev->cow.bitmap, dev->cow.bitmap_offset, | ||
1294 | new_bitmap, dev->cow.bitmap_len); | ||
1295 | |||
1296 | err = os_seek_file(dev->fd, bitmap_offset); | ||
1297 | if(err < 0) | ||
1298 | panic("Couldn't seek to %lld in COW file of ubd " | ||
1299 | "device %d, err = %d", bitmap_offset, i, -err); | ||
1300 | |||
1301 | n = os_write_file(dev->fd, new_bitmap, sizeof(new_bitmap)); | ||
1302 | if(n != sizeof(new_bitmap)) | ||
1303 | panic("Couldn't update bitmap of ubd device %d, " | ||
1304 | "err = %d", i, -n); | ||
1305 | |||
1306 | /* Maybe we can map the COW page in, and maybe we can't. If | ||
1307 | * it is a pre-V3 COW file, we can't, since the alignment will | ||
1308 | * be wrong. If it is a V3 or later COW file which has been | ||
1309 | * moved to a system with a larger page size, then maybe we | ||
1310 | * can't, depending on the exact location of the page. | ||
1311 | */ | ||
1312 | |||
1313 | offset += dev->cow.data_offset; | ||
1314 | |||
1315 | /* Remove the remapping, putting the original anonymous page | ||
1316 | * back. If the COW file can be mapped in, that is done. | ||
1317 | * Otherwise, the COW page is read in. | ||
1318 | */ | ||
1319 | |||
1320 | if(!physmem_remove_mapping((void *) address)) | ||
1321 | panic("Address 0x%lx not remapped by ubd device %d", | ||
1322 | address, i); | ||
1323 | if((offset % UBD_MMAP_BLOCK_SIZE) == 0) | ||
1324 | physmem_subst_mapping((void *) address, dev->fd, | ||
1325 | offset, 1); | ||
1326 | else { | ||
1327 | err = os_seek_file(dev->fd, offset); | ||
1328 | if(err < 0) | ||
1329 | panic("Couldn't seek to %lld in COW file of " | ||
1330 | "ubd device %d, err = %d", offset, i, | ||
1331 | -err); | ||
1332 | |||
1333 | n = os_read_file(dev->fd, (void *) address, PAGE_SIZE); | ||
1334 | if(n != PAGE_SIZE) | ||
1335 | panic("Failed to read page from offset %llx of " | ||
1336 | "COW file of ubd device %d, err = %d", | ||
1337 | offset, i, -n); | ||
1338 | } | ||
1339 | |||
1340 | return(1); | ||
1341 | } | ||
1342 | |||
1343 | /* It's not a write on a ubd device */ | ||
1344 | return(0); | ||
1345 | } | ||
1346 | |||
1347 | static struct remapper ubd_remapper = { | ||
1348 | .list = LIST_HEAD_INIT(ubd_remapper.list), | ||
1349 | .proc = ubd_check_remapped, | ||
1350 | }; | ||
1351 | |||
1352 | static int ubd_remapper_setup(void) | ||
1353 | { | ||
1354 | if(ubd_do_mmap) | ||
1355 | register_remapper(&ubd_remapper); | ||
1356 | |||
1357 | return(0); | ||
1358 | } | ||
1359 | |||
1360 | __initcall(ubd_remapper_setup); | ||
1361 | |||
1362 | static int same_backing_files(char *from_cmdline, char *from_cow, char *cow) | ||
1363 | { | ||
1364 | struct uml_stat buf1, buf2; | ||
1365 | int err; | ||
1366 | |||
1367 | if(from_cmdline == NULL) return(1); | ||
1368 | if(!strcmp(from_cmdline, from_cow)) return(1); | ||
1369 | |||
1370 | err = os_stat_file(from_cmdline, &buf1); | ||
1371 | if(err < 0){ | ||
1372 | printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err); | ||
1373 | return(1); | ||
1374 | } | ||
1375 | err = os_stat_file(from_cow, &buf2); | ||
1376 | if(err < 0){ | ||
1377 | printk("Couldn't stat '%s', err = %d\n", from_cow, -err); | ||
1378 | return(1); | ||
1379 | } | ||
1380 | if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino)) | ||
1381 | return(1); | ||
1382 | |||
1383 | printk("Backing file mismatch - \"%s\" requested,\n" | ||
1384 | "\"%s\" specified in COW header of \"%s\"\n", | ||
1385 | from_cmdline, from_cow, cow); | ||
1386 | return(0); | ||
1387 | } | ||
1388 | |||
1389 | static int backing_file_mismatch(char *file, __u64 size, time_t mtime) | ||
1390 | { | ||
1391 | unsigned long modtime; | ||
1392 | long long actual; | ||
1393 | int err; | ||
1394 | |||
1395 | err = os_file_modtime(file, &modtime); | ||
1396 | if(err < 0){ | ||
1397 | printk("Failed to get modification time of backing file " | ||
1398 | "\"%s\", err = %d\n", file, -err); | ||
1399 | return(err); | ||
1400 | } | ||
1401 | |||
1402 | err = os_file_size(file, &actual); | ||
1403 | if(err < 0){ | ||
1404 | printk("Failed to get size of backing file \"%s\", " | ||
1405 | "err = %d\n", file, -err); | ||
1406 | return(err); | ||
1407 | } | ||
1408 | |||
1409 | if(actual != size){ | ||
1410 | /*__u64 can be a long on AMD64 and with %lu GCC complains; so | ||
1411 | * the typecast.*/ | ||
1412 | printk("Size mismatch (%llu vs %llu) of COW header vs backing " | ||
1413 | "file\n", (unsigned long long) size, actual); | ||
1414 | return(-EINVAL); | ||
1415 | } | ||
1416 | if(modtime != mtime){ | ||
1417 | printk("mtime mismatch (%ld vs %ld) of COW header vs backing " | ||
1418 | "file\n", mtime, modtime); | ||
1419 | return(-EINVAL); | ||
1420 | } | ||
1421 | return(0); | ||
1422 | } | ||
1423 | |||
1424 | int read_cow_bitmap(int fd, void *buf, int offset, int len) | ||
1425 | { | ||
1426 | int err; | ||
1427 | |||
1428 | err = os_seek_file(fd, offset); | ||
1429 | if(err < 0) | ||
1430 | return(err); | ||
1431 | |||
1432 | err = os_read_file(fd, buf, len); | ||
1433 | if(err < 0) | ||
1434 | return(err); | ||
1435 | |||
1436 | return(0); | ||
1437 | } | ||
1438 | |||
1439 | int open_ubd_file(char *file, struct openflags *openflags, | ||
1440 | char **backing_file_out, int *bitmap_offset_out, | ||
1441 | unsigned long *bitmap_len_out, int *data_offset_out, | ||
1442 | int *create_cow_out) | ||
1443 | { | ||
1444 | time_t mtime; | ||
1445 | unsigned long long size; | ||
1446 | __u32 version, align; | ||
1447 | char *backing_file; | ||
1448 | int fd, err, sectorsize, same, mode = 0644; | ||
1449 | |||
1450 | fd = os_open_file(file, *openflags, mode); | ||
1451 | if(fd < 0){ | ||
1452 | if((fd == -ENOENT) && (create_cow_out != NULL)) | ||
1453 | *create_cow_out = 1; | ||
1454 | if(!openflags->w || | ||
1455 | ((fd != -EROFS) && (fd != -EACCES))) return(fd); | ||
1456 | openflags->w = 0; | ||
1457 | fd = os_open_file(file, *openflags, mode); | ||
1458 | if(fd < 0) | ||
1459 | return(fd); | ||
1460 | } | ||
1461 | |||
1462 | err = os_lock_file(fd, openflags->w); | ||
1463 | if(err < 0){ | ||
1464 | printk("Failed to lock '%s', err = %d\n", file, -err); | ||
1465 | goto out_close; | ||
1466 | } | ||
1467 | |||
1468 | if(backing_file_out == NULL) return(fd); | ||
1469 | |||
1470 | err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime, | ||
1471 | &size, §orsize, &align, bitmap_offset_out); | ||
1472 | if(err && (*backing_file_out != NULL)){ | ||
1473 | printk("Failed to read COW header from COW file \"%s\", " | ||
1474 | "errno = %d\n", file, -err); | ||
1475 | goto out_close; | ||
1476 | } | ||
1477 | if(err) return(fd); | ||
1478 | |||
1479 | if(backing_file_out == NULL) return(fd); | ||
1480 | |||
1481 | same = same_backing_files(*backing_file_out, backing_file, file); | ||
1482 | |||
1483 | if(!same && !backing_file_mismatch(*backing_file_out, size, mtime)){ | ||
1484 | printk("Switching backing file to '%s'\n", *backing_file_out); | ||
1485 | err = write_cow_header(file, fd, *backing_file_out, | ||
1486 | sectorsize, align, &size); | ||
1487 | if(err){ | ||
1488 | printk("Switch failed, errno = %d\n", -err); | ||
1489 | return(err); | ||
1490 | } | ||
1491 | } | ||
1492 | else { | ||
1493 | *backing_file_out = backing_file; | ||
1494 | err = backing_file_mismatch(*backing_file_out, size, mtime); | ||
1495 | if(err) goto out_close; | ||
1496 | } | ||
1497 | |||
1498 | cow_sizes(version, size, sectorsize, align, *bitmap_offset_out, | ||
1499 | bitmap_len_out, data_offset_out); | ||
1500 | |||
1501 | return(fd); | ||
1502 | out_close: | ||
1503 | os_close_file(fd); | ||
1504 | return(err); | ||
1505 | } | ||
1506 | |||
1507 | int create_cow_file(char *cow_file, char *backing_file, struct openflags flags, | ||
1508 | int sectorsize, int alignment, int *bitmap_offset_out, | ||
1509 | unsigned long *bitmap_len_out, int *data_offset_out) | ||
1510 | { | ||
1511 | int err, fd; | ||
1512 | |||
1513 | flags.c = 1; | ||
1514 | fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL, NULL); | ||
1515 | if(fd < 0){ | ||
1516 | err = fd; | ||
1517 | printk("Open of COW file '%s' failed, errno = %d\n", cow_file, | ||
1518 | -err); | ||
1519 | goto out; | ||
1520 | } | ||
1521 | |||
1522 | err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment, | ||
1523 | bitmap_offset_out, bitmap_len_out, | ||
1524 | data_offset_out); | ||
1525 | if(!err) | ||
1526 | return(fd); | ||
1527 | os_close_file(fd); | ||
1528 | out: | ||
1529 | return(err); | ||
1530 | } | ||
1531 | |||
1532 | static int update_bitmap(struct io_thread_req *req) | ||
1533 | { | ||
1534 | int n; | ||
1535 | |||
1536 | if(req->cow_offset == -1) | ||
1537 | return(0); | ||
1538 | |||
1539 | n = os_seek_file(req->fds[1], req->cow_offset); | ||
1540 | if(n < 0){ | ||
1541 | printk("do_io - bitmap lseek failed : err = %d\n", -n); | ||
1542 | return(1); | ||
1543 | } | ||
1544 | |||
1545 | n = os_write_file(req->fds[1], &req->bitmap_words, | ||
1546 | sizeof(req->bitmap_words)); | ||
1547 | if(n != sizeof(req->bitmap_words)){ | ||
1548 | printk("do_io - bitmap update failed, err = %d fd = %d\n", -n, | ||
1549 | req->fds[1]); | ||
1550 | return(1); | ||
1551 | } | ||
1552 | |||
1553 | return(0); | ||
1554 | } | ||
1555 | |||
1556 | void do_io(struct io_thread_req *req) | ||
1557 | { | ||
1558 | char *buf; | ||
1559 | unsigned long len; | ||
1560 | int n, nsectors, start, end, bit; | ||
1561 | int err; | ||
1562 | __u64 off; | ||
1563 | |||
1564 | if(req->op == UBD_MMAP){ | ||
1565 | /* Touch the page to force the host to do any necessary IO to | ||
1566 | * get it into memory | ||
1567 | */ | ||
1568 | n = *((volatile int *) req->buffer); | ||
1569 | req->error = update_bitmap(req); | ||
1570 | return; | ||
1571 | } | ||
1572 | |||
1573 | nsectors = req->length / req->sectorsize; | ||
1574 | start = 0; | ||
1575 | do { | ||
1576 | bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask); | ||
1577 | end = start; | ||
1578 | while((end < nsectors) && | ||
1579 | (ubd_test_bit(end, (unsigned char *) | ||
1580 | &req->sector_mask) == bit)) | ||
1581 | end++; | ||
1582 | |||
1583 | off = req->offset + req->offsets[bit] + | ||
1584 | start * req->sectorsize; | ||
1585 | len = (end - start) * req->sectorsize; | ||
1586 | buf = &req->buffer[start * req->sectorsize]; | ||
1587 | |||
1588 | err = os_seek_file(req->fds[bit], off); | ||
1589 | if(err < 0){ | ||
1590 | printk("do_io - lseek failed : err = %d\n", -err); | ||
1591 | req->error = 1; | ||
1592 | return; | ||
1593 | } | ||
1594 | if(req->op == UBD_READ){ | ||
1595 | n = 0; | ||
1596 | do { | ||
1597 | buf = &buf[n]; | ||
1598 | len -= n; | ||
1599 | n = os_read_file(req->fds[bit], buf, len); | ||
1600 | if (n < 0) { | ||
1601 | printk("do_io - read failed, err = %d " | ||
1602 | "fd = %d\n", -n, req->fds[bit]); | ||
1603 | req->error = 1; | ||
1604 | return; | ||
1605 | } | ||
1606 | } while((n < len) && (n != 0)); | ||
1607 | if (n < len) memset(&buf[n], 0, len - n); | ||
1608 | } | ||
1609 | else { | ||
1610 | n = os_write_file(req->fds[bit], buf, len); | ||
1611 | if(n != len){ | ||
1612 | printk("do_io - write failed err = %d " | ||
1613 | "fd = %d\n", -n, req->fds[bit]); | ||
1614 | req->error = 1; | ||
1615 | return; | ||
1616 | } | ||
1617 | } | ||
1618 | |||
1619 | start = end; | ||
1620 | } while(start < nsectors); | ||
1621 | |||
1622 | req->error = update_bitmap(req); | ||
1623 | } | ||
1624 | |||
1625 | /* Changed in start_io_thread, which is serialized by being called only | ||
1626 | * from ubd_init, which is an initcall. | ||
1627 | */ | ||
1628 | int kernel_fd = -1; | ||
1629 | |||
1630 | /* Only changed by the io thread */ | ||
1631 | int io_count = 0; | ||
1632 | |||
1633 | int io_thread(void *arg) | ||
1634 | { | ||
1635 | struct io_thread_req req; | ||
1636 | int n; | ||
1637 | |||
1638 | ignore_sigwinch_sig(); | ||
1639 | while(1){ | ||
1640 | n = os_read_file(kernel_fd, &req, sizeof(req)); | ||
1641 | if(n != sizeof(req)){ | ||
1642 | if(n < 0) | ||
1643 | printk("io_thread - read failed, fd = %d, " | ||
1644 | "err = %d\n", kernel_fd, -n); | ||
1645 | else { | ||
1646 | printk("io_thread - short read, fd = %d, " | ||
1647 | "length = %d\n", kernel_fd, n); | ||
1648 | } | ||
1649 | continue; | ||
1650 | } | ||
1651 | io_count++; | ||
1652 | do_io(&req); | ||
1653 | n = os_write_file(kernel_fd, &req, sizeof(req)); | ||
1654 | if(n != sizeof(req)) | ||
1655 | printk("io_thread - write failed, fd = %d, err = %d\n", | ||
1656 | kernel_fd, -n); | ||
1657 | } | ||
1658 | } | ||
1659 | |||
1660 | /* | ||
1661 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
1662 | * Emacs will notice this stuff at the end of the file and automatically | ||
1663 | * adjust the settings for this buffer only. This must remain at the end | ||
1664 | * of the file. | ||
1665 | * --------------------------------------------------------------------------- | ||
1666 | * Local variables: | ||
1667 | * c-file-style: "linux" | ||
1668 | * End: | ||
1669 | */ | ||
diff --git a/arch/um/drivers/ubd_user.c b/arch/um/drivers/ubd_user.c new file mode 100644 index 000000000000..b94d2bc4fe06 --- /dev/null +++ b/arch/um/drivers/ubd_user.c | |||
@@ -0,0 +1,75 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com) | ||
4 | * Licensed under the GPL | ||
5 | */ | ||
6 | |||
7 | #include <stddef.h> | ||
8 | #include <unistd.h> | ||
9 | #include <errno.h> | ||
10 | #include <sched.h> | ||
11 | #include <signal.h> | ||
12 | #include <string.h> | ||
13 | #include <netinet/in.h> | ||
14 | #include <sys/time.h> | ||
15 | #include <sys/socket.h> | ||
16 | #include <sys/mman.h> | ||
17 | #include <sys/param.h> | ||
18 | #include "asm/types.h" | ||
19 | #include "user_util.h" | ||
20 | #include "kern_util.h" | ||
21 | #include "user.h" | ||
22 | #include "ubd_user.h" | ||
23 | #include "os.h" | ||
24 | #include "cow.h" | ||
25 | |||
26 | #include <endian.h> | ||
27 | #include <byteswap.h> | ||
28 | |||
29 | void ignore_sigwinch_sig(void) | ||
30 | { | ||
31 | signal(SIGWINCH, SIG_IGN); | ||
32 | } | ||
33 | |||
34 | int start_io_thread(unsigned long sp, int *fd_out) | ||
35 | { | ||
36 | int pid, fds[2], err; | ||
37 | |||
38 | err = os_pipe(fds, 1, 1); | ||
39 | if(err < 0){ | ||
40 | printk("start_io_thread - os_pipe failed, err = %d\n", -err); | ||
41 | goto out; | ||
42 | } | ||
43 | |||
44 | kernel_fd = fds[0]; | ||
45 | *fd_out = fds[1]; | ||
46 | |||
47 | pid = clone(io_thread, (void *) sp, CLONE_FILES | CLONE_VM | SIGCHLD, | ||
48 | NULL); | ||
49 | if(pid < 0){ | ||
50 | printk("start_io_thread - clone failed : errno = %d\n", errno); | ||
51 | err = -errno; | ||
52 | goto out_close; | ||
53 | } | ||
54 | |||
55 | return(pid); | ||
56 | |||
57 | out_close: | ||
58 | os_close_file(fds[0]); | ||
59 | os_close_file(fds[1]); | ||
60 | kernel_fd = -1; | ||
61 | *fd_out = -1; | ||
62 | out: | ||
63 | return(err); | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
68 | * Emacs will notice this stuff at the end of the file and automatically | ||
69 | * adjust the settings for this buffer only. This must remain at the end | ||
70 | * of the file. | ||
71 | * --------------------------------------------------------------------------- | ||
72 | * Local variables: | ||
73 | * c-file-style: "linux" | ||
74 | * End: | ||
75 | */ | ||
diff --git a/arch/um/drivers/xterm.c b/arch/um/drivers/xterm.c new file mode 100644 index 000000000000..93dc1911363f --- /dev/null +++ b/arch/um/drivers/xterm.c | |||
@@ -0,0 +1,225 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <unistd.h> | ||
9 | #include <string.h> | ||
10 | #include <errno.h> | ||
11 | #include <termios.h> | ||
12 | #include <signal.h> | ||
13 | #include <sched.h> | ||
14 | #include <sys/socket.h> | ||
15 | #include "kern_util.h" | ||
16 | #include "chan_user.h" | ||
17 | #include "helper.h" | ||
18 | #include "user_util.h" | ||
19 | #include "user.h" | ||
20 | #include "os.h" | ||
21 | #include "xterm.h" | ||
22 | |||
23 | struct xterm_chan { | ||
24 | int pid; | ||
25 | int helper_pid; | ||
26 | char *title; | ||
27 | int device; | ||
28 | int raw; | ||
29 | struct termios tt; | ||
30 | unsigned long stack; | ||
31 | int direct_rcv; | ||
32 | }; | ||
33 | |||
34 | /* Not static because it's called directly by the tt mode gdb code */ | ||
35 | void *xterm_init(char *str, int device, struct chan_opts *opts) | ||
36 | { | ||
37 | struct xterm_chan *data; | ||
38 | |||
39 | data = malloc(sizeof(*data)); | ||
40 | if(data == NULL) return(NULL); | ||
41 | *data = ((struct xterm_chan) { .pid = -1, | ||
42 | .helper_pid = -1, | ||
43 | .device = device, | ||
44 | .title = opts->xterm_title, | ||
45 | .raw = opts->raw, | ||
46 | .stack = opts->tramp_stack, | ||
47 | .direct_rcv = !opts->in_kernel } ); | ||
48 | return(data); | ||
49 | } | ||
50 | |||
51 | /* Only changed by xterm_setup, which is a setup */ | ||
52 | static char *terminal_emulator = "xterm"; | ||
53 | static char *title_switch = "-T"; | ||
54 | static char *exec_switch = "-e"; | ||
55 | |||
56 | static int __init xterm_setup(char *line, int *add) | ||
57 | { | ||
58 | *add = 0; | ||
59 | terminal_emulator = line; | ||
60 | |||
61 | line = strchr(line, ','); | ||
62 | if(line == NULL) return(0); | ||
63 | *line++ = '\0'; | ||
64 | if(*line) title_switch = line; | ||
65 | |||
66 | line = strchr(line, ','); | ||
67 | if(line == NULL) return(0); | ||
68 | *line++ = '\0'; | ||
69 | if(*line) exec_switch = line; | ||
70 | |||
71 | return(0); | ||
72 | } | ||
73 | |||
74 | __uml_setup("xterm=", xterm_setup, | ||
75 | "xterm=<terminal emulator>,<title switch>,<exec switch>\n" | ||
76 | " Specifies an alternate terminal emulator to use for the debugger,\n" | ||
77 | " consoles, and serial lines when they are attached to the xterm channel.\n" | ||
78 | " The values are the terminal emulator binary, the switch it uses to set\n" | ||
79 | " its title, and the switch it uses to execute a subprocess,\n" | ||
80 | " respectively. The title switch must have the form '<switch> title',\n" | ||
81 | " not '<switch>=title'. Similarly, the exec switch must have the form\n" | ||
82 | " '<switch> command arg1 arg2 ...'.\n" | ||
83 | " The default values are 'xterm=xterm,-T,-e'. Values for gnome-terminal\n" | ||
84 | " are 'xterm=gnome-terminal,-t,-x'.\n\n" | ||
85 | ); | ||
86 | |||
87 | /* XXX This badly needs some cleaning up in the error paths | ||
88 | * Not static because it's called directly by the tt mode gdb code | ||
89 | */ | ||
90 | int xterm_open(int input, int output, int primary, void *d, | ||
91 | char **dev_out) | ||
92 | { | ||
93 | struct xterm_chan *data = d; | ||
94 | unsigned long stack; | ||
95 | int pid, fd, new, err; | ||
96 | char title[256], file[] = "/tmp/xterm-pipeXXXXXX"; | ||
97 | char *argv[] = { terminal_emulator, title_switch, title, exec_switch, | ||
98 | "/usr/lib/uml/port-helper", "-uml-socket", | ||
99 | file, NULL }; | ||
100 | |||
101 | if(os_access(argv[4], OS_ACC_X_OK) < 0) | ||
102 | argv[4] = "port-helper"; | ||
103 | |||
104 | /* Check that DISPLAY is set, this doesn't guarantee the xterm | ||
105 | * will work but w/o it we can be pretty sure it won't. */ | ||
106 | if (!getenv("DISPLAY")) { | ||
107 | printk("xterm_open: $DISPLAY not set.\n"); | ||
108 | return -ENODEV; | ||
109 | } | ||
110 | |||
111 | fd = mkstemp(file); | ||
112 | if(fd < 0){ | ||
113 | printk("xterm_open : mkstemp failed, errno = %d\n", errno); | ||
114 | return(-errno); | ||
115 | } | ||
116 | |||
117 | if(unlink(file)){ | ||
118 | printk("xterm_open : unlink failed, errno = %d\n", errno); | ||
119 | return(-errno); | ||
120 | } | ||
121 | os_close_file(fd); | ||
122 | |||
123 | fd = os_create_unix_socket(file, sizeof(file), 1); | ||
124 | if(fd < 0){ | ||
125 | printk("xterm_open : create_unix_socket failed, errno = %d\n", | ||
126 | -fd); | ||
127 | return(fd); | ||
128 | } | ||
129 | |||
130 | sprintf(title, data->title, data->device); | ||
131 | stack = data->stack; | ||
132 | pid = run_helper(NULL, NULL, argv, &stack); | ||
133 | if(pid < 0){ | ||
134 | printk("xterm_open : run_helper failed, errno = %d\n", -pid); | ||
135 | return(pid); | ||
136 | } | ||
137 | |||
138 | if(data->stack == 0) free_stack(stack, 0); | ||
139 | |||
140 | if (data->direct_rcv) { | ||
141 | new = os_rcv_fd(fd, &data->helper_pid); | ||
142 | } else { | ||
143 | err = os_set_fd_block(fd, 0); | ||
144 | if(err < 0){ | ||
145 | printk("xterm_open : failed to set descriptor " | ||
146 | "non-blocking, err = %d\n", -err); | ||
147 | return(err); | ||
148 | } | ||
149 | new = xterm_fd(fd, &data->helper_pid); | ||
150 | } | ||
151 | if(new < 0){ | ||
152 | printk("xterm_open : os_rcv_fd failed, err = %d\n", -new); | ||
153 | goto out; | ||
154 | } | ||
155 | |||
156 | CATCH_EINTR(err = tcgetattr(new, &data->tt)); | ||
157 | if(err){ | ||
158 | new = err; | ||
159 | goto out; | ||
160 | } | ||
161 | |||
162 | if(data->raw){ | ||
163 | err = raw(new); | ||
164 | if(err){ | ||
165 | new = err; | ||
166 | goto out; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | data->pid = pid; | ||
171 | *dev_out = NULL; | ||
172 | out: | ||
173 | unlink(file); | ||
174 | return(new); | ||
175 | } | ||
176 | |||
177 | /* Not static because it's called directly by the tt mode gdb code */ | ||
178 | void xterm_close(int fd, void *d) | ||
179 | { | ||
180 | struct xterm_chan *data = d; | ||
181 | |||
182 | if(data->pid != -1) | ||
183 | os_kill_process(data->pid, 1); | ||
184 | data->pid = -1; | ||
185 | if(data->helper_pid != -1) | ||
186 | os_kill_process(data->helper_pid, 0); | ||
187 | data->helper_pid = -1; | ||
188 | os_close_file(fd); | ||
189 | } | ||
190 | |||
191 | static void xterm_free(void *d) | ||
192 | { | ||
193 | free(d); | ||
194 | } | ||
195 | |||
196 | static int xterm_console_write(int fd, const char *buf, int n, void *d) | ||
197 | { | ||
198 | struct xterm_chan *data = d; | ||
199 | |||
200 | return(generic_console_write(fd, buf, n, &data->tt)); | ||
201 | } | ||
202 | |||
203 | struct chan_ops xterm_ops = { | ||
204 | .type = "xterm", | ||
205 | .init = xterm_init, | ||
206 | .open = xterm_open, | ||
207 | .close = xterm_close, | ||
208 | .read = generic_read, | ||
209 | .write = generic_write, | ||
210 | .console_write = xterm_console_write, | ||
211 | .window_size = generic_window_size, | ||
212 | .free = xterm_free, | ||
213 | .winch = 1, | ||
214 | }; | ||
215 | |||
216 | /* | ||
217 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
218 | * Emacs will notice this stuff at the end of the file and automatically | ||
219 | * adjust the settings for this buffer only. This must remain at the end | ||
220 | * of the file. | ||
221 | * --------------------------------------------------------------------------- | ||
222 | * Local variables: | ||
223 | * c-file-style: "linux" | ||
224 | * End: | ||
225 | */ | ||
diff --git a/arch/um/drivers/xterm.h b/arch/um/drivers/xterm.h new file mode 100644 index 000000000000..f33a6e77b186 --- /dev/null +++ b/arch/um/drivers/xterm.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #ifndef __XTERM_H__ | ||
7 | #define __XTERM_H__ | ||
8 | |||
9 | extern int xterm_fd(int socket, int *pid_out); | ||
10 | |||
11 | #endif | ||
12 | |||
13 | /* | ||
14 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
15 | * Emacs will notice this stuff at the end of the file and automatically | ||
16 | * adjust the settings for this buffer only. This must remain at the end | ||
17 | * of the file. | ||
18 | * --------------------------------------------------------------------------- | ||
19 | * Local variables: | ||
20 | * c-file-style: "linux" | ||
21 | * End: | ||
22 | */ | ||
diff --git a/arch/um/drivers/xterm_kern.c b/arch/um/drivers/xterm_kern.c new file mode 100644 index 000000000000..7917b9d1cec8 --- /dev/null +++ b/arch/um/drivers/xterm_kern.c | |||
@@ -0,0 +1,93 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include "linux/errno.h" | ||
7 | #include "linux/slab.h" | ||
8 | #include "linux/signal.h" | ||
9 | #include "linux/interrupt.h" | ||
10 | #include "asm/semaphore.h" | ||
11 | #include "asm/irq.h" | ||
12 | #include "irq_user.h" | ||
13 | #include "irq_kern.h" | ||
14 | #include "kern_util.h" | ||
15 | #include "os.h" | ||
16 | #include "xterm.h" | ||
17 | |||
18 | struct xterm_wait { | ||
19 | struct completion ready; | ||
20 | int fd; | ||
21 | int pid; | ||
22 | int new_fd; | ||
23 | }; | ||
24 | |||
25 | static irqreturn_t xterm_interrupt(int irq, void *data, struct pt_regs *regs) | ||
26 | { | ||
27 | struct xterm_wait *xterm = data; | ||
28 | int fd; | ||
29 | |||
30 | fd = os_rcv_fd(xterm->fd, &xterm->pid); | ||
31 | if(fd == -EAGAIN) | ||
32 | return(IRQ_NONE); | ||
33 | |||
34 | xterm->new_fd = fd; | ||
35 | complete(&xterm->ready); | ||
36 | return(IRQ_HANDLED); | ||
37 | } | ||
38 | |||
39 | int xterm_fd(int socket, int *pid_out) | ||
40 | { | ||
41 | struct xterm_wait *data; | ||
42 | int err, ret; | ||
43 | |||
44 | data = kmalloc(sizeof(*data), GFP_KERNEL); | ||
45 | if(data == NULL){ | ||
46 | printk(KERN_ERR "xterm_fd : failed to allocate xterm_wait\n"); | ||
47 | return(-ENOMEM); | ||
48 | } | ||
49 | |||
50 | /* This is a locked semaphore... */ | ||
51 | *data = ((struct xterm_wait) | ||
52 | { .fd = socket, | ||
53 | .pid = -1, | ||
54 | .new_fd = -1 }); | ||
55 | init_completion(&data->ready); | ||
56 | |||
57 | err = um_request_irq(XTERM_IRQ, socket, IRQ_READ, xterm_interrupt, | ||
58 | SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, | ||
59 | "xterm", data); | ||
60 | if (err){ | ||
61 | printk(KERN_ERR "xterm_fd : failed to get IRQ for xterm, " | ||
62 | "err = %d\n", err); | ||
63 | ret = err; | ||
64 | goto out; | ||
65 | } | ||
66 | |||
67 | /* ... so here we wait for an xterm interrupt. | ||
68 | * | ||
69 | * XXX Note, if the xterm doesn't work for some reason (eg. DISPLAY | ||
70 | * isn't set) this will hang... */ | ||
71 | wait_for_completion(&data->ready); | ||
72 | |||
73 | free_irq_by_irq_and_dev(XTERM_IRQ, data); | ||
74 | free_irq(XTERM_IRQ, data); | ||
75 | |||
76 | ret = data->new_fd; | ||
77 | *pid_out = data->pid; | ||
78 | out: | ||
79 | kfree(data); | ||
80 | |||
81 | return(ret); | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
86 | * Emacs will notice this stuff at the end of the file and automatically | ||
87 | * adjust the settings for this buffer only. This must remain at the end | ||
88 | * of the file. | ||
89 | * --------------------------------------------------------------------------- | ||
90 | * Local variables: | ||
91 | * c-file-style: "linux" | ||
92 | * End: | ||
93 | */ | ||