mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-09 01:28:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
67
arch/um/drivers/Makefile
Normal file
67
arch/um/drivers/Makefile
Normal file
|
@ -0,0 +1,67 @@
|
|||
#
|
||||
# Copyright (C) 2000, 2002, 2003 Jeff Dike (jdike@karaya.com)
|
||||
# Licensed under the GPL
|
||||
#
|
||||
|
||||
# pcap is broken in 2.5 because kbuild doesn't allow pcap.a to be linked
|
||||
# in to pcap.o
|
||||
|
||||
slip-objs := slip_kern.o slip_user.o
|
||||
slirp-objs := slirp_kern.o slirp_user.o
|
||||
daemon-objs := daemon_kern.o daemon_user.o
|
||||
umcast-objs := umcast_kern.o umcast_user.o
|
||||
net-objs := net_kern.o net_user.o
|
||||
mconsole-objs := mconsole_kern.o mconsole_user.o
|
||||
hostaudio-objs := hostaudio_kern.o
|
||||
ubd-objs := ubd_kern.o ubd_user.o
|
||||
port-objs := port_kern.o port_user.o
|
||||
harddog-objs := harddog_kern.o harddog_user.o
|
||||
|
||||
LDFLAGS_pcap.o := -r $(shell $(CC) $(KBUILD_CFLAGS) -print-file-name=libpcap.a)
|
||||
|
||||
LDFLAGS_vde.o := -r $(shell $(CC) $(CFLAGS) -print-file-name=libvdeplug.a)
|
||||
|
||||
targets := pcap_kern.o pcap_user.o vde_kern.o vde_user.o
|
||||
|
||||
$(obj)/pcap.o: $(obj)/pcap_kern.o $(obj)/pcap_user.o
|
||||
$(LD) -r -dp -o $@ $^ $(LDFLAGS) $(LDFLAGS_pcap.o)
|
||||
|
||||
$(obj)/vde.o: $(obj)/vde_kern.o $(obj)/vde_user.o
|
||||
$(LD) -r -dp -o $@ $^ $(LDFLAGS) $(LDFLAGS_vde.o)
|
||||
|
||||
#XXX: The call below does not work because the flags are added before the
|
||||
# object name, so nothing from the library gets linked.
|
||||
#$(call if_changed,ld)
|
||||
|
||||
# When the above is fixed, don't forget to add this too!
|
||||
#targets += $(obj)/pcap.o
|
||||
|
||||
obj-y := stdio_console.o fd.o chan_kern.o chan_user.o line.o
|
||||
obj-$(CONFIG_SSL) += ssl.o
|
||||
obj-$(CONFIG_STDERR_CONSOLE) += stderr_console.o
|
||||
|
||||
obj-$(CONFIG_UML_NET_SLIP) += slip.o slip_common.o
|
||||
obj-$(CONFIG_UML_NET_SLIRP) += slirp.o slip_common.o
|
||||
obj-$(CONFIG_UML_NET_DAEMON) += daemon.o
|
||||
obj-$(CONFIG_UML_NET_VDE) += vde.o
|
||||
obj-$(CONFIG_UML_NET_MCAST) += umcast.o
|
||||
obj-$(CONFIG_UML_NET_PCAP) += pcap.o
|
||||
obj-$(CONFIG_UML_NET) += net.o
|
||||
obj-$(CONFIG_MCONSOLE) += mconsole.o
|
||||
obj-$(CONFIG_MMAPPER) += mmapper_kern.o
|
||||
obj-$(CONFIG_BLK_DEV_UBD) += ubd.o
|
||||
obj-$(CONFIG_HOSTAUDIO) += hostaudio.o
|
||||
obj-$(CONFIG_NULL_CHAN) += null.o
|
||||
obj-$(CONFIG_PORT_CHAN) += port.o
|
||||
obj-$(CONFIG_PTY_CHAN) += pty.o
|
||||
obj-$(CONFIG_TTY_CHAN) += tty.o
|
||||
obj-$(CONFIG_XTERM_CHAN) += xterm.o xterm_kern.o
|
||||
obj-$(CONFIG_UML_WATCHDOG) += harddog.o
|
||||
obj-$(CONFIG_BLK_DEV_COW_COMMON) += cow_user.o
|
||||
obj-$(CONFIG_UML_RANDOM) += random.o
|
||||
|
||||
# pcap_user.o must be added explicitly.
|
||||
USER_OBJS := fd.o null.o pty.o tty.o xterm.o slip_common.o pcap_user.o vde_user.o
|
||||
CFLAGS_null.o = -DDEV_NULL=$(DEV_NULL_PATH)
|
||||
|
||||
include arch/um/scripts/Makefile.rules
|
49
arch/um/drivers/chan.h
Normal file
49
arch/um/drivers/chan.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __CHAN_KERN_H__
|
||||
#define __CHAN_KERN_H__
|
||||
|
||||
#include <linux/tty.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/console.h>
|
||||
#include "chan_user.h"
|
||||
#include "line.h"
|
||||
|
||||
struct chan {
|
||||
struct list_head list;
|
||||
struct list_head free_list;
|
||||
struct line *line;
|
||||
char *dev;
|
||||
unsigned int primary:1;
|
||||
unsigned int input:1;
|
||||
unsigned int output:1;
|
||||
unsigned int opened:1;
|
||||
unsigned int enabled:1;
|
||||
int fd;
|
||||
const struct chan_ops *ops;
|
||||
void *data;
|
||||
};
|
||||
|
||||
extern void chan_interrupt(struct line *line, int irq);
|
||||
extern int parse_chan_pair(char *str, struct line *line, int device,
|
||||
const struct chan_opts *opts, char **error_out);
|
||||
extern int write_chan(struct chan *chan, const char *buf, int len,
|
||||
int write_irq);
|
||||
extern int console_write_chan(struct chan *chan, const char *buf,
|
||||
int len);
|
||||
extern int console_open_chan(struct line *line, struct console *co);
|
||||
extern void deactivate_chan(struct chan *chan, int irq);
|
||||
extern void reactivate_chan(struct chan *chan, int irq);
|
||||
extern void chan_enable_winch(struct chan *chan, struct tty_port *port);
|
||||
extern int enable_chan(struct line *line);
|
||||
extern void close_chan(struct line *line);
|
||||
extern int chan_window_size(struct line *line,
|
||||
unsigned short *rows_out,
|
||||
unsigned short *cols_out);
|
||||
extern int chan_config_string(struct line *line, char *str, int size,
|
||||
char **error_out);
|
||||
|
||||
#endif
|
581
arch/um/drivers/chan_kern.c
Normal file
581
arch/um/drivers/chan_kern.c
Normal file
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include "chan.h"
|
||||
#include <os.h>
|
||||
#include <irq_kern.h>
|
||||
|
||||
#ifdef CONFIG_NOCONFIG_CHAN
|
||||
static void *not_configged_init(char *str, int device,
|
||||
const struct chan_opts *opts)
|
||||
{
|
||||
printk(KERN_ERR "Using a channel type which is configured out of "
|
||||
"UML\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int not_configged_open(int input, int output, int primary, void *data,
|
||||
char **dev_out)
|
||||
{
|
||||
printk(KERN_ERR "Using a channel type which is configured out of "
|
||||
"UML\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void not_configged_close(int fd, void *data)
|
||||
{
|
||||
printk(KERN_ERR "Using a channel type which is configured out of "
|
||||
"UML\n");
|
||||
}
|
||||
|
||||
static int not_configged_read(int fd, char *c_out, void *data)
|
||||
{
|
||||
printk(KERN_ERR "Using a channel type which is configured out of "
|
||||
"UML\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int not_configged_write(int fd, const char *buf, int len, void *data)
|
||||
{
|
||||
printk(KERN_ERR "Using a channel type which is configured out of "
|
||||
"UML\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int not_configged_console_write(int fd, const char *buf, int len)
|
||||
{
|
||||
printk(KERN_ERR "Using a channel type which is configured out of "
|
||||
"UML\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int not_configged_window_size(int fd, void *data, unsigned short *rows,
|
||||
unsigned short *cols)
|
||||
{
|
||||
printk(KERN_ERR "Using a channel type which is configured out of "
|
||||
"UML\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void not_configged_free(void *data)
|
||||
{
|
||||
printk(KERN_ERR "Using a channel type which is configured out of "
|
||||
"UML\n");
|
||||
}
|
||||
|
||||
static const struct chan_ops not_configged_ops = {
|
||||
.init = not_configged_init,
|
||||
.open = not_configged_open,
|
||||
.close = not_configged_close,
|
||||
.read = not_configged_read,
|
||||
.write = not_configged_write,
|
||||
.console_write = not_configged_console_write,
|
||||
.window_size = not_configged_window_size,
|
||||
.free = not_configged_free,
|
||||
.winch = 0,
|
||||
};
|
||||
#endif /* CONFIG_NOCONFIG_CHAN */
|
||||
|
||||
static int open_one_chan(struct chan *chan)
|
||||
{
|
||||
int fd, err;
|
||||
|
||||
if (chan->opened)
|
||||
return 0;
|
||||
|
||||
if (chan->ops->open == NULL)
|
||||
fd = 0;
|
||||
else fd = (*chan->ops->open)(chan->input, chan->output, chan->primary,
|
||||
chan->data, &chan->dev);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
err = os_set_fd_block(fd, 0);
|
||||
if (err) {
|
||||
(*chan->ops->close)(fd, chan->data);
|
||||
return err;
|
||||
}
|
||||
|
||||
chan->fd = fd;
|
||||
|
||||
chan->opened = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int open_chan(struct list_head *chans)
|
||||
{
|
||||
struct list_head *ele;
|
||||
struct chan *chan;
|
||||
int ret, err = 0;
|
||||
|
||||
list_for_each(ele, chans) {
|
||||
chan = list_entry(ele, struct chan, list);
|
||||
ret = open_one_chan(chan);
|
||||
if (chan->primary)
|
||||
err = ret;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void chan_enable_winch(struct chan *chan, struct tty_port *port)
|
||||
{
|
||||
if (chan && chan->primary && chan->ops->winch)
|
||||
register_winch(chan->fd, port);
|
||||
}
|
||||
|
||||
static void line_timer_cb(struct work_struct *work)
|
||||
{
|
||||
struct line *line = container_of(work, struct line, task.work);
|
||||
|
||||
if (!line->throttled)
|
||||
chan_interrupt(line, line->driver->read_irq);
|
||||
}
|
||||
|
||||
int enable_chan(struct line *line)
|
||||
{
|
||||
struct list_head *ele;
|
||||
struct chan *chan;
|
||||
int err;
|
||||
|
||||
INIT_DELAYED_WORK(&line->task, line_timer_cb);
|
||||
|
||||
list_for_each(ele, &line->chan_list) {
|
||||
chan = list_entry(ele, struct chan, list);
|
||||
err = open_one_chan(chan);
|
||||
if (err) {
|
||||
if (chan->primary)
|
||||
goto out_close;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chan->enabled)
|
||||
continue;
|
||||
err = line_setup_irq(chan->fd, chan->input, chan->output, line,
|
||||
chan);
|
||||
if (err)
|
||||
goto out_close;
|
||||
|
||||
chan->enabled = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_close:
|
||||
close_chan(line);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Items are added in IRQ context, when free_irq can't be called, and
|
||||
* removed in process context, when it can.
|
||||
* This handles interrupt sources which disappear, and which need to
|
||||
* be permanently disabled. This is discovered in IRQ context, but
|
||||
* the freeing of the IRQ must be done later.
|
||||
*/
|
||||
static DEFINE_SPINLOCK(irqs_to_free_lock);
|
||||
static LIST_HEAD(irqs_to_free);
|
||||
|
||||
void free_irqs(void)
|
||||
{
|
||||
struct chan *chan;
|
||||
LIST_HEAD(list);
|
||||
struct list_head *ele;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&irqs_to_free_lock, flags);
|
||||
list_splice_init(&irqs_to_free, &list);
|
||||
spin_unlock_irqrestore(&irqs_to_free_lock, flags);
|
||||
|
||||
list_for_each(ele, &list) {
|
||||
chan = list_entry(ele, struct chan, free_list);
|
||||
|
||||
if (chan->input && chan->enabled)
|
||||
um_free_irq(chan->line->driver->read_irq, chan);
|
||||
if (chan->output && chan->enabled)
|
||||
um_free_irq(chan->line->driver->write_irq, chan);
|
||||
chan->enabled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void close_one_chan(struct chan *chan, int delay_free_irq)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!chan->opened)
|
||||
return;
|
||||
|
||||
if (delay_free_irq) {
|
||||
spin_lock_irqsave(&irqs_to_free_lock, flags);
|
||||
list_add(&chan->free_list, &irqs_to_free);
|
||||
spin_unlock_irqrestore(&irqs_to_free_lock, flags);
|
||||
}
|
||||
else {
|
||||
if (chan->input && chan->enabled)
|
||||
um_free_irq(chan->line->driver->read_irq, chan);
|
||||
if (chan->output && chan->enabled)
|
||||
um_free_irq(chan->line->driver->write_irq, chan);
|
||||
chan->enabled = 0;
|
||||
}
|
||||
if (chan->ops->close != NULL)
|
||||
(*chan->ops->close)(chan->fd, chan->data);
|
||||
|
||||
chan->opened = 0;
|
||||
chan->fd = -1;
|
||||
}
|
||||
|
||||
void close_chan(struct line *line)
|
||||
{
|
||||
struct chan *chan;
|
||||
|
||||
/* Close in reverse order as open in case more than one of them
|
||||
* refers to the same device and they save and restore that device's
|
||||
* state. Then, the first one opened will have the original state,
|
||||
* so it must be the last closed.
|
||||
*/
|
||||
list_for_each_entry_reverse(chan, &line->chan_list, list) {
|
||||
close_one_chan(chan, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void deactivate_chan(struct chan *chan, int irq)
|
||||
{
|
||||
if (chan && chan->enabled)
|
||||
deactivate_fd(chan->fd, irq);
|
||||
}
|
||||
|
||||
void reactivate_chan(struct chan *chan, int irq)
|
||||
{
|
||||
if (chan && chan->enabled)
|
||||
reactivate_fd(chan->fd, irq);
|
||||
}
|
||||
|
||||
int write_chan(struct chan *chan, const char *buf, int len,
|
||||
int write_irq)
|
||||
{
|
||||
int n, ret = 0;
|
||||
|
||||
if (len == 0 || !chan || !chan->ops->write)
|
||||
return 0;
|
||||
|
||||
n = chan->ops->write(chan->fd, buf, len, chan->data);
|
||||
if (chan->primary) {
|
||||
ret = n;
|
||||
if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len)))
|
||||
reactivate_fd(chan->fd, write_irq);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int console_write_chan(struct chan *chan, const char *buf, int len)
|
||||
{
|
||||
int n, ret = 0;
|
||||
|
||||
if (!chan || !chan->ops->console_write)
|
||||
return 0;
|
||||
|
||||
n = chan->ops->console_write(chan->fd, buf, len);
|
||||
if (chan->primary)
|
||||
ret = n;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int console_open_chan(struct line *line, struct console *co)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = open_chan(&line->chan_list);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
printk(KERN_INFO "Console initialized on /dev/%s%d\n", co->name,
|
||||
co->index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chan_window_size(struct line *line, unsigned short *rows_out,
|
||||
unsigned short *cols_out)
|
||||
{
|
||||
struct chan *chan;
|
||||
|
||||
chan = line->chan_in;
|
||||
if (chan && chan->primary) {
|
||||
if (chan->ops->window_size == NULL)
|
||||
return 0;
|
||||
return chan->ops->window_size(chan->fd, chan->data,
|
||||
rows_out, cols_out);
|
||||
}
|
||||
chan = line->chan_out;
|
||||
if (chan && chan->primary) {
|
||||
if (chan->ops->window_size == NULL)
|
||||
return 0;
|
||||
return chan->ops->window_size(chan->fd, chan->data,
|
||||
rows_out, cols_out);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_one_chan(struct chan *chan)
|
||||
{
|
||||
list_del(&chan->list);
|
||||
|
||||
close_one_chan(chan, 0);
|
||||
|
||||
if (chan->ops->free != NULL)
|
||||
(*chan->ops->free)(chan->data);
|
||||
|
||||
if (chan->primary && chan->output)
|
||||
ignore_sigio_fd(chan->fd);
|
||||
kfree(chan);
|
||||
}
|
||||
|
||||
static void free_chan(struct list_head *chans)
|
||||
{
|
||||
struct list_head *ele, *next;
|
||||
struct chan *chan;
|
||||
|
||||
list_for_each_safe(ele, next, chans) {
|
||||
chan = list_entry(ele, struct chan, list);
|
||||
free_one_chan(chan);
|
||||
}
|
||||
}
|
||||
|
||||
static int one_chan_config_string(struct chan *chan, char *str, int size,
|
||||
char **error_out)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
if (chan == NULL) {
|
||||
CONFIG_CHUNK(str, size, n, "none", 1);
|
||||
return n;
|
||||
}
|
||||
|
||||
CONFIG_CHUNK(str, size, n, chan->ops->type, 0);
|
||||
|
||||
if (chan->dev == NULL) {
|
||||
CONFIG_CHUNK(str, size, n, "", 1);
|
||||
return n;
|
||||
}
|
||||
|
||||
CONFIG_CHUNK(str, size, n, ":", 0);
|
||||
CONFIG_CHUNK(str, size, n, chan->dev, 0);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int chan_pair_config_string(struct chan *in, struct chan *out,
|
||||
char *str, int size, char **error_out)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = one_chan_config_string(in, str, size, error_out);
|
||||
str += n;
|
||||
size -= n;
|
||||
|
||||
if (in == out) {
|
||||
CONFIG_CHUNK(str, size, n, "", 1);
|
||||
return n;
|
||||
}
|
||||
|
||||
CONFIG_CHUNK(str, size, n, ",", 1);
|
||||
n = one_chan_config_string(out, str, size, error_out);
|
||||
str += n;
|
||||
size -= n;
|
||||
CONFIG_CHUNK(str, size, n, "", 1);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int chan_config_string(struct line *line, char *str, int size,
|
||||
char **error_out)
|
||||
{
|
||||
struct chan *in = line->chan_in, *out = line->chan_out;
|
||||
|
||||
if (in && !in->primary)
|
||||
in = NULL;
|
||||
if (out && !out->primary)
|
||||
out = NULL;
|
||||
|
||||
return chan_pair_config_string(in, out, str, size, error_out);
|
||||
}
|
||||
|
||||
struct chan_type {
|
||||
char *key;
|
||||
const struct chan_ops *ops;
|
||||
};
|
||||
|
||||
static const struct chan_type chan_table[] = {
|
||||
{ "fd", &fd_ops },
|
||||
|
||||
#ifdef CONFIG_NULL_CHAN
|
||||
{ "null", &null_ops },
|
||||
#else
|
||||
{ "null", ¬_configged_ops },
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PORT_CHAN
|
||||
{ "port", &port_ops },
|
||||
#else
|
||||
{ "port", ¬_configged_ops },
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PTY_CHAN
|
||||
{ "pty", &pty_ops },
|
||||
{ "pts", &pts_ops },
|
||||
#else
|
||||
{ "pty", ¬_configged_ops },
|
||||
{ "pts", ¬_configged_ops },
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TTY_CHAN
|
||||
{ "tty", &tty_ops },
|
||||
#else
|
||||
{ "tty", ¬_configged_ops },
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_XTERM_CHAN
|
||||
{ "xterm", &xterm_ops },
|
||||
#else
|
||||
{ "xterm", ¬_configged_ops },
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct chan *parse_chan(struct line *line, char *str, int device,
|
||||
const struct chan_opts *opts, char **error_out)
|
||||
{
|
||||
const struct chan_type *entry;
|
||||
const struct chan_ops *ops;
|
||||
struct chan *chan;
|
||||
void *data;
|
||||
int i;
|
||||
|
||||
ops = NULL;
|
||||
data = NULL;
|
||||
for(i = 0; i < ARRAY_SIZE(chan_table); i++) {
|
||||
entry = &chan_table[i];
|
||||
if (!strncmp(str, entry->key, strlen(entry->key))) {
|
||||
ops = entry->ops;
|
||||
str += strlen(entry->key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ops == NULL) {
|
||||
*error_out = "No match for configured backends";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = (*ops->init)(str, device, opts);
|
||||
if (data == NULL) {
|
||||
*error_out = "Configuration failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
chan = kmalloc(sizeof(*chan), GFP_ATOMIC);
|
||||
if (chan == NULL) {
|
||||
*error_out = "Memory allocation failed";
|
||||
return NULL;
|
||||
}
|
||||
*chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list),
|
||||
.free_list =
|
||||
LIST_HEAD_INIT(chan->free_list),
|
||||
.line = line,
|
||||
.primary = 1,
|
||||
.input = 0,
|
||||
.output = 0,
|
||||
.opened = 0,
|
||||
.enabled = 0,
|
||||
.fd = -1,
|
||||
.ops = ops,
|
||||
.data = data });
|
||||
return chan;
|
||||
}
|
||||
|
||||
int parse_chan_pair(char *str, struct line *line, int device,
|
||||
const struct chan_opts *opts, char **error_out)
|
||||
{
|
||||
struct list_head *chans = &line->chan_list;
|
||||
struct chan *new;
|
||||
char *in, *out;
|
||||
|
||||
if (!list_empty(chans)) {
|
||||
line->chan_in = line->chan_out = NULL;
|
||||
free_chan(chans);
|
||||
INIT_LIST_HEAD(chans);
|
||||
}
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
out = strchr(str, ',');
|
||||
if (out != NULL) {
|
||||
in = str;
|
||||
*out = '\0';
|
||||
out++;
|
||||
new = parse_chan(line, in, device, opts, error_out);
|
||||
if (new == NULL)
|
||||
return -1;
|
||||
|
||||
new->input = 1;
|
||||
list_add(&new->list, chans);
|
||||
line->chan_in = new;
|
||||
|
||||
new = parse_chan(line, out, device, opts, error_out);
|
||||
if (new == NULL)
|
||||
return -1;
|
||||
|
||||
list_add(&new->list, chans);
|
||||
new->output = 1;
|
||||
line->chan_out = new;
|
||||
}
|
||||
else {
|
||||
new = parse_chan(line, str, device, opts, error_out);
|
||||
if (new == NULL)
|
||||
return -1;
|
||||
|
||||
list_add(&new->list, chans);
|
||||
new->input = 1;
|
||||
new->output = 1;
|
||||
line->chan_in = line->chan_out = new;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void chan_interrupt(struct line *line, int irq)
|
||||
{
|
||||
struct tty_port *port = &line->port;
|
||||
struct chan *chan = line->chan_in;
|
||||
int err;
|
||||
char c;
|
||||
|
||||
if (!chan || !chan->ops->read)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
if (!tty_buffer_request_room(port, 1)) {
|
||||
schedule_delayed_work(&line->task, 1);
|
||||
goto out;
|
||||
}
|
||||
err = chan->ops->read(chan->fd, &c, chan->data);
|
||||
if (err > 0)
|
||||
tty_insert_flip_char(port, c, TTY_NORMAL);
|
||||
} while (err > 0);
|
||||
|
||||
if (err == 0)
|
||||
reactivate_fd(chan->fd, irq);
|
||||
if (err == -EIO) {
|
||||
if (chan->primary) {
|
||||
tty_port_tty_hangup(&line->port, false);
|
||||
if (line->chan_out != chan)
|
||||
close_one_chan(line->chan_out, 1);
|
||||
}
|
||||
close_one_chan(chan, 1);
|
||||
if (chan->primary)
|
||||
return;
|
||||
}
|
||||
out:
|
||||
tty_flip_buffer_push(port);
|
||||
}
|
301
arch/um/drivers/chan_user.c
Normal file
301
arch/um/drivers/chan_user.c
Normal file
|
@ -0,0 +1,301 @@
|
|||
/*
|
||||
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "chan_user.h"
|
||||
#include <os.h>
|
||||
#include <um_malloc.h>
|
||||
|
||||
void generic_close(int fd, void *unused)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int generic_read(int fd, char *c_out, void *unused)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = read(fd, c_out, sizeof(*c_out));
|
||||
if (n > 0)
|
||||
return n;
|
||||
else if (errno == EAGAIN)
|
||||
return 0;
|
||||
else if (n == 0)
|
||||
return -EIO;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* XXX Trivial wrapper around write */
|
||||
|
||||
int generic_write(int fd, const char *buf, int n, void *unused)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = write(fd, buf, n);
|
||||
if (err > 0)
|
||||
return err;
|
||||
else if (errno == EAGAIN)
|
||||
return 0;
|
||||
else if (err == 0)
|
||||
return -EIO;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
int generic_window_size(int fd, void *unused, unsigned short *rows_out,
|
||||
unsigned short *cols_out)
|
||||
{
|
||||
struct winsize size;
|
||||
int ret;
|
||||
|
||||
if (ioctl(fd, TIOCGWINSZ, &size) < 0)
|
||||
return -errno;
|
||||
|
||||
ret = ((*rows_out != size.ws_row) || (*cols_out != size.ws_col));
|
||||
|
||||
*rows_out = size.ws_row;
|
||||
*cols_out = size.ws_col;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void generic_free(void *data)
|
||||
{
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
int generic_console_write(int fd, const char *buf, int n)
|
||||
{
|
||||
sigset_t old, no_sigio;
|
||||
struct termios save, new;
|
||||
int err;
|
||||
|
||||
if (isatty(fd)) {
|
||||
sigemptyset(&no_sigio);
|
||||
sigaddset(&no_sigio, SIGIO);
|
||||
if (sigprocmask(SIG_BLOCK, &no_sigio, &old))
|
||||
goto error;
|
||||
|
||||
CATCH_EINTR(err = tcgetattr(fd, &save));
|
||||
if (err)
|
||||
goto error;
|
||||
new = save;
|
||||
/*
|
||||
* The terminal becomes a bit less raw, to handle \n also as
|
||||
* "Carriage Return", not only as "New Line". Otherwise, the new
|
||||
* line won't start at the first column.
|
||||
*/
|
||||
new.c_oflag |= OPOST;
|
||||
CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &new));
|
||||
if (err)
|
||||
goto error;
|
||||
}
|
||||
err = generic_write(fd, buf, n, NULL);
|
||||
/*
|
||||
* Restore raw mode, in any case; we *must* ignore any error apart
|
||||
* EINTR, except for debug.
|
||||
*/
|
||||
if (isatty(fd)) {
|
||||
CATCH_EINTR(tcsetattr(fd, TCSAFLUSH, &save));
|
||||
sigprocmask(SIG_SETMASK, &old, NULL);
|
||||
}
|
||||
|
||||
return err;
|
||||
error:
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/*
|
||||
* UML SIGWINCH handling
|
||||
*
|
||||
* The point of this is to handle SIGWINCH on consoles which have host
|
||||
* ttys and relay them inside UML to whatever might be running on the
|
||||
* console and cares about the window size (since SIGWINCH notifies
|
||||
* about terminal size changes).
|
||||
*
|
||||
* So, we have a separate thread for each host tty attached to a UML
|
||||
* device (side-issue - I'm annoyed that one thread can't have
|
||||
* multiple controlling ttys for the purpose of handling SIGWINCH, but
|
||||
* I imagine there are other reasons that doesn't make any sense).
|
||||
*
|
||||
* SIGWINCH can't be received synchronously, so you have to set up to
|
||||
* receive it as a signal. That being the case, if you are going to
|
||||
* wait for it, it is convenient to sit in sigsuspend() and wait for
|
||||
* the signal to bounce you out of it (see below for how we make sure
|
||||
* to exit only on SIGWINCH).
|
||||
*/
|
||||
|
||||
static void winch_handler(int sig)
|
||||
{
|
||||
}
|
||||
|
||||
struct winch_data {
|
||||
int pty_fd;
|
||||
int pipe_fd;
|
||||
};
|
||||
|
||||
static int winch_thread(void *arg)
|
||||
{
|
||||
struct winch_data *data = arg;
|
||||
sigset_t sigs;
|
||||
int pty_fd, pipe_fd;
|
||||
int count;
|
||||
char c = 1;
|
||||
|
||||
pty_fd = data->pty_fd;
|
||||
pipe_fd = data->pipe_fd;
|
||||
count = write(pipe_fd, &c, sizeof(c));
|
||||
if (count != sizeof(c))
|
||||
printk(UM_KERN_ERR "winch_thread : failed to write "
|
||||
"synchronization byte, err = %d\n", -count);
|
||||
|
||||
/*
|
||||
* We are not using SIG_IGN on purpose, so don't fix it as I thought to
|
||||
* do! If using SIG_IGN, the sigsuspend() call below would not stop on
|
||||
* SIGWINCH.
|
||||
*/
|
||||
|
||||
signal(SIGWINCH, winch_handler);
|
||||
sigfillset(&sigs);
|
||||
/* Block all signals possible. */
|
||||
if (sigprocmask(SIG_SETMASK, &sigs, NULL) < 0) {
|
||||
printk(UM_KERN_ERR "winch_thread : sigprocmask failed, "
|
||||
"errno = %d\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
/* In sigsuspend(), block anything else than SIGWINCH. */
|
||||
sigdelset(&sigs, SIGWINCH);
|
||||
|
||||
if (setsid() < 0) {
|
||||
printk(UM_KERN_ERR "winch_thread : setsid failed, errno = %d\n",
|
||||
errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ioctl(pty_fd, TIOCSCTTY, 0) < 0) {
|
||||
printk(UM_KERN_ERR "winch_thread : TIOCSCTTY failed on "
|
||||
"fd %d err = %d\n", pty_fd, errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (tcsetpgrp(pty_fd, os_getpid()) < 0) {
|
||||
printk(UM_KERN_ERR "winch_thread : tcsetpgrp failed on "
|
||||
"fd %d err = %d\n", pty_fd, errno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* These are synchronization calls between various UML threads on the
|
||||
* host - since they are not different kernel threads, we cannot use
|
||||
* kernel semaphores. We don't use SysV semaphores because they are
|
||||
* persistent.
|
||||
*/
|
||||
count = read(pipe_fd, &c, sizeof(c));
|
||||
if (count != sizeof(c))
|
||||
printk(UM_KERN_ERR "winch_thread : failed to read "
|
||||
"synchronization byte, err = %d\n", errno);
|
||||
|
||||
while(1) {
|
||||
/*
|
||||
* This will be interrupted by SIGWINCH only, since
|
||||
* other signals are blocked.
|
||||
*/
|
||||
sigsuspend(&sigs);
|
||||
|
||||
count = write(pipe_fd, &c, sizeof(c));
|
||||
if (count != sizeof(c))
|
||||
printk(UM_KERN_ERR "winch_thread : write failed, "
|
||||
"err = %d\n", errno);
|
||||
}
|
||||
}
|
||||
|
||||
static int winch_tramp(int fd, struct tty_port *port, int *fd_out,
|
||||
unsigned long *stack_out)
|
||||
{
|
||||
struct winch_data data;
|
||||
int fds[2], n, err;
|
||||
char c;
|
||||
|
||||
err = os_pipe(fds, 1, 1);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "winch_tramp : os_pipe failed, err = %d\n",
|
||||
-err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
data = ((struct winch_data) { .pty_fd = fd,
|
||||
.pipe_fd = fds[1] } );
|
||||
/*
|
||||
* CLONE_FILES so this thread doesn't hold open files which are open
|
||||
* now, but later closed in a different thread. This is a
|
||||
* problem with /dev/net/tun, which if held open by this
|
||||
* thread, prevents the TUN/TAP device from being reused.
|
||||
*/
|
||||
err = run_helper_thread(winch_thread, &data, CLONE_FILES, stack_out);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "fork of winch_thread failed - errno = %d\n",
|
||||
-err);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
*fd_out = fds[0];
|
||||
n = read(fds[0], &c, sizeof(c));
|
||||
if (n != sizeof(c)) {
|
||||
printk(UM_KERN_ERR "winch_tramp : failed to read "
|
||||
"synchronization byte\n");
|
||||
printk(UM_KERN_ERR "read failed, err = %d\n", errno);
|
||||
printk(UM_KERN_ERR "fd %d will not support SIGWINCH\n", fd);
|
||||
err = -EINVAL;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (os_set_fd_block(*fd_out, 0)) {
|
||||
printk(UM_KERN_ERR "winch_tramp: failed to set thread_fd "
|
||||
"non-blocking.\n");
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
out_close:
|
||||
close(fds[1]);
|
||||
close(fds[0]);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
void register_winch(int fd, struct tty_port *port)
|
||||
{
|
||||
unsigned long stack;
|
||||
int pid, thread, count, thread_fd = -1;
|
||||
char c = 1;
|
||||
|
||||
if (!isatty(fd))
|
||||
return;
|
||||
|
||||
pid = tcgetpgrp(fd);
|
||||
if (is_skas_winch(pid, fd, port)) {
|
||||
register_winch_irq(-1, fd, -1, port, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pid == -1) {
|
||||
thread = winch_tramp(fd, port, &thread_fd, &stack);
|
||||
if (thread < 0)
|
||||
return;
|
||||
|
||||
register_winch_irq(thread_fd, fd, thread, port, stack);
|
||||
|
||||
count = write(thread_fd, &c, sizeof(c));
|
||||
if (count != sizeof(c))
|
||||
printk(UM_KERN_ERR "register_winch : failed to write "
|
||||
"synchronization byte, err = %d\n", errno);
|
||||
}
|
||||
}
|
53
arch/um/drivers/chan_user.h
Normal file
53
arch/um/drivers/chan_user.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __CHAN_USER_H__
|
||||
#define __CHAN_USER_H__
|
||||
|
||||
#include <init.h>
|
||||
|
||||
struct chan_opts {
|
||||
void (*const announce)(char *dev_name, int dev);
|
||||
char *xterm_title;
|
||||
const int raw;
|
||||
};
|
||||
|
||||
struct chan_ops {
|
||||
char *type;
|
||||
void *(*init)(char *, int, const struct chan_opts *);
|
||||
int (*open)(int, int, int, void *, char **);
|
||||
void (*close)(int, void *);
|
||||
int (*read)(int, char *, void *);
|
||||
int (*write)(int, const char *, int, void *);
|
||||
int (*console_write)(int, const char *, int);
|
||||
int (*window_size)(int, void *, unsigned short *, unsigned short *);
|
||||
void (*free)(void *);
|
||||
int winch;
|
||||
};
|
||||
|
||||
extern const struct chan_ops fd_ops, null_ops, port_ops, pts_ops, pty_ops,
|
||||
tty_ops, xterm_ops;
|
||||
|
||||
extern void generic_close(int fd, void *unused);
|
||||
extern int generic_read(int fd, char *c_out, void *unused);
|
||||
extern int generic_write(int fd, const char *buf, int n, void *unused);
|
||||
extern int generic_console_write(int fd, const char *buf, int n);
|
||||
extern int generic_window_size(int fd, void *unused, unsigned short *rows_out,
|
||||
unsigned short *cols_out);
|
||||
extern void generic_free(void *data);
|
||||
|
||||
struct tty_port;
|
||||
extern void register_winch(int fd, struct tty_port *port);
|
||||
extern void register_winch_irq(int fd, int tty_fd, int pid,
|
||||
struct tty_port *port, unsigned long stack);
|
||||
|
||||
#define __channel_help(fn, prefix) \
|
||||
__uml_help(fn, prefix "[0-9]*=<channel description>\n" \
|
||||
" Attach a console or serial line to a host channel. See\n" \
|
||||
" http://user-mode-linux.sourceforge.net/old/input.html for a complete\n" \
|
||||
" description of this switch.\n\n" \
|
||||
);
|
||||
|
||||
#endif
|
32
arch/um/drivers/cow.h
Normal file
32
arch/um/drivers/cow.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
#ifndef __COW_H__
|
||||
#define __COW_H__
|
||||
|
||||
#include <asm/types.h>
|
||||
|
||||
extern int init_cow_file(int fd, char *cow_file, char *backing_file,
|
||||
int sectorsize, int alignment, int *bitmap_offset_out,
|
||||
unsigned long *bitmap_len_out, int *data_offset_out);
|
||||
|
||||
extern int file_reader(__u64 offset, char *buf, int len, void *arg);
|
||||
extern int read_cow_header(int (*reader)(__u64, char *, int, void *),
|
||||
void *arg, __u32 *version_out,
|
||||
char **backing_file_out, time_t *mtime_out,
|
||||
unsigned long long *size_out, int *sectorsize_out,
|
||||
__u32 *align_out, int *bitmap_offset_out);
|
||||
|
||||
extern int write_cow_header(char *cow_file, int fd, char *backing_file,
|
||||
int sectorsize, int alignment,
|
||||
unsigned long long *size);
|
||||
|
||||
extern void cow_sizes(int version, __u64 size, int sectorsize, int align,
|
||||
int bitmap_offset, unsigned long *bitmap_len_out,
|
||||
int *data_offset_out);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------------------
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
40
arch/um/drivers/cow_sys.h
Normal file
40
arch/um/drivers/cow_sys.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef __COW_SYS_H__
|
||||
#define __COW_SYS_H__
|
||||
|
||||
#include <kern_util.h>
|
||||
#include <os.h>
|
||||
#include <um_malloc.h>
|
||||
|
||||
static inline void *cow_malloc(int size)
|
||||
{
|
||||
return uml_kmalloc(size, UM_GFP_KERNEL);
|
||||
}
|
||||
|
||||
static inline void cow_free(void *ptr)
|
||||
{
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
#define cow_printf printk
|
||||
|
||||
static inline char *cow_strdup(char *str)
|
||||
{
|
||||
return uml_strdup(str);
|
||||
}
|
||||
|
||||
static inline int cow_seek_file(int fd, __u64 offset)
|
||||
{
|
||||
return os_seek_file(fd, offset);
|
||||
}
|
||||
|
||||
static inline int cow_file_size(char *file, unsigned long long *size_out)
|
||||
{
|
||||
return os_file_size(file, size_out);
|
||||
}
|
||||
|
||||
static inline int cow_write_file(int fd, void *buf, int size)
|
||||
{
|
||||
return os_write_file(fd, buf, size);
|
||||
}
|
||||
|
||||
#endif
|
443
arch/um/drivers/cow_user.c
Normal file
443
arch/um/drivers/cow_user.c
Normal file
|
@ -0,0 +1,443 @@
|
|||
/*
|
||||
* Copyright (C) 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
/*
|
||||
* _XOPEN_SOURCE is needed for pread, but we define _GNU_SOURCE, which defines
|
||||
* that.
|
||||
*/
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <endian.h>
|
||||
#include "cow.h"
|
||||
#include "cow_sys.h"
|
||||
|
||||
#define PATH_LEN_V1 256
|
||||
|
||||
typedef __u32 time32_t;
|
||||
|
||||
struct cow_header_v1 {
|
||||
__s32 magic;
|
||||
__s32 version;
|
||||
char backing_file[PATH_LEN_V1];
|
||||
time32_t mtime;
|
||||
__u64 size;
|
||||
__s32 sectorsize;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in
|
||||
* case other systems have different values for MAXPATHLEN.
|
||||
*
|
||||
* The same must hold for V2 - we want file format compatibility, not anything
|
||||
* else.
|
||||
*/
|
||||
#define PATH_LEN_V3 4096
|
||||
#define PATH_LEN_V2 PATH_LEN_V3
|
||||
|
||||
struct cow_header_v2 {
|
||||
__u32 magic;
|
||||
__u32 version;
|
||||
char backing_file[PATH_LEN_V2];
|
||||
time32_t mtime;
|
||||
__u64 size;
|
||||
__s32 sectorsize;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* Changes from V2 -
|
||||
* PATH_LEN_V3 as described above
|
||||
* Explicitly specify field bit lengths for systems with different
|
||||
* lengths for the usual C types. Not sure whether char or
|
||||
* time_t should be changed, this can be changed later without
|
||||
* breaking compatibility
|
||||
* Add alignment field so that different alignments can be used for the
|
||||
* bitmap and data
|
||||
* Add cow_format field to allow for the possibility of different ways
|
||||
* of specifying the COW blocks. For now, the only value is 0,
|
||||
* for the traditional COW bitmap.
|
||||
* Move the backing_file field to the end of the header. This allows
|
||||
* for the possibility of expanding it into the padding required
|
||||
* by the bitmap alignment.
|
||||
* The bitmap and data portions of the file will be aligned as specified
|
||||
* by the alignment field. This is to allow COW files to be
|
||||
* put on devices with restrictions on access alignments, such as
|
||||
* /dev/raw, with a 512 byte alignment restriction. This also
|
||||
* allows the data to be more aligned more strictly than on
|
||||
* sector boundaries. This is needed for ubd-mmap, which needs
|
||||
* the data to be page aligned.
|
||||
* Fixed (finally!) the rounding bug
|
||||
*/
|
||||
|
||||
/*
|
||||
* Until Dec2005, __attribute__((packed)) was left out from the below
|
||||
* definition, leading on 64-bit systems to 4 bytes of padding after mtime, to
|
||||
* align size to 8-byte alignment. This shifted all fields above (no padding
|
||||
* was present on 32-bit, no other padding was added).
|
||||
*
|
||||
* However, this _can be detected_: it means that cow_format (always 0 until
|
||||
* now) is shifted onto the first 4 bytes of backing_file, where it is otherwise
|
||||
* impossible to find 4 zeros. -bb */
|
||||
|
||||
struct cow_header_v3 {
|
||||
__u32 magic;
|
||||
__u32 version;
|
||||
__u32 mtime;
|
||||
__u64 size;
|
||||
__u32 sectorsize;
|
||||
__u32 alignment;
|
||||
__u32 cow_format;
|
||||
char backing_file[PATH_LEN_V3];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* This is the broken layout used by some 64-bit binaries. */
|
||||
struct cow_header_v3_broken {
|
||||
__u32 magic;
|
||||
__u32 version;
|
||||
__s64 mtime;
|
||||
__u64 size;
|
||||
__u32 sectorsize;
|
||||
__u32 alignment;
|
||||
__u32 cow_format;
|
||||
char backing_file[PATH_LEN_V3];
|
||||
};
|
||||
|
||||
/* COW format definitions - for now, we have only the usual COW bitmap */
|
||||
#define COW_BITMAP 0
|
||||
|
||||
union cow_header {
|
||||
struct cow_header_v1 v1;
|
||||
struct cow_header_v2 v2;
|
||||
struct cow_header_v3 v3;
|
||||
struct cow_header_v3_broken v3_b;
|
||||
};
|
||||
|
||||
#define COW_MAGIC 0x4f4f4f4d /* MOOO */
|
||||
#define COW_VERSION 3
|
||||
|
||||
#define DIV_ROUND(x, len) (((x) + (len) - 1) / (len))
|
||||
#define ROUND_UP(x, align) DIV_ROUND(x, align) * (align)
|
||||
|
||||
void cow_sizes(int version, __u64 size, int sectorsize, int align,
|
||||
int bitmap_offset, unsigned long *bitmap_len_out,
|
||||
int *data_offset_out)
|
||||
{
|
||||
if (version < 3) {
|
||||
*bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize);
|
||||
|
||||
*data_offset_out = bitmap_offset + *bitmap_len_out;
|
||||
*data_offset_out = (*data_offset_out + sectorsize - 1) /
|
||||
sectorsize;
|
||||
*data_offset_out *= sectorsize;
|
||||
}
|
||||
else {
|
||||
*bitmap_len_out = DIV_ROUND(size, sectorsize);
|
||||
*bitmap_len_out = DIV_ROUND(*bitmap_len_out, 8);
|
||||
|
||||
*data_offset_out = bitmap_offset + *bitmap_len_out;
|
||||
*data_offset_out = ROUND_UP(*data_offset_out, align);
|
||||
}
|
||||
}
|
||||
|
||||
static int absolutize(char *to, int size, char *from)
|
||||
{
|
||||
char save_cwd[256], *slash;
|
||||
int remaining;
|
||||
|
||||
if (getcwd(save_cwd, sizeof(save_cwd)) == NULL) {
|
||||
cow_printf("absolutize : unable to get cwd - errno = %d\n",
|
||||
errno);
|
||||
return -1;
|
||||
}
|
||||
slash = strrchr(from, '/');
|
||||
if (slash != NULL) {
|
||||
*slash = '\0';
|
||||
if (chdir(from)) {
|
||||
*slash = '/';
|
||||
cow_printf("absolutize : Can't cd to '%s' - "
|
||||
"errno = %d\n", from, errno);
|
||||
return -1;
|
||||
}
|
||||
*slash = '/';
|
||||
if (getcwd(to, size) == NULL) {
|
||||
cow_printf("absolutize : unable to get cwd of '%s' - "
|
||||
"errno = %d\n", from, errno);
|
||||
return -1;
|
||||
}
|
||||
remaining = size - strlen(to);
|
||||
if (strlen(slash) + 1 > remaining) {
|
||||
cow_printf("absolutize : unable to fit '%s' into %d "
|
||||
"chars\n", from, size);
|
||||
return -1;
|
||||
}
|
||||
strcat(to, slash);
|
||||
}
|
||||
else {
|
||||
if (strlen(save_cwd) + 1 + strlen(from) + 1 > size) {
|
||||
cow_printf("absolutize : unable to fit '%s' into %d "
|
||||
"chars\n", from, size);
|
||||
return -1;
|
||||
}
|
||||
strcpy(to, save_cwd);
|
||||
strcat(to, "/");
|
||||
strcat(to, from);
|
||||
}
|
||||
if (chdir(save_cwd)) {
|
||||
cow_printf("absolutize : Can't cd to '%s' - "
|
||||
"errno = %d\n", save_cwd, errno);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_cow_header(char *cow_file, int fd, char *backing_file,
|
||||
int sectorsize, int alignment, unsigned long long *size)
|
||||
{
|
||||
struct cow_header_v3 *header;
|
||||
unsigned long modtime;
|
||||
int err;
|
||||
|
||||
err = cow_seek_file(fd, 0);
|
||||
if (err < 0) {
|
||||
cow_printf("write_cow_header - lseek failed, err = %d\n", -err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
header = cow_malloc(sizeof(*header));
|
||||
if (header == NULL) {
|
||||
cow_printf("write_cow_header - failed to allocate COW V3 "
|
||||
"header\n");
|
||||
goto out;
|
||||
}
|
||||
header->magic = htobe32(COW_MAGIC);
|
||||
header->version = htobe32(COW_VERSION);
|
||||
|
||||
err = -EINVAL;
|
||||
if (strlen(backing_file) > sizeof(header->backing_file) - 1) {
|
||||
/* Below, %zd is for a size_t value */
|
||||
cow_printf("Backing file name \"%s\" is too long - names are "
|
||||
"limited to %zd characters\n", backing_file,
|
||||
sizeof(header->backing_file) - 1);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (absolutize(header->backing_file, sizeof(header->backing_file),
|
||||
backing_file))
|
||||
goto out_free;
|
||||
|
||||
err = os_file_modtime(header->backing_file, &modtime);
|
||||
if (err < 0) {
|
||||
cow_printf("write_cow_header - backing file '%s' mtime "
|
||||
"request failed, err = %d\n", header->backing_file,
|
||||
-err);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
err = cow_file_size(header->backing_file, size);
|
||||
if (err < 0) {
|
||||
cow_printf("write_cow_header - couldn't get size of "
|
||||
"backing file '%s', err = %d\n",
|
||||
header->backing_file, -err);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
header->mtime = htobe32(modtime);
|
||||
header->size = htobe64(*size);
|
||||
header->sectorsize = htobe32(sectorsize);
|
||||
header->alignment = htobe32(alignment);
|
||||
header->cow_format = COW_BITMAP;
|
||||
|
||||
err = cow_write_file(fd, header, sizeof(*header));
|
||||
if (err != sizeof(*header)) {
|
||||
cow_printf("write_cow_header - write of header to "
|
||||
"new COW file '%s' failed, err = %d\n", cow_file,
|
||||
-err);
|
||||
goto out_free;
|
||||
}
|
||||
err = 0;
|
||||
out_free:
|
||||
cow_free(header);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int file_reader(__u64 offset, char *buf, int len, void *arg)
|
||||
{
|
||||
int fd = *((int *) arg);
|
||||
|
||||
return pread(fd, buf, len, offset);
|
||||
}
|
||||
|
||||
/* XXX Need to sanity-check the values read from the header */
|
||||
|
||||
int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg,
|
||||
__u32 *version_out, char **backing_file_out,
|
||||
time_t *mtime_out, unsigned long long *size_out,
|
||||
int *sectorsize_out, __u32 *align_out,
|
||||
int *bitmap_offset_out)
|
||||
{
|
||||
union cow_header *header;
|
||||
char *file;
|
||||
int err, n;
|
||||
unsigned long version, magic;
|
||||
|
||||
header = cow_malloc(sizeof(*header));
|
||||
if (header == NULL) {
|
||||
cow_printf("read_cow_header - Failed to allocate header\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
err = -EINVAL;
|
||||
n = (*reader)(0, (char *) header, sizeof(*header), arg);
|
||||
if (n < offsetof(typeof(header->v1), backing_file)) {
|
||||
cow_printf("read_cow_header - short header\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
magic = header->v1.magic;
|
||||
if (magic == COW_MAGIC)
|
||||
version = header->v1.version;
|
||||
else if (magic == be32toh(COW_MAGIC))
|
||||
version = be32toh(header->v1.version);
|
||||
/* No error printed because the non-COW case comes through here */
|
||||
else goto out;
|
||||
|
||||
*version_out = version;
|
||||
|
||||
if (version == 1) {
|
||||
if (n < sizeof(header->v1)) {
|
||||
cow_printf("read_cow_header - failed to read V1 "
|
||||
"header\n");
|
||||
goto out;
|
||||
}
|
||||
*mtime_out = header->v1.mtime;
|
||||
*size_out = header->v1.size;
|
||||
*sectorsize_out = header->v1.sectorsize;
|
||||
*bitmap_offset_out = sizeof(header->v1);
|
||||
*align_out = *sectorsize_out;
|
||||
file = header->v1.backing_file;
|
||||
}
|
||||
else if (version == 2) {
|
||||
if (n < sizeof(header->v2)) {
|
||||
cow_printf("read_cow_header - failed to read V2 "
|
||||
"header\n");
|
||||
goto out;
|
||||
}
|
||||
*mtime_out = be32toh(header->v2.mtime);
|
||||
*size_out = be64toh(header->v2.size);
|
||||
*sectorsize_out = be32toh(header->v2.sectorsize);
|
||||
*bitmap_offset_out = sizeof(header->v2);
|
||||
*align_out = *sectorsize_out;
|
||||
file = header->v2.backing_file;
|
||||
}
|
||||
/* This is very subtle - see above at union cow_header definition */
|
||||
else if (version == 3 && (*((int*)header->v3.backing_file) != 0)) {
|
||||
if (n < sizeof(header->v3)) {
|
||||
cow_printf("read_cow_header - failed to read V3 "
|
||||
"header\n");
|
||||
goto out;
|
||||
}
|
||||
*mtime_out = be32toh(header->v3.mtime);
|
||||
*size_out = be64toh(header->v3.size);
|
||||
*sectorsize_out = be32toh(header->v3.sectorsize);
|
||||
*align_out = be32toh(header->v3.alignment);
|
||||
if (*align_out == 0) {
|
||||
cow_printf("read_cow_header - invalid COW header, "
|
||||
"align == 0\n");
|
||||
}
|
||||
*bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out);
|
||||
file = header->v3.backing_file;
|
||||
}
|
||||
else if (version == 3) {
|
||||
cow_printf("read_cow_header - broken V3 file with"
|
||||
" 64-bit layout - recovering content.\n");
|
||||
|
||||
if (n < sizeof(header->v3_b)) {
|
||||
cow_printf("read_cow_header - failed to read V3 "
|
||||
"header\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* this was used until Dec2005 - 64bits are needed to represent
|
||||
* 2038+. I.e. we can safely do this truncating cast.
|
||||
*
|
||||
* Additionally, we must use be32toh() instead of be64toh(), since
|
||||
* the program used to use the former (tested - I got mtime
|
||||
* mismatch "0 vs whatever").
|
||||
*
|
||||
* Ever heard about bug-to-bug-compatibility ? ;-) */
|
||||
*mtime_out = (time32_t) be32toh(header->v3_b.mtime);
|
||||
|
||||
*size_out = be64toh(header->v3_b.size);
|
||||
*sectorsize_out = be32toh(header->v3_b.sectorsize);
|
||||
*align_out = be32toh(header->v3_b.alignment);
|
||||
if (*align_out == 0) {
|
||||
cow_printf("read_cow_header - invalid COW header, "
|
||||
"align == 0\n");
|
||||
}
|
||||
*bitmap_offset_out = ROUND_UP(sizeof(header->v3_b), *align_out);
|
||||
file = header->v3_b.backing_file;
|
||||
}
|
||||
else {
|
||||
cow_printf("read_cow_header - invalid COW version\n");
|
||||
goto out;
|
||||
}
|
||||
err = -ENOMEM;
|
||||
*backing_file_out = cow_strdup(file);
|
||||
if (*backing_file_out == NULL) {
|
||||
cow_printf("read_cow_header - failed to allocate backing "
|
||||
"file\n");
|
||||
goto out;
|
||||
}
|
||||
err = 0;
|
||||
out:
|
||||
cow_free(header);
|
||||
return err;
|
||||
}
|
||||
|
||||
int init_cow_file(int fd, char *cow_file, char *backing_file, int sectorsize,
|
||||
int alignment, int *bitmap_offset_out,
|
||||
unsigned long *bitmap_len_out, int *data_offset_out)
|
||||
{
|
||||
unsigned long long size, offset;
|
||||
char zero = 0;
|
||||
int err;
|
||||
|
||||
err = write_cow_header(cow_file, fd, backing_file, sectorsize,
|
||||
alignment, &size);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
*bitmap_offset_out = ROUND_UP(sizeof(struct cow_header_v3), alignment);
|
||||
cow_sizes(COW_VERSION, size, sectorsize, alignment, *bitmap_offset_out,
|
||||
bitmap_len_out, data_offset_out);
|
||||
|
||||
offset = *data_offset_out + size - sizeof(zero);
|
||||
err = cow_seek_file(fd, offset);
|
||||
if (err < 0) {
|
||||
cow_printf("cow bitmap lseek failed : err = %d\n", -err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* does not really matter how much we write it is just to set EOF
|
||||
* this also sets the entire COW bitmap
|
||||
* to zero without having to allocate it
|
||||
*/
|
||||
err = cow_write_file(fd, &zero, sizeof(zero));
|
||||
if (err != sizeof(zero)) {
|
||||
cow_printf("Write of bitmap to new COW file '%s' failed, "
|
||||
"err = %d\n", cow_file, -err);
|
||||
if (err >= 0)
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
return err;
|
||||
}
|
29
arch/um/drivers/daemon.h
Normal file
29
arch/um/drivers/daemon.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __DAEMON_H__
|
||||
#define __DAEMON_H__
|
||||
|
||||
#include <net_user.h>
|
||||
|
||||
#define SWITCH_VERSION 3
|
||||
|
||||
struct daemon_data {
|
||||
char *sock_type;
|
||||
char *ctl_sock;
|
||||
void *ctl_addr;
|
||||
void *data_addr;
|
||||
void *local_addr;
|
||||
int fd;
|
||||
int control;
|
||||
void *dev;
|
||||
};
|
||||
|
||||
extern const struct net_user_info daemon_user_info;
|
||||
|
||||
extern int daemon_user_write(int fd, void *buf, int len,
|
||||
struct daemon_data *pri);
|
||||
|
||||
#endif
|
95
arch/um/drivers/daemon_kern.c
Normal file
95
arch/um/drivers/daemon_kern.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
|
||||
* James Leu (jleu@mindspring.net).
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Copyright (C) 2001 by various other people who didn't put their name here.
|
||||
* Licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <net_kern.h>
|
||||
#include "daemon.h"
|
||||
|
||||
struct daemon_init {
|
||||
char *sock_type;
|
||||
char *ctl_sock;
|
||||
};
|
||||
|
||||
static void daemon_init(struct net_device *dev, void *data)
|
||||
{
|
||||
struct uml_net_private *pri;
|
||||
struct daemon_data *dpri;
|
||||
struct daemon_init *init = data;
|
||||
|
||||
pri = netdev_priv(dev);
|
||||
dpri = (struct daemon_data *) pri->user;
|
||||
dpri->sock_type = init->sock_type;
|
||||
dpri->ctl_sock = init->ctl_sock;
|
||||
dpri->fd = -1;
|
||||
dpri->control = -1;
|
||||
dpri->dev = dev;
|
||||
/* We will free this pointer. If it contains crap we're burned. */
|
||||
dpri->ctl_addr = NULL;
|
||||
dpri->data_addr = NULL;
|
||||
dpri->local_addr = NULL;
|
||||
|
||||
printk("daemon backend (uml_switch version %d) - %s:%s",
|
||||
SWITCH_VERSION, dpri->sock_type, dpri->ctl_sock);
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
static int daemon_read(int fd, struct sk_buff *skb, struct uml_net_private *lp)
|
||||
{
|
||||
return net_recvfrom(fd, skb_mac_header(skb),
|
||||
skb->dev->mtu + ETH_HEADER_OTHER);
|
||||
}
|
||||
|
||||
static int daemon_write(int fd, struct sk_buff *skb, struct uml_net_private *lp)
|
||||
{
|
||||
return daemon_user_write(fd, skb->data, skb->len,
|
||||
(struct daemon_data *) &lp->user);
|
||||
}
|
||||
|
||||
static const struct net_kern_info daemon_kern_info = {
|
||||
.init = daemon_init,
|
||||
.protocol = eth_protocol,
|
||||
.read = daemon_read,
|
||||
.write = daemon_write,
|
||||
};
|
||||
|
||||
static int daemon_setup(char *str, char **mac_out, void *data)
|
||||
{
|
||||
struct daemon_init *init = data;
|
||||
char *remain;
|
||||
|
||||
*init = ((struct daemon_init)
|
||||
{ .sock_type = "unix",
|
||||
.ctl_sock = "/tmp/uml.ctl" });
|
||||
|
||||
remain = split_if_spec(str, mac_out, &init->sock_type, &init->ctl_sock,
|
||||
NULL);
|
||||
if (remain != NULL)
|
||||
printk(KERN_WARNING "daemon_setup : Ignoring data socket "
|
||||
"specification\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct transport daemon_transport = {
|
||||
.list = LIST_HEAD_INIT(daemon_transport.list),
|
||||
.name = "daemon",
|
||||
.setup = daemon_setup,
|
||||
.user = &daemon_user_info,
|
||||
.kern = &daemon_kern_info,
|
||||
.private_size = sizeof(struct daemon_data),
|
||||
.setup_size = sizeof(struct daemon_init),
|
||||
};
|
||||
|
||||
static int register_daemon(void)
|
||||
{
|
||||
register_transport(&daemon_transport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(register_daemon);
|
193
arch/um/drivers/daemon_user.c
Normal file
193
arch/um/drivers/daemon_user.c
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
|
||||
* James Leu (jleu@mindspring.net).
|
||||
* Copyright (C) 2001 by various other people who didn't put their name here.
|
||||
* Licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/un.h>
|
||||
#include "daemon.h"
|
||||
#include <net_user.h>
|
||||
#include <os.h>
|
||||
#include <um_malloc.h>
|
||||
|
||||
enum request_type { REQ_NEW_CONTROL };
|
||||
|
||||
#define SWITCH_MAGIC 0xfeedface
|
||||
|
||||
struct request_v3 {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
enum request_type type;
|
||||
struct sockaddr_un sock;
|
||||
};
|
||||
|
||||
static struct sockaddr_un *new_addr(void *name, int len)
|
||||
{
|
||||
struct sockaddr_un *sun;
|
||||
|
||||
sun = uml_kmalloc(sizeof(struct sockaddr_un), UM_GFP_KERNEL);
|
||||
if (sun == NULL) {
|
||||
printk(UM_KERN_ERR "new_addr: allocation of sockaddr_un "
|
||||
"failed\n");
|
||||
return NULL;
|
||||
}
|
||||
sun->sun_family = AF_UNIX;
|
||||
memcpy(sun->sun_path, name, len);
|
||||
return sun;
|
||||
}
|
||||
|
||||
static int connect_to_switch(struct daemon_data *pri)
|
||||
{
|
||||
struct sockaddr_un *ctl_addr = pri->ctl_addr;
|
||||
struct sockaddr_un *local_addr = pri->local_addr;
|
||||
struct sockaddr_un *sun;
|
||||
struct request_v3 req;
|
||||
int fd, n, err;
|
||||
|
||||
pri->control = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (pri->control < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "daemon_open : control socket failed, "
|
||||
"errno = %d\n", -err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (connect(pri->control, (struct sockaddr *) ctl_addr,
|
||||
sizeof(*ctl_addr)) < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "daemon_open : control connect failed, "
|
||||
"errno = %d\n", -err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (fd < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "daemon_open : data socket failed, "
|
||||
"errno = %d\n", -err);
|
||||
goto out;
|
||||
}
|
||||
if (bind(fd, (struct sockaddr *) local_addr, sizeof(*local_addr)) < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "daemon_open : data bind failed, "
|
||||
"errno = %d\n", -err);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
sun = uml_kmalloc(sizeof(struct sockaddr_un), UM_GFP_KERNEL);
|
||||
if (sun == NULL) {
|
||||
printk(UM_KERN_ERR "new_addr: allocation of sockaddr_un "
|
||||
"failed\n");
|
||||
err = -ENOMEM;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
req.magic = SWITCH_MAGIC;
|
||||
req.version = SWITCH_VERSION;
|
||||
req.type = REQ_NEW_CONTROL;
|
||||
req.sock = *local_addr;
|
||||
n = write(pri->control, &req, sizeof(req));
|
||||
if (n != sizeof(req)) {
|
||||
printk(UM_KERN_ERR "daemon_open : control setup request "
|
||||
"failed, err = %d\n", -errno);
|
||||
err = -ENOTCONN;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
n = read(pri->control, sun, sizeof(*sun));
|
||||
if (n != sizeof(*sun)) {
|
||||
printk(UM_KERN_ERR "daemon_open : read of data socket failed, "
|
||||
"err = %d\n", -errno);
|
||||
err = -ENOTCONN;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
pri->data_addr = sun;
|
||||
return fd;
|
||||
|
||||
out_free:
|
||||
kfree(sun);
|
||||
out_close:
|
||||
close(fd);
|
||||
out:
|
||||
close(pri->control);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int daemon_user_init(void *data, void *dev)
|
||||
{
|
||||
struct daemon_data *pri = data;
|
||||
struct timeval tv;
|
||||
struct {
|
||||
char zero;
|
||||
int pid;
|
||||
int usecs;
|
||||
} name;
|
||||
|
||||
if (!strcmp(pri->sock_type, "unix"))
|
||||
pri->ctl_addr = new_addr(pri->ctl_sock,
|
||||
strlen(pri->ctl_sock) + 1);
|
||||
name.zero = 0;
|
||||
name.pid = os_getpid();
|
||||
gettimeofday(&tv, NULL);
|
||||
name.usecs = tv.tv_usec;
|
||||
pri->local_addr = new_addr(&name, sizeof(name));
|
||||
pri->dev = dev;
|
||||
pri->fd = connect_to_switch(pri);
|
||||
if (pri->fd < 0) {
|
||||
kfree(pri->local_addr);
|
||||
pri->local_addr = NULL;
|
||||
return pri->fd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int daemon_open(void *data)
|
||||
{
|
||||
struct daemon_data *pri = data;
|
||||
return pri->fd;
|
||||
}
|
||||
|
||||
static void daemon_remove(void *data)
|
||||
{
|
||||
struct daemon_data *pri = data;
|
||||
|
||||
close(pri->fd);
|
||||
pri->fd = -1;
|
||||
close(pri->control);
|
||||
pri->control = -1;
|
||||
|
||||
kfree(pri->data_addr);
|
||||
pri->data_addr = NULL;
|
||||
kfree(pri->ctl_addr);
|
||||
pri->ctl_addr = NULL;
|
||||
kfree(pri->local_addr);
|
||||
pri->local_addr = NULL;
|
||||
}
|
||||
|
||||
int daemon_user_write(int fd, void *buf, int len, struct daemon_data *pri)
|
||||
{
|
||||
struct sockaddr_un *data_addr = pri->data_addr;
|
||||
|
||||
return net_sendto(fd, buf, len, data_addr, sizeof(*data_addr));
|
||||
}
|
||||
|
||||
const struct net_user_info daemon_user_info = {
|
||||
.init = daemon_user_init,
|
||||
.open = daemon_open,
|
||||
.close = NULL,
|
||||
.remove = daemon_remove,
|
||||
.add_address = NULL,
|
||||
.delete_address = NULL,
|
||||
.mtu = ETH_MAX_PACKET,
|
||||
.max_packet = ETH_MAX_PACKET + ETH_HEADER_OTHER,
|
||||
};
|
95
arch/um/drivers/fd.c
Normal file
95
arch/um/drivers/fd.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include "chan_user.h"
|
||||
#include <os.h>
|
||||
#include <um_malloc.h>
|
||||
|
||||
struct fd_chan {
|
||||
int fd;
|
||||
int raw;
|
||||
struct termios tt;
|
||||
char str[sizeof("1234567890\0")];
|
||||
};
|
||||
|
||||
static void *fd_init(char *str, int device, const struct chan_opts *opts)
|
||||
{
|
||||
struct fd_chan *data;
|
||||
char *end;
|
||||
int n;
|
||||
|
||||
if (*str != ':') {
|
||||
printk(UM_KERN_ERR "fd_init : channel type 'fd' must specify a "
|
||||
"file descriptor\n");
|
||||
return NULL;
|
||||
}
|
||||
str++;
|
||||
n = strtoul(str, &end, 0);
|
||||
if ((*end != '\0') || (end == str)) {
|
||||
printk(UM_KERN_ERR "fd_init : couldn't parse file descriptor "
|
||||
"'%s'\n", str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
|
||||
*data = ((struct fd_chan) { .fd = n,
|
||||
.raw = opts->raw });
|
||||
return data;
|
||||
}
|
||||
|
||||
static int fd_open(int input, int output, int primary, void *d, char **dev_out)
|
||||
{
|
||||
struct fd_chan *data = d;
|
||||
int err;
|
||||
|
||||
if (data->raw && isatty(data->fd)) {
|
||||
CATCH_EINTR(err = tcgetattr(data->fd, &data->tt));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = raw(data->fd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
sprintf(data->str, "%d", data->fd);
|
||||
*dev_out = data->str;
|
||||
return data->fd;
|
||||
}
|
||||
|
||||
static void fd_close(int fd, void *d)
|
||||
{
|
||||
struct fd_chan *data = d;
|
||||
int err;
|
||||
|
||||
if (!data->raw || !isatty(fd))
|
||||
return;
|
||||
|
||||
CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &data->tt));
|
||||
if (err)
|
||||
printk(UM_KERN_ERR "Failed to restore terminal state - "
|
||||
"errno = %d\n", -err);
|
||||
data->raw = 0;
|
||||
}
|
||||
|
||||
const struct chan_ops fd_ops = {
|
||||
.type = "fd",
|
||||
.init = fd_init,
|
||||
.open = fd_open,
|
||||
.close = fd_close,
|
||||
.read = generic_read,
|
||||
.write = generic_write,
|
||||
.console_write = generic_console_write,
|
||||
.window_size = generic_window_size,
|
||||
.free = generic_free,
|
||||
.winch = 1,
|
||||
};
|
201
arch/um/drivers/harddog_kern.c
Normal file
201
arch/um/drivers/harddog_kern.c
Normal file
|
@ -0,0 +1,201 @@
|
|||
/* UML hardware watchdog, shamelessly stolen from:
|
||||
*
|
||||
* SoftDog 0.05: A Software Watchdog Device
|
||||
*
|
||||
* (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
|
||||
* http://www.redhat.com
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
|
||||
* warranty for any of this software. This material is provided
|
||||
* "AS-IS" and at no charge.
|
||||
*
|
||||
* (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk>
|
||||
*
|
||||
* Software only watchdog driver. Unlike its big brother the WDT501P
|
||||
* driver this won't always recover a failed machine.
|
||||
*
|
||||
* 03/96: Angelo Haritsis <ah@doc.ic.ac.uk> :
|
||||
* Modularised.
|
||||
* Added soft_margin; use upon insmod to change the timer delay.
|
||||
* NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate
|
||||
* minors.
|
||||
*
|
||||
* 19980911 Alan Cox
|
||||
* Made SMP safe for 2.3.x
|
||||
*
|
||||
* 20011127 Joel Becker (jlbec@evilplan.org>
|
||||
* Added soft_noboot; Allows testing the softdog trigger without
|
||||
* requiring a recompile.
|
||||
* Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "mconsole.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static DEFINE_MUTEX(harddog_mutex);
|
||||
static DEFINE_SPINLOCK(lock);
|
||||
static int timer_alive;
|
||||
static int harddog_in_fd = -1;
|
||||
static int harddog_out_fd = -1;
|
||||
|
||||
/*
|
||||
* Allow only one person to hold it open
|
||||
*/
|
||||
|
||||
extern int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock);
|
||||
|
||||
static int harddog_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int err = -EBUSY;
|
||||
char *sock = NULL;
|
||||
|
||||
mutex_lock(&harddog_mutex);
|
||||
spin_lock(&lock);
|
||||
if(timer_alive)
|
||||
goto err;
|
||||
#ifdef CONFIG_WATCHDOG_NOWAYOUT
|
||||
__module_get(THIS_MODULE);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MCONSOLE
|
||||
sock = mconsole_notify_socket();
|
||||
#endif
|
||||
err = start_watchdog(&harddog_in_fd, &harddog_out_fd, sock);
|
||||
if(err)
|
||||
goto err;
|
||||
|
||||
timer_alive = 1;
|
||||
spin_unlock(&lock);
|
||||
mutex_unlock(&harddog_mutex);
|
||||
return nonseekable_open(inode, file);
|
||||
err:
|
||||
spin_unlock(&lock);
|
||||
mutex_unlock(&harddog_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
extern void stop_watchdog(int in_fd, int out_fd);
|
||||
|
||||
static int harddog_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
/*
|
||||
* Shut off the timer.
|
||||
*/
|
||||
|
||||
spin_lock(&lock);
|
||||
|
||||
stop_watchdog(harddog_in_fd, harddog_out_fd);
|
||||
harddog_in_fd = -1;
|
||||
harddog_out_fd = -1;
|
||||
|
||||
timer_alive=0;
|
||||
spin_unlock(&lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int ping_watchdog(int fd);
|
||||
|
||||
static ssize_t harddog_write(struct file *file, const char __user *data, size_t len,
|
||||
loff_t *ppos)
|
||||
{
|
||||
/*
|
||||
* Refresh the timer.
|
||||
*/
|
||||
if(len)
|
||||
return ping_watchdog(harddog_out_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int harddog_ioctl_unlocked(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *argp= (void __user *)arg;
|
||||
static struct watchdog_info ident = {
|
||||
WDIOC_SETTIMEOUT,
|
||||
0,
|
||||
"UML Hardware Watchdog"
|
||||
};
|
||||
switch (cmd) {
|
||||
default:
|
||||
return -ENOTTY;
|
||||
case WDIOC_GETSUPPORT:
|
||||
if(copy_to_user(argp, &ident, sizeof(ident)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
case WDIOC_GETSTATUS:
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(0,(int __user *)argp);
|
||||
case WDIOC_KEEPALIVE:
|
||||
return ping_watchdog(harddog_out_fd);
|
||||
}
|
||||
}
|
||||
|
||||
static long harddog_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
long ret;
|
||||
|
||||
mutex_lock(&harddog_mutex);
|
||||
ret = harddog_ioctl_unlocked(file, cmd, arg);
|
||||
mutex_unlock(&harddog_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations harddog_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.write = harddog_write,
|
||||
.unlocked_ioctl = harddog_ioctl,
|
||||
.open = harddog_open,
|
||||
.release = harddog_release,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static struct miscdevice harddog_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &harddog_fops,
|
||||
};
|
||||
|
||||
static char banner[] __initdata = KERN_INFO "UML Watchdog Timer\n";
|
||||
|
||||
static int __init harddog_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = misc_register(&harddog_miscdev);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
printk(banner);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit harddog_exit(void)
|
||||
{
|
||||
misc_deregister(&harddog_miscdev);
|
||||
}
|
||||
|
||||
module_init(harddog_init);
|
||||
module_exit(harddog_exit);
|
127
arch/um/drivers/harddog_user.c
Normal file
127
arch/um/drivers/harddog_user.c
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <os.h>
|
||||
|
||||
struct dog_data {
|
||||
int stdin;
|
||||
int stdout;
|
||||
int close_me[2];
|
||||
};
|
||||
|
||||
static void pre_exec(void *d)
|
||||
{
|
||||
struct dog_data *data = d;
|
||||
|
||||
dup2(data->stdin, 0);
|
||||
dup2(data->stdout, 1);
|
||||
dup2(data->stdout, 2);
|
||||
close(data->stdin);
|
||||
close(data->stdout);
|
||||
close(data->close_me[0]);
|
||||
close(data->close_me[1]);
|
||||
}
|
||||
|
||||
int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock)
|
||||
{
|
||||
struct dog_data data;
|
||||
int in_fds[2], out_fds[2], pid, n, err;
|
||||
char pid_buf[sizeof("nnnnnnn\0")], c;
|
||||
char *pid_args[] = { "/usr/bin/uml_watchdog", "-pid", pid_buf, NULL };
|
||||
char *mconsole_args[] = { "/usr/bin/uml_watchdog", "-mconsole", NULL,
|
||||
NULL };
|
||||
char **args = NULL;
|
||||
|
||||
err = os_pipe(in_fds, 1, 0);
|
||||
if (err < 0) {
|
||||
printk("harddog_open - os_pipe failed, err = %d\n", -err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = os_pipe(out_fds, 1, 0);
|
||||
if (err < 0) {
|
||||
printk("harddog_open - os_pipe failed, err = %d\n", -err);
|
||||
goto out_close_in;
|
||||
}
|
||||
|
||||
data.stdin = out_fds[0];
|
||||
data.stdout = in_fds[1];
|
||||
data.close_me[0] = out_fds[1];
|
||||
data.close_me[1] = in_fds[0];
|
||||
|
||||
if (sock != NULL) {
|
||||
mconsole_args[2] = sock;
|
||||
args = mconsole_args;
|
||||
}
|
||||
else {
|
||||
/* XXX The os_getpid() is not SMP correct */
|
||||
sprintf(pid_buf, "%d", os_getpid());
|
||||
args = pid_args;
|
||||
}
|
||||
|
||||
pid = run_helper(pre_exec, &data, args);
|
||||
|
||||
close(out_fds[0]);
|
||||
close(in_fds[1]);
|
||||
|
||||
if (pid < 0) {
|
||||
err = -pid;
|
||||
printk("harddog_open - run_helper failed, errno = %d\n", -err);
|
||||
goto out_close_out;
|
||||
}
|
||||
|
||||
n = read(in_fds[0], &c, sizeof(c));
|
||||
if (n == 0) {
|
||||
printk("harddog_open - EOF on watchdog pipe\n");
|
||||
helper_wait(pid);
|
||||
err = -EIO;
|
||||
goto out_close_out;
|
||||
}
|
||||
else if (n < 0) {
|
||||
printk("harddog_open - read of watchdog pipe failed, "
|
||||
"err = %d\n", errno);
|
||||
helper_wait(pid);
|
||||
err = n;
|
||||
goto out_close_out;
|
||||
}
|
||||
*in_fd_ret = in_fds[0];
|
||||
*out_fd_ret = out_fds[1];
|
||||
return 0;
|
||||
|
||||
out_close_in:
|
||||
close(in_fds[0]);
|
||||
close(in_fds[1]);
|
||||
out_close_out:
|
||||
close(out_fds[0]);
|
||||
close(out_fds[1]);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
void stop_watchdog(int in_fd, int out_fd)
|
||||
{
|
||||
close(in_fd);
|
||||
close(out_fd);
|
||||
}
|
||||
|
||||
int ping_watchdog(int fd)
|
||||
{
|
||||
int n;
|
||||
char c = '\n';
|
||||
|
||||
n = write(fd, &c, sizeof(c));
|
||||
if (n != sizeof(c)) {
|
||||
printk("ping_watchdog - write failed, ret = %d, err = %d\n",
|
||||
n, errno);
|
||||
if (n < 0)
|
||||
return n;
|
||||
return -EIO;
|
||||
}
|
||||
return 1;
|
||||
|
||||
}
|
358
arch/um/drivers/hostaudio_kern.c
Normal file
358
arch/um/drivers/hostaudio_kern.c
Normal file
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
* Copyright (C) 2002 Steve Schmidtke
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sound.h>
|
||||
#include <linux/soundcard.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <init.h>
|
||||
#include <os.h>
|
||||
|
||||
struct hostaudio_state {
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct hostmixer_state {
|
||||
int fd;
|
||||
};
|
||||
|
||||
#define HOSTAUDIO_DEV_DSP "/dev/sound/dsp"
|
||||
#define HOSTAUDIO_DEV_MIXER "/dev/sound/mixer"
|
||||
|
||||
/*
|
||||
* Changed either at boot time or module load time. At boot, this is
|
||||
* single-threaded; at module load, multiple modules would each have
|
||||
* their own copy of these variables.
|
||||
*/
|
||||
static char *dsp = HOSTAUDIO_DEV_DSP;
|
||||
static char *mixer = HOSTAUDIO_DEV_MIXER;
|
||||
|
||||
#define DSP_HELP \
|
||||
" This is used to specify the host dsp device to the hostaudio driver.\n" \
|
||||
" The default is \"" HOSTAUDIO_DEV_DSP "\".\n\n"
|
||||
|
||||
#define MIXER_HELP \
|
||||
" This is used to specify the host mixer device to the hostaudio driver.\n"\
|
||||
" The default is \"" HOSTAUDIO_DEV_MIXER "\".\n\n"
|
||||
|
||||
module_param(dsp, charp, 0644);
|
||||
MODULE_PARM_DESC(dsp, DSP_HELP);
|
||||
module_param(mixer, charp, 0644);
|
||||
MODULE_PARM_DESC(mixer, MIXER_HELP);
|
||||
|
||||
#ifndef MODULE
|
||||
static int set_dsp(char *name, int *add)
|
||||
{
|
||||
dsp = name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("dsp=", set_dsp, "dsp=<dsp device>\n" DSP_HELP);
|
||||
|
||||
static int set_mixer(char *name, int *add)
|
||||
{
|
||||
mixer = name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("mixer=", set_mixer, "mixer=<mixer device>\n" MIXER_HELP);
|
||||
#endif
|
||||
|
||||
static DEFINE_MUTEX(hostaudio_mutex);
|
||||
|
||||
/* /dev/dsp file operations */
|
||||
|
||||
static ssize_t hostaudio_read(struct file *file, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hostaudio_state *state = file->private_data;
|
||||
void *kbuf;
|
||||
int err;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "hostaudio: read called, count = %d\n", count);
|
||||
#endif
|
||||
|
||||
kbuf = kmalloc(count, GFP_KERNEL);
|
||||
if (kbuf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
err = os_read_file(state->fd, kbuf, count);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
if (copy_to_user(buffer, kbuf, err))
|
||||
err = -EFAULT;
|
||||
|
||||
out:
|
||||
kfree(kbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t hostaudio_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hostaudio_state *state = file->private_data;
|
||||
void *kbuf;
|
||||
int err;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "hostaudio: write called, count = %d\n", count);
|
||||
#endif
|
||||
|
||||
kbuf = kmalloc(count, GFP_KERNEL);
|
||||
if (kbuf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
err = -EFAULT;
|
||||
if (copy_from_user(kbuf, buffer, count))
|
||||
goto out;
|
||||
|
||||
err = os_write_file(state->fd, kbuf, count);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
*ppos += err;
|
||||
|
||||
out:
|
||||
kfree(kbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static unsigned int hostaudio_poll(struct file *file,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "hostaudio: poll called (unimplemented)\n");
|
||||
#endif
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static long hostaudio_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct hostaudio_state *state = file->private_data;
|
||||
unsigned long data = 0;
|
||||
int err;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "hostaudio: ioctl called, cmd = %u\n", cmd);
|
||||
#endif
|
||||
switch(cmd){
|
||||
case SNDCTL_DSP_SPEED:
|
||||
case SNDCTL_DSP_STEREO:
|
||||
case SNDCTL_DSP_GETBLKSIZE:
|
||||
case SNDCTL_DSP_CHANNELS:
|
||||
case SNDCTL_DSP_SUBDIVIDE:
|
||||
case SNDCTL_DSP_SETFRAGMENT:
|
||||
if (get_user(data, (int __user *) arg))
|
||||
return -EFAULT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
err = os_ioctl_generic(state->fd, cmd, (unsigned long) &data);
|
||||
|
||||
switch(cmd){
|
||||
case SNDCTL_DSP_SPEED:
|
||||
case SNDCTL_DSP_STEREO:
|
||||
case SNDCTL_DSP_GETBLKSIZE:
|
||||
case SNDCTL_DSP_CHANNELS:
|
||||
case SNDCTL_DSP_SUBDIVIDE:
|
||||
case SNDCTL_DSP_SETFRAGMENT:
|
||||
if (put_user(data, (int __user *) arg))
|
||||
return -EFAULT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hostaudio_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hostaudio_state *state;
|
||||
int r = 0, w = 0;
|
||||
int ret;
|
||||
|
||||
#ifdef DEBUG
|
||||
kparam_block_sysfs_write(dsp);
|
||||
printk(KERN_DEBUG "hostaudio: open called (host: %s)\n", dsp);
|
||||
kparam_unblock_sysfs_write(dsp);
|
||||
#endif
|
||||
|
||||
state = kmalloc(sizeof(struct hostaudio_state), GFP_KERNEL);
|
||||
if (state == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (file->f_mode & FMODE_READ)
|
||||
r = 1;
|
||||
if (file->f_mode & FMODE_WRITE)
|
||||
w = 1;
|
||||
|
||||
kparam_block_sysfs_write(dsp);
|
||||
mutex_lock(&hostaudio_mutex);
|
||||
ret = os_open_file(dsp, of_set_rw(OPENFLAGS(), r, w), 0);
|
||||
mutex_unlock(&hostaudio_mutex);
|
||||
kparam_unblock_sysfs_write(dsp);
|
||||
|
||||
if (ret < 0) {
|
||||
kfree(state);
|
||||
return ret;
|
||||
}
|
||||
state->fd = ret;
|
||||
file->private_data = state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hostaudio_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hostaudio_state *state = file->private_data;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "hostaudio: release called\n");
|
||||
#endif
|
||||
os_close_file(state->fd);
|
||||
kfree(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* /dev/mixer file operations */
|
||||
|
||||
static long hostmixer_ioctl_mixdev(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct hostmixer_state *state = file->private_data;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "hostmixer: ioctl called\n");
|
||||
#endif
|
||||
|
||||
return os_ioctl_generic(state->fd, cmd, arg);
|
||||
}
|
||||
|
||||
static int hostmixer_open_mixdev(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hostmixer_state *state;
|
||||
int r = 0, w = 0;
|
||||
int ret;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "hostmixer: open called (host: %s)\n", mixer);
|
||||
#endif
|
||||
|
||||
state = kmalloc(sizeof(struct hostmixer_state), GFP_KERNEL);
|
||||
if (state == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (file->f_mode & FMODE_READ)
|
||||
r = 1;
|
||||
if (file->f_mode & FMODE_WRITE)
|
||||
w = 1;
|
||||
|
||||
kparam_block_sysfs_write(mixer);
|
||||
mutex_lock(&hostaudio_mutex);
|
||||
ret = os_open_file(mixer, of_set_rw(OPENFLAGS(), r, w), 0);
|
||||
mutex_unlock(&hostaudio_mutex);
|
||||
kparam_unblock_sysfs_write(mixer);
|
||||
|
||||
if (ret < 0) {
|
||||
kparam_block_sysfs_write(dsp);
|
||||
printk(KERN_ERR "hostaudio_open_mixdev failed to open '%s', "
|
||||
"err = %d\n", dsp, -ret);
|
||||
kparam_unblock_sysfs_write(dsp);
|
||||
kfree(state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
file->private_data = state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hostmixer_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hostmixer_state *state = file->private_data;
|
||||
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "hostmixer: release called\n");
|
||||
#endif
|
||||
|
||||
os_close_file(state->fd);
|
||||
kfree(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* kernel module operations */
|
||||
|
||||
static const struct file_operations hostaudio_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = hostaudio_read,
|
||||
.write = hostaudio_write,
|
||||
.poll = hostaudio_poll,
|
||||
.unlocked_ioctl = hostaudio_ioctl,
|
||||
.mmap = NULL,
|
||||
.open = hostaudio_open,
|
||||
.release = hostaudio_release,
|
||||
};
|
||||
|
||||
static const struct file_operations hostmixer_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.unlocked_ioctl = hostmixer_ioctl_mixdev,
|
||||
.open = hostmixer_open_mixdev,
|
||||
.release = hostmixer_release,
|
||||
};
|
||||
|
||||
struct {
|
||||
int dev_audio;
|
||||
int dev_mixer;
|
||||
} module_data;
|
||||
|
||||
MODULE_AUTHOR("Steve Schmidtke");
|
||||
MODULE_DESCRIPTION("UML Audio Relay");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int __init hostaudio_init_module(void)
|
||||
{
|
||||
__kernel_param_lock();
|
||||
printk(KERN_INFO "UML Audio Relay (host dsp = %s, host mixer = %s)\n",
|
||||
dsp, mixer);
|
||||
__kernel_param_unlock();
|
||||
|
||||
module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1);
|
||||
if (module_data.dev_audio < 0) {
|
||||
printk(KERN_ERR "hostaudio: couldn't register DSP device!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
module_data.dev_mixer = register_sound_mixer(&hostmixer_fops, -1);
|
||||
if (module_data.dev_mixer < 0) {
|
||||
printk(KERN_ERR "hostmixer: couldn't register mixer "
|
||||
"device!\n");
|
||||
unregister_sound_dsp(module_data.dev_audio);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit hostaudio_cleanup_module (void)
|
||||
{
|
||||
unregister_sound_mixer(module_data.dev_mixer);
|
||||
unregister_sound_dsp(module_data.dev_audio);
|
||||
}
|
||||
|
||||
module_init(hostaudio_init_module);
|
||||
module_exit(hostaudio_cleanup_module);
|
764
arch/um/drivers/line.c
Normal file
764
arch/um/drivers/line.c
Normal file
|
@ -0,0 +1,764 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/kd.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include "chan.h"
|
||||
#include <irq_kern.h>
|
||||
#include <irq_user.h>
|
||||
#include <kern_util.h>
|
||||
#include <os.h>
|
||||
|
||||
#define LINE_BUFSIZE 4096
|
||||
|
||||
static irqreturn_t line_interrupt(int irq, void *data)
|
||||
{
|
||||
struct chan *chan = data;
|
||||
struct line *line = chan->line;
|
||||
|
||||
if (line)
|
||||
chan_interrupt(line, irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the free space inside the ring buffer of this line.
|
||||
*
|
||||
* Should be called while holding line->lock (this does not modify data).
|
||||
*/
|
||||
static int write_room(struct line *line)
|
||||
{
|
||||
int n;
|
||||
|
||||
if (line->buffer == NULL)
|
||||
return LINE_BUFSIZE - 1;
|
||||
|
||||
/* This is for the case where the buffer is wrapped! */
|
||||
n = line->head - line->tail;
|
||||
|
||||
if (n <= 0)
|
||||
n += LINE_BUFSIZE; /* The other case */
|
||||
return n - 1;
|
||||
}
|
||||
|
||||
int line_write_room(struct tty_struct *tty)
|
||||
{
|
||||
struct line *line = tty->driver_data;
|
||||
unsigned long flags;
|
||||
int room;
|
||||
|
||||
spin_lock_irqsave(&line->lock, flags);
|
||||
room = write_room(line);
|
||||
spin_unlock_irqrestore(&line->lock, flags);
|
||||
|
||||
return room;
|
||||
}
|
||||
|
||||
int line_chars_in_buffer(struct tty_struct *tty)
|
||||
{
|
||||
struct line *line = tty->driver_data;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&line->lock, flags);
|
||||
/* write_room subtracts 1 for the needed NULL, so we readd it.*/
|
||||
ret = LINE_BUFSIZE - (write_room(line) + 1);
|
||||
spin_unlock_irqrestore(&line->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This copies the content of buf into the circular buffer associated with
|
||||
* this line.
|
||||
* The return value is the number of characters actually copied, i.e. the ones
|
||||
* for which there was space: this function is not supposed to ever flush out
|
||||
* the circular buffer.
|
||||
*
|
||||
* Must be called while holding line->lock!
|
||||
*/
|
||||
static int buffer_data(struct line *line, const char *buf, int len)
|
||||
{
|
||||
int end, room;
|
||||
|
||||
if (line->buffer == NULL) {
|
||||
line->buffer = kmalloc(LINE_BUFSIZE, GFP_ATOMIC);
|
||||
if (line->buffer == NULL) {
|
||||
printk(KERN_ERR "buffer_data - atomic allocation "
|
||||
"failed\n");
|
||||
return 0;
|
||||
}
|
||||
line->head = line->buffer;
|
||||
line->tail = line->buffer;
|
||||
}
|
||||
|
||||
room = write_room(line);
|
||||
len = (len > room) ? room : len;
|
||||
|
||||
end = line->buffer + LINE_BUFSIZE - line->tail;
|
||||
|
||||
if (len < end) {
|
||||
memcpy(line->tail, buf, len);
|
||||
line->tail += len;
|
||||
}
|
||||
else {
|
||||
/* The circular buffer is wrapping */
|
||||
memcpy(line->tail, buf, end);
|
||||
buf += end;
|
||||
memcpy(line->buffer, buf, len - end);
|
||||
line->tail = line->buffer + len - end;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flushes the ring buffer to the output channels. That is, write_chan is
|
||||
* called, passing it line->head as buffer, and an appropriate count.
|
||||
*
|
||||
* On exit, returns 1 when the buffer is empty,
|
||||
* 0 when the buffer is not empty on exit,
|
||||
* and -errno when an error occurred.
|
||||
*
|
||||
* Must be called while holding line->lock!*/
|
||||
static int flush_buffer(struct line *line)
|
||||
{
|
||||
int n, count;
|
||||
|
||||
if ((line->buffer == NULL) || (line->head == line->tail))
|
||||
return 1;
|
||||
|
||||
if (line->tail < line->head) {
|
||||
/* line->buffer + LINE_BUFSIZE is the end of the buffer! */
|
||||
count = line->buffer + LINE_BUFSIZE - line->head;
|
||||
|
||||
n = write_chan(line->chan_out, line->head, count,
|
||||
line->driver->write_irq);
|
||||
if (n < 0)
|
||||
return n;
|
||||
if (n == count) {
|
||||
/*
|
||||
* We have flushed from ->head to buffer end, now we
|
||||
* must flush only from the beginning to ->tail.
|
||||
*/
|
||||
line->head = line->buffer;
|
||||
} else {
|
||||
line->head += n;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
count = line->tail - line->head;
|
||||
n = write_chan(line->chan_out, line->head, count,
|
||||
line->driver->write_irq);
|
||||
|
||||
if (n < 0)
|
||||
return n;
|
||||
|
||||
line->head += n;
|
||||
return line->head == line->tail;
|
||||
}
|
||||
|
||||
void line_flush_buffer(struct tty_struct *tty)
|
||||
{
|
||||
struct line *line = tty->driver_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&line->lock, flags);
|
||||
flush_buffer(line);
|
||||
spin_unlock_irqrestore(&line->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* We map both ->flush_chars and ->put_char (which go in pair) onto
|
||||
* ->flush_buffer and ->write. Hope it's not that bad.
|
||||
*/
|
||||
void line_flush_chars(struct tty_struct *tty)
|
||||
{
|
||||
line_flush_buffer(tty);
|
||||
}
|
||||
|
||||
int line_put_char(struct tty_struct *tty, unsigned char ch)
|
||||
{
|
||||
return line_write(tty, &ch, sizeof(ch));
|
||||
}
|
||||
|
||||
int line_write(struct tty_struct *tty, const unsigned char *buf, int len)
|
||||
{
|
||||
struct line *line = tty->driver_data;
|
||||
unsigned long flags;
|
||||
int n, ret = 0;
|
||||
|
||||
spin_lock_irqsave(&line->lock, flags);
|
||||
if (line->head != line->tail)
|
||||
ret = buffer_data(line, buf, len);
|
||||
else {
|
||||
n = write_chan(line->chan_out, buf, len,
|
||||
line->driver->write_irq);
|
||||
if (n < 0) {
|
||||
ret = n;
|
||||
goto out_up;
|
||||
}
|
||||
|
||||
len -= n;
|
||||
ret += n;
|
||||
if (len > 0)
|
||||
ret += buffer_data(line, buf + n, len);
|
||||
}
|
||||
out_up:
|
||||
spin_unlock_irqrestore(&line->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void line_set_termios(struct tty_struct *tty, struct ktermios * old)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
void line_throttle(struct tty_struct *tty)
|
||||
{
|
||||
struct line *line = tty->driver_data;
|
||||
|
||||
deactivate_chan(line->chan_in, line->driver->read_irq);
|
||||
line->throttled = 1;
|
||||
}
|
||||
|
||||
void line_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct line *line = tty->driver_data;
|
||||
|
||||
line->throttled = 0;
|
||||
chan_interrupt(line, line->driver->read_irq);
|
||||
|
||||
/*
|
||||
* Maybe there is enough stuff pending that calling the interrupt
|
||||
* throttles us again. In this case, line->throttled will be 1
|
||||
* again and we shouldn't turn the interrupt back on.
|
||||
*/
|
||||
if (!line->throttled)
|
||||
reactivate_chan(line->chan_in, line->driver->read_irq);
|
||||
}
|
||||
|
||||
static irqreturn_t line_write_interrupt(int irq, void *data)
|
||||
{
|
||||
struct chan *chan = data;
|
||||
struct line *line = chan->line;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Interrupts are disabled here because genirq keep irqs disabled when
|
||||
* calling the action handler.
|
||||
*/
|
||||
|
||||
spin_lock(&line->lock);
|
||||
err = flush_buffer(line);
|
||||
if (err == 0) {
|
||||
spin_unlock(&line->lock);
|
||||
return IRQ_NONE;
|
||||
} else if (err < 0) {
|
||||
line->head = line->buffer;
|
||||
line->tail = line->buffer;
|
||||
}
|
||||
spin_unlock(&line->lock);
|
||||
|
||||
tty_port_tty_wakeup(&line->port);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
|
||||
{
|
||||
const struct line_driver *driver = line->driver;
|
||||
int err = 0;
|
||||
|
||||
if (input)
|
||||
err = um_request_irq(driver->read_irq, fd, IRQ_READ,
|
||||
line_interrupt, IRQF_SHARED,
|
||||
driver->read_irq_name, data);
|
||||
if (err)
|
||||
return err;
|
||||
if (output)
|
||||
err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
|
||||
line_write_interrupt, IRQF_SHARED,
|
||||
driver->write_irq_name, data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int line_activate(struct tty_port *port, struct tty_struct *tty)
|
||||
{
|
||||
int ret;
|
||||
struct line *line = tty->driver_data;
|
||||
|
||||
ret = enable_chan(line);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!line->sigio) {
|
||||
chan_enable_winch(line->chan_out, port);
|
||||
line->sigio = 1;
|
||||
}
|
||||
|
||||
chan_window_size(line, &tty->winsize.ws_row,
|
||||
&tty->winsize.ws_col);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unregister_winch(struct tty_struct *tty);
|
||||
|
||||
static void line_destruct(struct tty_port *port)
|
||||
{
|
||||
struct tty_struct *tty = tty_port_tty_get(port);
|
||||
struct line *line = tty->driver_data;
|
||||
|
||||
if (line->sigio) {
|
||||
unregister_winch(tty);
|
||||
line->sigio = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct tty_port_operations line_port_ops = {
|
||||
.activate = line_activate,
|
||||
.destruct = line_destruct,
|
||||
};
|
||||
|
||||
int line_open(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct line *line = tty->driver_data;
|
||||
|
||||
return tty_port_open(&line->port, tty, filp);
|
||||
}
|
||||
|
||||
int line_install(struct tty_driver *driver, struct tty_struct *tty,
|
||||
struct line *line)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = tty_standard_install(driver, tty);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tty->driver_data = line;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void line_close(struct tty_struct *tty, struct file * filp)
|
||||
{
|
||||
struct line *line = tty->driver_data;
|
||||
|
||||
tty_port_close(&line->port, tty, filp);
|
||||
}
|
||||
|
||||
void line_hangup(struct tty_struct *tty)
|
||||
{
|
||||
struct line *line = tty->driver_data;
|
||||
|
||||
tty_port_hangup(&line->port);
|
||||
}
|
||||
|
||||
void close_lines(struct line *lines, int nlines)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < nlines; i++)
|
||||
close_chan(&lines[i]);
|
||||
}
|
||||
|
||||
int setup_one_line(struct line *lines, int n, char *init,
|
||||
const struct chan_opts *opts, char **error_out)
|
||||
{
|
||||
struct line *line = &lines[n];
|
||||
struct tty_driver *driver = line->driver->driver;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (line->port.count) {
|
||||
*error_out = "Device is already open";
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!strcmp(init, "none")) {
|
||||
if (line->valid) {
|
||||
line->valid = 0;
|
||||
kfree(line->init_str);
|
||||
tty_unregister_device(driver, n);
|
||||
parse_chan_pair(NULL, line, n, opts, error_out);
|
||||
err = 0;
|
||||
}
|
||||
} else {
|
||||
char *new = kstrdup(init, GFP_KERNEL);
|
||||
if (!new) {
|
||||
*error_out = "Failed to allocate memory";
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (line->valid) {
|
||||
tty_unregister_device(driver, n);
|
||||
kfree(line->init_str);
|
||||
}
|
||||
line->init_str = new;
|
||||
line->valid = 1;
|
||||
err = parse_chan_pair(new, line, n, opts, error_out);
|
||||
if (!err) {
|
||||
struct device *d = tty_port_register_device(&line->port,
|
||||
driver, n, NULL);
|
||||
if (IS_ERR(d)) {
|
||||
*error_out = "Failed to register device";
|
||||
err = PTR_ERR(d);
|
||||
parse_chan_pair(NULL, line, n, opts, error_out);
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
line->init_str = NULL;
|
||||
line->valid = 0;
|
||||
kfree(new);
|
||||
}
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common setup code for both startup command line and mconsole initialization.
|
||||
* @lines contains the array (of size @num) to modify;
|
||||
* @init is the setup string;
|
||||
* @error_out is an error string in the case of failure;
|
||||
*/
|
||||
|
||||
int line_setup(char **conf, unsigned int num, char **def,
|
||||
char *init, char *name)
|
||||
{
|
||||
char *error;
|
||||
|
||||
if (*init == '=') {
|
||||
/*
|
||||
* We said con=/ssl= instead of con#=, so we are configuring all
|
||||
* consoles at once.
|
||||
*/
|
||||
*def = init + 1;
|
||||
} else {
|
||||
char *end;
|
||||
unsigned n = simple_strtoul(init, &end, 0);
|
||||
|
||||
if (*end != '=') {
|
||||
error = "Couldn't parse device number";
|
||||
goto out;
|
||||
}
|
||||
if (n >= num) {
|
||||
error = "Device number out of range";
|
||||
goto out;
|
||||
}
|
||||
conf[n] = end + 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
out:
|
||||
printk(KERN_ERR "Failed to set up %s with "
|
||||
"configuration string \"%s\" : %s\n", name, init, error);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int line_config(struct line *lines, unsigned int num, char *str,
|
||||
const struct chan_opts *opts, char **error_out)
|
||||
{
|
||||
char *end;
|
||||
int n;
|
||||
|
||||
if (*str == '=') {
|
||||
*error_out = "Can't configure all devices from mconsole";
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
n = simple_strtoul(str, &end, 0);
|
||||
if (*end++ != '=') {
|
||||
*error_out = "Couldn't parse device number";
|
||||
return -EINVAL;
|
||||
}
|
||||
if (n >= num) {
|
||||
*error_out = "Device number out of range";
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return setup_one_line(lines, n, end, opts, error_out);
|
||||
}
|
||||
|
||||
int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
|
||||
int size, char **error_out)
|
||||
{
|
||||
struct line *line;
|
||||
char *end;
|
||||
int dev, n = 0;
|
||||
|
||||
dev = simple_strtoul(name, &end, 0);
|
||||
if ((*end != '\0') || (end == name)) {
|
||||
*error_out = "line_get_config failed to parse device number";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((dev < 0) || (dev >= num)) {
|
||||
*error_out = "device number out of range";
|
||||
return 0;
|
||||
}
|
||||
|
||||
line = &lines[dev];
|
||||
|
||||
if (!line->valid)
|
||||
CONFIG_CHUNK(str, size, n, "none", 1);
|
||||
else {
|
||||
struct tty_struct *tty = tty_port_tty_get(&line->port);
|
||||
if (tty == NULL) {
|
||||
CONFIG_CHUNK(str, size, n, line->init_str, 1);
|
||||
} else {
|
||||
n = chan_config_string(line, str, size, error_out);
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int line_id(char **str, int *start_out, int *end_out)
|
||||
{
|
||||
char *end;
|
||||
int n;
|
||||
|
||||
n = simple_strtoul(*str, &end, 0);
|
||||
if ((*end != '\0') || (end == *str))
|
||||
return -1;
|
||||
|
||||
*str = end;
|
||||
*start_out = n;
|
||||
*end_out = n;
|
||||
return n;
|
||||
}
|
||||
|
||||
int line_remove(struct line *lines, unsigned int num, int n, char **error_out)
|
||||
{
|
||||
if (n >= num) {
|
||||
*error_out = "Device number out of range";
|
||||
return -EINVAL;
|
||||
}
|
||||
return setup_one_line(lines, n, "none", NULL, error_out);
|
||||
}
|
||||
|
||||
int register_lines(struct line_driver *line_driver,
|
||||
const struct tty_operations *ops,
|
||||
struct line *lines, int nlines)
|
||||
{
|
||||
struct tty_driver *driver = alloc_tty_driver(nlines);
|
||||
int err;
|
||||
int i;
|
||||
|
||||
if (!driver)
|
||||
return -ENOMEM;
|
||||
|
||||
driver->driver_name = line_driver->name;
|
||||
driver->name = line_driver->device_name;
|
||||
driver->major = line_driver->major;
|
||||
driver->minor_start = line_driver->minor_start;
|
||||
driver->type = line_driver->type;
|
||||
driver->subtype = line_driver->subtype;
|
||||
driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
||||
driver->init_termios = tty_std_termios;
|
||||
|
||||
for (i = 0; i < nlines; i++) {
|
||||
tty_port_init(&lines[i].port);
|
||||
lines[i].port.ops = &line_port_ops;
|
||||
spin_lock_init(&lines[i].lock);
|
||||
lines[i].driver = line_driver;
|
||||
INIT_LIST_HEAD(&lines[i].chan_list);
|
||||
}
|
||||
tty_set_operations(driver, ops);
|
||||
|
||||
err = tty_register_driver(driver);
|
||||
if (err) {
|
||||
printk(KERN_ERR "register_lines : can't register %s driver\n",
|
||||
line_driver->name);
|
||||
put_tty_driver(driver);
|
||||
for (i = 0; i < nlines; i++)
|
||||
tty_port_destroy(&lines[i].port);
|
||||
return err;
|
||||
}
|
||||
|
||||
line_driver->driver = driver;
|
||||
mconsole_register_dev(&line_driver->mc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(winch_handler_lock);
|
||||
static LIST_HEAD(winch_handlers);
|
||||
|
||||
struct winch {
|
||||
struct list_head list;
|
||||
int fd;
|
||||
int tty_fd;
|
||||
int pid;
|
||||
struct tty_port *port;
|
||||
unsigned long stack;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
static void __free_winch(struct work_struct *work)
|
||||
{
|
||||
struct winch *winch = container_of(work, struct winch, work);
|
||||
um_free_irq(WINCH_IRQ, winch);
|
||||
|
||||
if (winch->pid != -1)
|
||||
os_kill_process(winch->pid, 1);
|
||||
if (winch->stack != 0)
|
||||
free_stack(winch->stack, 0);
|
||||
kfree(winch);
|
||||
}
|
||||
|
||||
static void free_winch(struct winch *winch)
|
||||
{
|
||||
int fd = winch->fd;
|
||||
winch->fd = -1;
|
||||
if (fd != -1)
|
||||
os_close_file(fd);
|
||||
list_del(&winch->list);
|
||||
__free_winch(&winch->work);
|
||||
}
|
||||
|
||||
static irqreturn_t winch_interrupt(int irq, void *data)
|
||||
{
|
||||
struct winch *winch = data;
|
||||
struct tty_struct *tty;
|
||||
struct line *line;
|
||||
int fd = winch->fd;
|
||||
int err;
|
||||
char c;
|
||||
|
||||
if (fd != -1) {
|
||||
err = generic_read(fd, &c, NULL);
|
||||
if (err < 0) {
|
||||
if (err != -EAGAIN) {
|
||||
winch->fd = -1;
|
||||
list_del(&winch->list);
|
||||
os_close_file(fd);
|
||||
printk(KERN_ERR "winch_interrupt : "
|
||||
"read failed, errno = %d\n", -err);
|
||||
printk(KERN_ERR "fd %d is losing SIGWINCH "
|
||||
"support\n", winch->tty_fd);
|
||||
INIT_WORK(&winch->work, __free_winch);
|
||||
schedule_work(&winch->work);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
tty = tty_port_tty_get(winch->port);
|
||||
if (tty != NULL) {
|
||||
line = tty->driver_data;
|
||||
if (line != NULL) {
|
||||
chan_window_size(line, &tty->winsize.ws_row,
|
||||
&tty->winsize.ws_col);
|
||||
kill_pgrp(tty->pgrp, SIGWINCH, 1);
|
||||
}
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
out:
|
||||
if (winch->fd != -1)
|
||||
reactivate_fd(winch->fd, WINCH_IRQ);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void register_winch_irq(int fd, int tty_fd, int pid, struct tty_port *port,
|
||||
unsigned long stack)
|
||||
{
|
||||
struct winch *winch;
|
||||
|
||||
winch = kmalloc(sizeof(*winch), GFP_KERNEL);
|
||||
if (winch == NULL) {
|
||||
printk(KERN_ERR "register_winch_irq - kmalloc failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*winch = ((struct winch) { .list = LIST_HEAD_INIT(winch->list),
|
||||
.fd = fd,
|
||||
.tty_fd = tty_fd,
|
||||
.pid = pid,
|
||||
.port = port,
|
||||
.stack = stack });
|
||||
|
||||
if (um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt,
|
||||
IRQF_SHARED, "winch", winch) < 0) {
|
||||
printk(KERN_ERR "register_winch_irq - failed to register "
|
||||
"IRQ\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
spin_lock(&winch_handler_lock);
|
||||
list_add(&winch->list, &winch_handlers);
|
||||
spin_unlock(&winch_handler_lock);
|
||||
|
||||
return;
|
||||
|
||||
out_free:
|
||||
kfree(winch);
|
||||
cleanup:
|
||||
os_kill_process(pid, 1);
|
||||
os_close_file(fd);
|
||||
if (stack != 0)
|
||||
free_stack(stack, 0);
|
||||
}
|
||||
|
||||
static void unregister_winch(struct tty_struct *tty)
|
||||
{
|
||||
struct list_head *ele, *next;
|
||||
struct winch *winch;
|
||||
struct tty_struct *wtty;
|
||||
|
||||
spin_lock(&winch_handler_lock);
|
||||
|
||||
list_for_each_safe(ele, next, &winch_handlers) {
|
||||
winch = list_entry(ele, struct winch, list);
|
||||
wtty = tty_port_tty_get(winch->port);
|
||||
if (wtty == tty) {
|
||||
free_winch(winch);
|
||||
break;
|
||||
}
|
||||
tty_kref_put(wtty);
|
||||
}
|
||||
spin_unlock(&winch_handler_lock);
|
||||
}
|
||||
|
||||
static void winch_cleanup(void)
|
||||
{
|
||||
struct list_head *ele, *next;
|
||||
struct winch *winch;
|
||||
|
||||
spin_lock(&winch_handler_lock);
|
||||
|
||||
list_for_each_safe(ele, next, &winch_handlers) {
|
||||
winch = list_entry(ele, struct winch, list);
|
||||
free_winch(winch);
|
||||
}
|
||||
|
||||
spin_unlock(&winch_handler_lock);
|
||||
}
|
||||
__uml_exitcall(winch_cleanup);
|
||||
|
||||
char *add_xterm_umid(char *base)
|
||||
{
|
||||
char *umid, *title;
|
||||
int len;
|
||||
|
||||
umid = get_umid();
|
||||
if (*umid == '\0')
|
||||
return base;
|
||||
|
||||
len = strlen(base) + strlen(" ()") + strlen(umid) + 1;
|
||||
title = kmalloc(len, GFP_KERNEL);
|
||||
if (title == NULL) {
|
||||
printk(KERN_ERR "Failed to allocate buffer for xterm title\n");
|
||||
return base;
|
||||
}
|
||||
|
||||
snprintf(title, len, "%s (%s)", base, umid);
|
||||
return title;
|
||||
}
|
99
arch/um/drivers/line.h
Normal file
99
arch/um/drivers/line.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __LINE_H__
|
||||
#define __LINE_H__
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include "chan_user.h"
|
||||
#include "mconsole_kern.h"
|
||||
|
||||
/* There's only two modifiable fields in this - .mc.list and .driver */
|
||||
struct line_driver {
|
||||
const char *name;
|
||||
const char *device_name;
|
||||
const short major;
|
||||
const short minor_start;
|
||||
const short type;
|
||||
const short subtype;
|
||||
const int read_irq;
|
||||
const char *read_irq_name;
|
||||
const int write_irq;
|
||||
const char *write_irq_name;
|
||||
struct mc_device mc;
|
||||
struct tty_driver *driver;
|
||||
};
|
||||
|
||||
struct line {
|
||||
struct tty_port port;
|
||||
int valid;
|
||||
|
||||
char *init_str;
|
||||
struct list_head chan_list;
|
||||
struct chan *chan_in, *chan_out;
|
||||
|
||||
/*This lock is actually, mostly, local to*/
|
||||
spinlock_t lock;
|
||||
int throttled;
|
||||
/* Yes, this is a real circular buffer.
|
||||
* XXX: And this should become a struct kfifo!
|
||||
*
|
||||
* buffer points to a buffer allocated on demand, of length
|
||||
* LINE_BUFSIZE, head to the start of the ring, tail to the end.*/
|
||||
char *buffer;
|
||||
char *head;
|
||||
char *tail;
|
||||
|
||||
int sigio;
|
||||
struct delayed_work task;
|
||||
const struct line_driver *driver;
|
||||
};
|
||||
|
||||
extern void line_close(struct tty_struct *tty, struct file * filp);
|
||||
extern int line_open(struct tty_struct *tty, struct file *filp);
|
||||
extern int line_install(struct tty_driver *driver, struct tty_struct *tty,
|
||||
struct line *line);
|
||||
extern void line_cleanup(struct tty_struct *tty);
|
||||
extern void line_hangup(struct tty_struct *tty);
|
||||
extern int line_setup(char **conf, unsigned nlines, char **def,
|
||||
char *init, char *name);
|
||||
extern int line_write(struct tty_struct *tty, const unsigned char *buf,
|
||||
int len);
|
||||
extern int line_put_char(struct tty_struct *tty, unsigned char ch);
|
||||
extern void line_set_termios(struct tty_struct *tty, struct ktermios * old);
|
||||
extern int line_chars_in_buffer(struct tty_struct *tty);
|
||||
extern void line_flush_buffer(struct tty_struct *tty);
|
||||
extern void line_flush_chars(struct tty_struct *tty);
|
||||
extern int line_write_room(struct tty_struct *tty);
|
||||
extern void line_throttle(struct tty_struct *tty);
|
||||
extern void line_unthrottle(struct tty_struct *tty);
|
||||
|
||||
extern char *add_xterm_umid(char *base);
|
||||
extern int line_setup_irq(int fd, int input, int output, struct line *line,
|
||||
void *data);
|
||||
extern void line_close_chan(struct line *line);
|
||||
extern int register_lines(struct line_driver *line_driver,
|
||||
const struct tty_operations *driver,
|
||||
struct line *lines, int nlines);
|
||||
extern int setup_one_line(struct line *lines, int n, char *init,
|
||||
const struct chan_opts *opts, char **error_out);
|
||||
extern void close_lines(struct line *lines, int nlines);
|
||||
|
||||
extern int line_config(struct line *lines, unsigned int sizeof_lines,
|
||||
char *str, const struct chan_opts *opts,
|
||||
char **error_out);
|
||||
extern int line_id(char **str, int *start_out, int *end_out);
|
||||
extern int line_remove(struct line *lines, unsigned int sizeof_lines, int n,
|
||||
char **error_out);
|
||||
extern int line_get_config(char *dev, struct line *lines,
|
||||
unsigned int sizeof_lines, char *str,
|
||||
int size, char **error_out);
|
||||
|
||||
#endif
|
98
arch/um/drivers/mconsole.h
Normal file
98
arch/um/drivers/mconsole.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __MCONSOLE_H__
|
||||
#define __MCONSOLE_H__
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#include <stdint.h>
|
||||
#define u32 uint32_t
|
||||
#endif
|
||||
|
||||
#include <sysdep/ptrace.h>
|
||||
|
||||
#define MCONSOLE_MAGIC (0xcafebabe)
|
||||
#define MCONSOLE_MAX_DATA (512)
|
||||
#define MCONSOLE_VERSION 2
|
||||
|
||||
struct mconsole_request {
|
||||
u32 magic;
|
||||
u32 version;
|
||||
u32 len;
|
||||
char data[MCONSOLE_MAX_DATA];
|
||||
};
|
||||
|
||||
struct mconsole_reply {
|
||||
u32 err;
|
||||
u32 more;
|
||||
u32 len;
|
||||
char data[MCONSOLE_MAX_DATA];
|
||||
};
|
||||
|
||||
struct mconsole_notify {
|
||||
u32 magic;
|
||||
u32 version;
|
||||
enum { MCONSOLE_SOCKET, MCONSOLE_PANIC, MCONSOLE_HANG,
|
||||
MCONSOLE_USER_NOTIFY } type;
|
||||
u32 len;
|
||||
char data[MCONSOLE_MAX_DATA];
|
||||
};
|
||||
|
||||
struct mc_request;
|
||||
|
||||
enum mc_context { MCONSOLE_INTR, MCONSOLE_PROC };
|
||||
|
||||
struct mconsole_command
|
||||
{
|
||||
char *command;
|
||||
void (*handler)(struct mc_request *req);
|
||||
enum mc_context context;
|
||||
};
|
||||
|
||||
struct mc_request
|
||||
{
|
||||
int len;
|
||||
int as_interrupt;
|
||||
|
||||
int originating_fd;
|
||||
unsigned int originlen;
|
||||
unsigned char origin[128]; /* sockaddr_un */
|
||||
|
||||
struct mconsole_request request;
|
||||
struct mconsole_command *cmd;
|
||||
struct uml_pt_regs regs;
|
||||
};
|
||||
|
||||
extern char mconsole_socket_name[];
|
||||
|
||||
extern int mconsole_unlink_socket(void);
|
||||
extern int mconsole_reply_len(struct mc_request *req, const char *reply,
|
||||
int len, int err, int more);
|
||||
extern int mconsole_reply(struct mc_request *req, const char *str, int err,
|
||||
int more);
|
||||
|
||||
extern void mconsole_version(struct mc_request *req);
|
||||
extern void mconsole_help(struct mc_request *req);
|
||||
extern void mconsole_halt(struct mc_request *req);
|
||||
extern void mconsole_reboot(struct mc_request *req);
|
||||
extern void mconsole_config(struct mc_request *req);
|
||||
extern void mconsole_remove(struct mc_request *req);
|
||||
extern void mconsole_sysrq(struct mc_request *req);
|
||||
extern void mconsole_cad(struct mc_request *req);
|
||||
extern void mconsole_stop(struct mc_request *req);
|
||||
extern void mconsole_go(struct mc_request *req);
|
||||
extern void mconsole_log(struct mc_request *req);
|
||||
extern void mconsole_proc(struct mc_request *req);
|
||||
extern void mconsole_stack(struct mc_request *req);
|
||||
|
||||
extern int mconsole_get_request(int fd, struct mc_request *req);
|
||||
extern int mconsole_notify(char *sock_name, int type, const void *data,
|
||||
int len);
|
||||
extern char *mconsole_notify_socket(void);
|
||||
extern void lock_notify(void);
|
||||
extern void unlock_notify(void);
|
||||
|
||||
#endif
|
858
arch/um/drivers/mconsole_kern.c
Normal file
858
arch/um/drivers/mconsole_kern.c
Normal file
|
@ -0,0 +1,858 @@
|
|||
/*
|
||||
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
|
||||
* Copyright (C) 2001 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <linux/console.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/un.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/file.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/switch_to.h>
|
||||
|
||||
#include <init.h>
|
||||
#include <irq_kern.h>
|
||||
#include <irq_user.h>
|
||||
#include <kern_util.h>
|
||||
#include "mconsole.h"
|
||||
#include "mconsole_kern.h"
|
||||
#include <os.h>
|
||||
|
||||
static int do_unlink_socket(struct notifier_block *notifier,
|
||||
unsigned long what, void *data)
|
||||
{
|
||||
return mconsole_unlink_socket();
|
||||
}
|
||||
|
||||
|
||||
static struct notifier_block reboot_notifier = {
|
||||
.notifier_call = do_unlink_socket,
|
||||
.priority = 0,
|
||||
};
|
||||
|
||||
/* Safe without explicit locking for now. Tasklets provide their own
|
||||
* locking, and the interrupt handler is safe because it can't interrupt
|
||||
* itself and it can only happen on CPU 0.
|
||||
*/
|
||||
|
||||
static LIST_HEAD(mc_requests);
|
||||
|
||||
static void mc_work_proc(struct work_struct *unused)
|
||||
{
|
||||
struct mconsole_entry *req;
|
||||
unsigned long flags;
|
||||
|
||||
while (!list_empty(&mc_requests)) {
|
||||
local_irq_save(flags);
|
||||
req = list_entry(mc_requests.next, struct mconsole_entry, list);
|
||||
list_del(&req->list);
|
||||
local_irq_restore(flags);
|
||||
req->request.cmd->handler(&req->request);
|
||||
kfree(req);
|
||||
}
|
||||
}
|
||||
|
||||
static DECLARE_WORK(mconsole_work, mc_work_proc);
|
||||
|
||||
static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
/* long to avoid size mismatch warnings from gcc */
|
||||
long fd;
|
||||
struct mconsole_entry *new;
|
||||
static struct mc_request req; /* that's OK */
|
||||
|
||||
fd = (long) dev_id;
|
||||
while (mconsole_get_request(fd, &req)) {
|
||||
if (req.cmd->context == MCONSOLE_INTR)
|
||||
(*req.cmd->handler)(&req);
|
||||
else {
|
||||
new = kmalloc(sizeof(*new), GFP_NOWAIT);
|
||||
if (new == NULL)
|
||||
mconsole_reply(&req, "Out of memory", 1, 0);
|
||||
else {
|
||||
new->request = req;
|
||||
new->request.regs = get_irq_regs()->regs;
|
||||
list_add(&new->list, &mc_requests);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!list_empty(&mc_requests))
|
||||
schedule_work(&mconsole_work);
|
||||
reactivate_fd(fd, MCONSOLE_IRQ);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void mconsole_version(struct mc_request *req)
|
||||
{
|
||||
char version[256];
|
||||
|
||||
sprintf(version, "%s %s %s %s %s", utsname()->sysname,
|
||||
utsname()->nodename, utsname()->release, utsname()->version,
|
||||
utsname()->machine);
|
||||
mconsole_reply(req, version, 0, 0);
|
||||
}
|
||||
|
||||
void mconsole_log(struct mc_request *req)
|
||||
{
|
||||
int len;
|
||||
char *ptr = req->request.data;
|
||||
|
||||
ptr += strlen("log ");
|
||||
|
||||
len = req->len - (ptr - req->request.data);
|
||||
printk(KERN_WARNING "%.*s", len, ptr);
|
||||
mconsole_reply(req, "", 0, 0);
|
||||
}
|
||||
|
||||
void mconsole_proc(struct mc_request *req)
|
||||
{
|
||||
struct vfsmount *mnt = task_active_pid_ns(current)->proc_mnt;
|
||||
char *buf;
|
||||
int len;
|
||||
struct file *file;
|
||||
int first_chunk = 1;
|
||||
char *ptr = req->request.data;
|
||||
|
||||
ptr += strlen("proc");
|
||||
ptr = skip_spaces(ptr);
|
||||
|
||||
file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY);
|
||||
if (IS_ERR(file)) {
|
||||
mconsole_reply(req, "Failed to open file", 1, 0);
|
||||
printk(KERN_ERR "open /proc/%s: %ld\n", ptr, PTR_ERR(file));
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
mconsole_reply(req, "Failed to allocate buffer", 1, 0);
|
||||
goto out_fput;
|
||||
}
|
||||
|
||||
do {
|
||||
loff_t pos = file->f_pos;
|
||||
mm_segment_t old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
len = vfs_read(file, buf, PAGE_SIZE - 1, &pos);
|
||||
set_fs(old_fs);
|
||||
file->f_pos = pos;
|
||||
if (len < 0) {
|
||||
mconsole_reply(req, "Read of file failed", 1, 0);
|
||||
goto out_free;
|
||||
}
|
||||
/* Begin the file content on his own line. */
|
||||
if (first_chunk) {
|
||||
mconsole_reply(req, "\n", 0, 1);
|
||||
first_chunk = 0;
|
||||
}
|
||||
buf[len] = '\0';
|
||||
mconsole_reply(req, buf, 0, (len > 0));
|
||||
} while (len > 0);
|
||||
out_free:
|
||||
kfree(buf);
|
||||
out_fput:
|
||||
fput(file);
|
||||
out: ;
|
||||
}
|
||||
|
||||
#define UML_MCONSOLE_HELPTEXT \
|
||||
"Commands: \n\
|
||||
version - Get kernel version \n\
|
||||
help - Print this message \n\
|
||||
halt - Halt UML \n\
|
||||
reboot - Reboot UML \n\
|
||||
config <dev>=<config> - Add a new device to UML; \n\
|
||||
same syntax as command line \n\
|
||||
config <dev> - Query the configuration of a device \n\
|
||||
remove <dev> - Remove a device from UML \n\
|
||||
sysrq <letter> - Performs the SysRq action controlled by the letter \n\
|
||||
cad - invoke the Ctrl-Alt-Del handler \n\
|
||||
stop - pause the UML; it will do nothing until it receives a 'go' \n\
|
||||
go - continue the UML after a 'stop' \n\
|
||||
log <string> - make UML enter <string> into the kernel log\n\
|
||||
proc <file> - returns the contents of the UML's /proc/<file>\n\
|
||||
stack <pid> - returns the stack of the specified pid\n\
|
||||
"
|
||||
|
||||
void mconsole_help(struct mc_request *req)
|
||||
{
|
||||
mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
|
||||
}
|
||||
|
||||
void mconsole_halt(struct mc_request *req)
|
||||
{
|
||||
mconsole_reply(req, "", 0, 0);
|
||||
machine_halt();
|
||||
}
|
||||
|
||||
void mconsole_reboot(struct mc_request *req)
|
||||
{
|
||||
mconsole_reply(req, "", 0, 0);
|
||||
machine_restart(NULL);
|
||||
}
|
||||
|
||||
void mconsole_cad(struct mc_request *req)
|
||||
{
|
||||
mconsole_reply(req, "", 0, 0);
|
||||
ctrl_alt_del();
|
||||
}
|
||||
|
||||
void mconsole_go(struct mc_request *req)
|
||||
{
|
||||
mconsole_reply(req, "Not stopped", 1, 0);
|
||||
}
|
||||
|
||||
void mconsole_stop(struct mc_request *req)
|
||||
{
|
||||
deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
|
||||
os_set_fd_block(req->originating_fd, 1);
|
||||
mconsole_reply(req, "stopped", 0, 0);
|
||||
for (;;) {
|
||||
if (!mconsole_get_request(req->originating_fd, req))
|
||||
continue;
|
||||
if (req->cmd->handler == mconsole_go)
|
||||
break;
|
||||
if (req->cmd->handler == mconsole_stop) {
|
||||
mconsole_reply(req, "Already stopped", 1, 0);
|
||||
continue;
|
||||
}
|
||||
if (req->cmd->handler == mconsole_sysrq) {
|
||||
struct pt_regs *old_regs;
|
||||
old_regs = set_irq_regs((struct pt_regs *)&req->regs);
|
||||
mconsole_sysrq(req);
|
||||
set_irq_regs(old_regs);
|
||||
continue;
|
||||
}
|
||||
(*req->cmd->handler)(req);
|
||||
}
|
||||
os_set_fd_block(req->originating_fd, 0);
|
||||
reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
|
||||
mconsole_reply(req, "", 0, 0);
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(mc_devices_lock);
|
||||
static LIST_HEAD(mconsole_devices);
|
||||
|
||||
void mconsole_register_dev(struct mc_device *new)
|
||||
{
|
||||
spin_lock(&mc_devices_lock);
|
||||
BUG_ON(!list_empty(&new->list));
|
||||
list_add(&new->list, &mconsole_devices);
|
||||
spin_unlock(&mc_devices_lock);
|
||||
}
|
||||
|
||||
static struct mc_device *mconsole_find_dev(char *name)
|
||||
{
|
||||
struct list_head *ele;
|
||||
struct mc_device *dev;
|
||||
|
||||
list_for_each(ele, &mconsole_devices) {
|
||||
dev = list_entry(ele, struct mc_device, list);
|
||||
if (!strncmp(name, dev->name, strlen(dev->name)))
|
||||
return dev;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define UNPLUGGED_PER_PAGE \
|
||||
((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
|
||||
|
||||
struct unplugged_pages {
|
||||
struct list_head list;
|
||||
void *pages[UNPLUGGED_PER_PAGE];
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(plug_mem_mutex);
|
||||
static unsigned long long unplugged_pages_count = 0;
|
||||
static LIST_HEAD(unplugged_pages);
|
||||
static int unplug_index = UNPLUGGED_PER_PAGE;
|
||||
|
||||
static int mem_config(char *str, char **error_out)
|
||||
{
|
||||
unsigned long long diff;
|
||||
int err = -EINVAL, i, add;
|
||||
char *ret;
|
||||
|
||||
if (str[0] != '=') {
|
||||
*error_out = "Expected '=' after 'mem'";
|
||||
goto out;
|
||||
}
|
||||
|
||||
str++;
|
||||
if (str[0] == '-')
|
||||
add = 0;
|
||||
else if (str[0] == '+') {
|
||||
add = 1;
|
||||
}
|
||||
else {
|
||||
*error_out = "Expected increment to start with '-' or '+'";
|
||||
goto out;
|
||||
}
|
||||
|
||||
str++;
|
||||
diff = memparse(str, &ret);
|
||||
if (*ret != '\0') {
|
||||
*error_out = "Failed to parse memory increment";
|
||||
goto out;
|
||||
}
|
||||
|
||||
diff /= PAGE_SIZE;
|
||||
|
||||
mutex_lock(&plug_mem_mutex);
|
||||
for (i = 0; i < diff; i++) {
|
||||
struct unplugged_pages *unplugged;
|
||||
void *addr;
|
||||
|
||||
if (add) {
|
||||
if (list_empty(&unplugged_pages))
|
||||
break;
|
||||
|
||||
unplugged = list_entry(unplugged_pages.next,
|
||||
struct unplugged_pages, list);
|
||||
if (unplug_index > 0)
|
||||
addr = unplugged->pages[--unplug_index];
|
||||
else {
|
||||
list_del(&unplugged->list);
|
||||
addr = unplugged;
|
||||
unplug_index = UNPLUGGED_PER_PAGE;
|
||||
}
|
||||
|
||||
free_page((unsigned long) addr);
|
||||
unplugged_pages_count--;
|
||||
}
|
||||
else {
|
||||
struct page *page;
|
||||
|
||||
page = alloc_page(GFP_ATOMIC);
|
||||
if (page == NULL)
|
||||
break;
|
||||
|
||||
unplugged = page_address(page);
|
||||
if (unplug_index == UNPLUGGED_PER_PAGE) {
|
||||
list_add(&unplugged->list, &unplugged_pages);
|
||||
unplug_index = 0;
|
||||
}
|
||||
else {
|
||||
struct list_head *entry = unplugged_pages.next;
|
||||
addr = unplugged;
|
||||
|
||||
unplugged = list_entry(entry,
|
||||
struct unplugged_pages,
|
||||
list);
|
||||
err = os_drop_memory(addr, PAGE_SIZE);
|
||||
if (err) {
|
||||
printk(KERN_ERR "Failed to release "
|
||||
"memory - errno = %d\n", err);
|
||||
*error_out = "Failed to release memory";
|
||||
goto out_unlock;
|
||||
}
|
||||
unplugged->pages[unplug_index++] = addr;
|
||||
}
|
||||
|
||||
unplugged_pages_count++;
|
||||
}
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out_unlock:
|
||||
mutex_unlock(&plug_mem_mutex);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mem_get_config(char *name, char *str, int size, char **error_out)
|
||||
{
|
||||
char buf[sizeof("18446744073709551615")];
|
||||
int len = 0;
|
||||
|
||||
sprintf(buf, "%ld", uml_physmem);
|
||||
CONFIG_CHUNK(str, size, len, buf, 1);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int mem_id(char **str, int *start_out, int *end_out)
|
||||
{
|
||||
*start_out = 0;
|
||||
*end_out = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mem_remove(int n, char **error_out)
|
||||
{
|
||||
*error_out = "Memory doesn't support the remove operation";
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static struct mc_device mem_mc = {
|
||||
.list = LIST_HEAD_INIT(mem_mc.list),
|
||||
.name = "mem",
|
||||
.config = mem_config,
|
||||
.get_config = mem_get_config,
|
||||
.id = mem_id,
|
||||
.remove = mem_remove,
|
||||
};
|
||||
|
||||
static int __init mem_mc_init(void)
|
||||
{
|
||||
if (can_drop_memory())
|
||||
mconsole_register_dev(&mem_mc);
|
||||
else printk(KERN_ERR "Can't release memory to the host - memory "
|
||||
"hotplug won't be supported\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__initcall(mem_mc_init);
|
||||
|
||||
#define CONFIG_BUF_SIZE 64
|
||||
|
||||
static void mconsole_get_config(int (*get_config)(char *, char *, int,
|
||||
char **),
|
||||
struct mc_request *req, char *name)
|
||||
{
|
||||
char default_buf[CONFIG_BUF_SIZE], *error, *buf;
|
||||
int n, size;
|
||||
|
||||
if (get_config == NULL) {
|
||||
mconsole_reply(req, "No get_config routine defined", 1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
error = NULL;
|
||||
size = ARRAY_SIZE(default_buf);
|
||||
buf = default_buf;
|
||||
|
||||
while (1) {
|
||||
n = (*get_config)(name, buf, size, &error);
|
||||
if (error != NULL) {
|
||||
mconsole_reply(req, error, 1, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (n <= size) {
|
||||
mconsole_reply(req, buf, 0, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (buf != default_buf)
|
||||
kfree(buf);
|
||||
|
||||
size = n;
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
mconsole_reply(req, "Failed to allocate buffer", 1, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (buf != default_buf)
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
void mconsole_config(struct mc_request *req)
|
||||
{
|
||||
struct mc_device *dev;
|
||||
char *ptr = req->request.data, *name, *error_string = "";
|
||||
int err;
|
||||
|
||||
ptr += strlen("config");
|
||||
ptr = skip_spaces(ptr);
|
||||
dev = mconsole_find_dev(ptr);
|
||||
if (dev == NULL) {
|
||||
mconsole_reply(req, "Bad configuration option", 1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
name = &ptr[strlen(dev->name)];
|
||||
ptr = name;
|
||||
while ((*ptr != '=') && (*ptr != '\0'))
|
||||
ptr++;
|
||||
|
||||
if (*ptr == '=') {
|
||||
err = (*dev->config)(name, &error_string);
|
||||
mconsole_reply(req, error_string, err, 0);
|
||||
}
|
||||
else mconsole_get_config(dev->get_config, req, name);
|
||||
}
|
||||
|
||||
void mconsole_remove(struct mc_request *req)
|
||||
{
|
||||
struct mc_device *dev;
|
||||
char *ptr = req->request.data, *err_msg = "";
|
||||
char error[256];
|
||||
int err, start, end, n;
|
||||
|
||||
ptr += strlen("remove");
|
||||
ptr = skip_spaces(ptr);
|
||||
dev = mconsole_find_dev(ptr);
|
||||
if (dev == NULL) {
|
||||
mconsole_reply(req, "Bad remove option", 1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = &ptr[strlen(dev->name)];
|
||||
|
||||
err = 1;
|
||||
n = (*dev->id)(&ptr, &start, &end);
|
||||
if (n < 0) {
|
||||
err_msg = "Couldn't parse device number";
|
||||
goto out;
|
||||
}
|
||||
else if ((n < start) || (n > end)) {
|
||||
sprintf(error, "Invalid device number - must be between "
|
||||
"%d and %d", start, end);
|
||||
err_msg = error;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err_msg = NULL;
|
||||
err = (*dev->remove)(n, &err_msg);
|
||||
switch(err) {
|
||||
case 0:
|
||||
err_msg = "";
|
||||
break;
|
||||
case -ENODEV:
|
||||
if (err_msg == NULL)
|
||||
err_msg = "Device doesn't exist";
|
||||
break;
|
||||
case -EBUSY:
|
||||
if (err_msg == NULL)
|
||||
err_msg = "Device is currently open";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
out:
|
||||
mconsole_reply(req, err_msg, err, 0);
|
||||
}
|
||||
|
||||
struct mconsole_output {
|
||||
struct list_head list;
|
||||
struct mc_request *req;
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(client_lock);
|
||||
static LIST_HEAD(clients);
|
||||
static char console_buf[MCONSOLE_MAX_DATA];
|
||||
|
||||
static void console_write(struct console *console, const char *string,
|
||||
unsigned int len)
|
||||
{
|
||||
struct list_head *ele;
|
||||
int n;
|
||||
|
||||
if (list_empty(&clients))
|
||||
return;
|
||||
|
||||
while (len > 0) {
|
||||
n = min((size_t) len, ARRAY_SIZE(console_buf));
|
||||
strncpy(console_buf, string, n);
|
||||
string += n;
|
||||
len -= n;
|
||||
|
||||
list_for_each(ele, &clients) {
|
||||
struct mconsole_output *entry;
|
||||
|
||||
entry = list_entry(ele, struct mconsole_output, list);
|
||||
mconsole_reply_len(entry->req, console_buf, n, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct console mc_console = { .name = "mc",
|
||||
.write = console_write,
|
||||
.flags = CON_ENABLED,
|
||||
.index = -1 };
|
||||
|
||||
static int mc_add_console(void)
|
||||
{
|
||||
register_console(&mc_console);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(mc_add_console);
|
||||
|
||||
static void with_console(struct mc_request *req, void (*proc)(void *),
|
||||
void *arg)
|
||||
{
|
||||
struct mconsole_output entry;
|
||||
unsigned long flags;
|
||||
|
||||
entry.req = req;
|
||||
spin_lock_irqsave(&client_lock, flags);
|
||||
list_add(&entry.list, &clients);
|
||||
spin_unlock_irqrestore(&client_lock, flags);
|
||||
|
||||
(*proc)(arg);
|
||||
|
||||
mconsole_reply_len(req, "", 0, 0, 0);
|
||||
|
||||
spin_lock_irqsave(&client_lock, flags);
|
||||
list_del(&entry.list);
|
||||
spin_unlock_irqrestore(&client_lock, flags);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MAGIC_SYSRQ
|
||||
|
||||
#include <linux/sysrq.h>
|
||||
|
||||
static void sysrq_proc(void *arg)
|
||||
{
|
||||
char *op = arg;
|
||||
handle_sysrq(*op);
|
||||
}
|
||||
|
||||
void mconsole_sysrq(struct mc_request *req)
|
||||
{
|
||||
char *ptr = req->request.data;
|
||||
|
||||
ptr += strlen("sysrq");
|
||||
ptr = skip_spaces(ptr);
|
||||
|
||||
/*
|
||||
* With 'b', the system will shut down without a chance to reply,
|
||||
* so in this case, we reply first.
|
||||
*/
|
||||
if (*ptr == 'b')
|
||||
mconsole_reply(req, "", 0, 0);
|
||||
|
||||
with_console(req, sysrq_proc, ptr);
|
||||
}
|
||||
#else
|
||||
void mconsole_sysrq(struct mc_request *req)
|
||||
{
|
||||
mconsole_reply(req, "Sysrq not compiled in", 1, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void stack_proc(void *arg)
|
||||
{
|
||||
struct task_struct *task = arg;
|
||||
|
||||
show_stack(task, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mconsole stack trace
|
||||
* Added by Allan Graves, Jeff Dike
|
||||
* Dumps a stacks registers to the linux console.
|
||||
* Usage stack <pid>.
|
||||
*/
|
||||
void mconsole_stack(struct mc_request *req)
|
||||
{
|
||||
char *ptr = req->request.data;
|
||||
int pid_requested= -1;
|
||||
struct task_struct *to = NULL;
|
||||
|
||||
/*
|
||||
* Would be nice:
|
||||
* 1) Send showregs output to mconsole.
|
||||
* 2) Add a way to stack dump all pids.
|
||||
*/
|
||||
|
||||
ptr += strlen("stack");
|
||||
ptr = skip_spaces(ptr);
|
||||
|
||||
/*
|
||||
* Should really check for multiple pids or reject bad args here
|
||||
*/
|
||||
/* What do the arguments in mconsole_reply mean? */
|
||||
if (sscanf(ptr, "%d", &pid_requested) == 0) {
|
||||
mconsole_reply(req, "Please specify a pid", 1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
|
||||
if ((to == NULL) || (pid_requested == 0)) {
|
||||
mconsole_reply(req, "Couldn't find that pid", 1, 0);
|
||||
return;
|
||||
}
|
||||
with_console(req, stack_proc, to);
|
||||
}
|
||||
|
||||
/*
|
||||
* Changed by mconsole_setup, which is __setup, and called before SMP is
|
||||
* active.
|
||||
*/
|
||||
static char *notify_socket = NULL;
|
||||
|
||||
static int __init mconsole_init(void)
|
||||
{
|
||||
/* long to avoid size mismatch warnings from gcc */
|
||||
long sock;
|
||||
int err;
|
||||
char file[UNIX_PATH_MAX];
|
||||
|
||||
if (umid_file_name("mconsole", file, sizeof(file)))
|
||||
return -1;
|
||||
snprintf(mconsole_socket_name, sizeof(file), "%s", file);
|
||||
|
||||
sock = os_create_unix_socket(file, sizeof(file), 1);
|
||||
if (sock < 0) {
|
||||
printk(KERN_ERR "Failed to initialize management console\n");
|
||||
return 1;
|
||||
}
|
||||
if (os_set_fd_block(sock, 0))
|
||||
goto out;
|
||||
|
||||
register_reboot_notifier(&reboot_notifier);
|
||||
|
||||
err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
|
||||
IRQF_SHARED, "mconsole", (void *)sock);
|
||||
if (err) {
|
||||
printk(KERN_ERR "Failed to get IRQ for management console\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (notify_socket != NULL) {
|
||||
notify_socket = kstrdup(notify_socket, GFP_KERNEL);
|
||||
if (notify_socket != NULL)
|
||||
mconsole_notify(notify_socket, MCONSOLE_SOCKET,
|
||||
mconsole_socket_name,
|
||||
strlen(mconsole_socket_name) + 1);
|
||||
else printk(KERN_ERR "mconsole_setup failed to strdup "
|
||||
"string\n");
|
||||
}
|
||||
|
||||
printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
|
||||
MCONSOLE_VERSION, mconsole_socket_name);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
os_close_file(sock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
__initcall(mconsole_init);
|
||||
|
||||
static ssize_t mconsole_proc_write(struct file *file,
|
||||
const char __user *buffer, size_t count, loff_t *pos)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
buf = kmalloc(count + 1, GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(buf, buffer, count)) {
|
||||
count = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf[count] = '\0';
|
||||
|
||||
mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
|
||||
out:
|
||||
kfree(buf);
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations mconsole_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.write = mconsole_proc_write,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static int create_proc_mconsole(void)
|
||||
{
|
||||
struct proc_dir_entry *ent;
|
||||
|
||||
if (notify_socket == NULL)
|
||||
return 0;
|
||||
|
||||
ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
|
||||
if (ent == NULL) {
|
||||
printk(KERN_INFO "create_proc_mconsole : proc_create failed\n");
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(notify_spinlock);
|
||||
|
||||
void lock_notify(void)
|
||||
{
|
||||
spin_lock(¬ify_spinlock);
|
||||
}
|
||||
|
||||
void unlock_notify(void)
|
||||
{
|
||||
spin_unlock(¬ify_spinlock);
|
||||
}
|
||||
|
||||
__initcall(create_proc_mconsole);
|
||||
|
||||
#define NOTIFY "notify:"
|
||||
|
||||
static int mconsole_setup(char *str)
|
||||
{
|
||||
if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
|
||||
str += strlen(NOTIFY);
|
||||
notify_socket = str;
|
||||
}
|
||||
else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("mconsole=", mconsole_setup);
|
||||
|
||||
__uml_help(mconsole_setup,
|
||||
"mconsole=notify:<socket>\n"
|
||||
" Requests that the mconsole driver send a message to the named Unix\n"
|
||||
" socket containing the name of the mconsole socket. This also serves\n"
|
||||
" to notify outside processes when UML has booted far enough to respond\n"
|
||||
" to mconsole requests.\n\n"
|
||||
);
|
||||
|
||||
static int notify_panic(struct notifier_block *self, unsigned long unused1,
|
||||
void *ptr)
|
||||
{
|
||||
char *message = ptr;
|
||||
|
||||
if (notify_socket == NULL)
|
||||
return 0;
|
||||
|
||||
mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
|
||||
strlen(message) + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct notifier_block panic_exit_notifier = {
|
||||
.notifier_call = notify_panic,
|
||||
.next = NULL,
|
||||
.priority = 1
|
||||
};
|
||||
|
||||
static int add_notifier(void)
|
||||
{
|
||||
atomic_notifier_chain_register(&panic_notifier_list,
|
||||
&panic_exit_notifier);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__initcall(add_notifier);
|
||||
|
||||
char *mconsole_notify_socket(void)
|
||||
{
|
||||
return notify_socket;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mconsole_notify_socket);
|
52
arch/um/drivers/mconsole_kern.h
Normal file
52
arch/um/drivers/mconsole_kern.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __MCONSOLE_KERN_H__
|
||||
#define __MCONSOLE_KERN_H__
|
||||
|
||||
#include <linux/list.h>
|
||||
#include "mconsole.h"
|
||||
|
||||
struct mconsole_entry {
|
||||
struct list_head list;
|
||||
struct mc_request request;
|
||||
};
|
||||
|
||||
/* All these methods are called in process context. */
|
||||
struct mc_device {
|
||||
struct list_head list;
|
||||
char *name;
|
||||
int (*config)(char *, char **);
|
||||
int (*get_config)(char *, char *, int, char **);
|
||||
int (*id)(char **, int *, int *);
|
||||
int (*remove)(int, char **);
|
||||
};
|
||||
|
||||
#define CONFIG_CHUNK(str, size, current, chunk, end) \
|
||||
do { \
|
||||
current += strlen(chunk); \
|
||||
if(current >= size) \
|
||||
str = NULL; \
|
||||
if(str != NULL){ \
|
||||
strcpy(str, chunk); \
|
||||
str += strlen(chunk); \
|
||||
} \
|
||||
if(end) \
|
||||
current++; \
|
||||
} while(0)
|
||||
|
||||
#ifdef CONFIG_MCONSOLE
|
||||
|
||||
extern void mconsole_register_dev(struct mc_device *new);
|
||||
|
||||
#else
|
||||
|
||||
static inline void mconsole_register_dev(struct mc_device *new)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
219
arch/um/drivers/mconsole_user.c
Normal file
219
arch/um/drivers/mconsole_user.c
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/un.h>
|
||||
#include "mconsole.h"
|
||||
|
||||
static struct mconsole_command commands[] = {
|
||||
/*
|
||||
* With uts namespaces, uts information becomes process-specific, so
|
||||
* we need a process context. If we try handling this in interrupt
|
||||
* context, we may hit an exiting process without a valid uts
|
||||
* namespace.
|
||||
*/
|
||||
{ "version", mconsole_version, MCONSOLE_PROC },
|
||||
{ "halt", mconsole_halt, MCONSOLE_PROC },
|
||||
{ "reboot", mconsole_reboot, MCONSOLE_PROC },
|
||||
{ "config", mconsole_config, MCONSOLE_PROC },
|
||||
{ "remove", mconsole_remove, MCONSOLE_PROC },
|
||||
{ "sysrq", mconsole_sysrq, MCONSOLE_INTR },
|
||||
{ "help", mconsole_help, MCONSOLE_INTR },
|
||||
{ "cad", mconsole_cad, MCONSOLE_INTR },
|
||||
{ "stop", mconsole_stop, MCONSOLE_PROC },
|
||||
{ "go", mconsole_go, MCONSOLE_INTR },
|
||||
{ "log", mconsole_log, MCONSOLE_INTR },
|
||||
{ "proc", mconsole_proc, MCONSOLE_PROC },
|
||||
{ "stack", mconsole_stack, MCONSOLE_INTR },
|
||||
};
|
||||
|
||||
/* Initialized in mconsole_init, which is an initcall */
|
||||
char mconsole_socket_name[256];
|
||||
|
||||
static int mconsole_reply_v0(struct mc_request *req, char *reply)
|
||||
{
|
||||
struct iovec iov;
|
||||
struct msghdr msg;
|
||||
|
||||
iov.iov_base = reply;
|
||||
iov.iov_len = strlen(reply);
|
||||
|
||||
msg.msg_name = &(req->origin);
|
||||
msg.msg_namelen = req->originlen;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
return sendmsg(req->originating_fd, &msg, 0);
|
||||
}
|
||||
|
||||
static struct mconsole_command *mconsole_parse(struct mc_request *req)
|
||||
{
|
||||
struct mconsole_command *cmd;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(commands); i++) {
|
||||
cmd = &commands[i];
|
||||
if (!strncmp(req->request.data, cmd->command,
|
||||
strlen(cmd->command))) {
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define MIN(a,b) ((a)<(b) ? (a):(b))
|
||||
|
||||
#define STRINGX(x) #x
|
||||
#define STRING(x) STRINGX(x)
|
||||
|
||||
int mconsole_get_request(int fd, struct mc_request *req)
|
||||
{
|
||||
int len;
|
||||
|
||||
req->originlen = sizeof(req->origin);
|
||||
req->len = recvfrom(fd, &req->request, sizeof(req->request), 0,
|
||||
(struct sockaddr *) req->origin, &req->originlen);
|
||||
if (req->len < 0)
|
||||
return 0;
|
||||
|
||||
req->originating_fd = fd;
|
||||
|
||||
if (req->request.magic != MCONSOLE_MAGIC) {
|
||||
/* Unversioned request */
|
||||
len = MIN(sizeof(req->request.data) - 1,
|
||||
strlen((char *) &req->request));
|
||||
memmove(req->request.data, &req->request, len);
|
||||
req->request.data[len] = '\0';
|
||||
|
||||
req->request.magic = MCONSOLE_MAGIC;
|
||||
req->request.version = 0;
|
||||
req->request.len = len;
|
||||
|
||||
mconsole_reply_v0(req, "ERR Version 0 mconsole clients are "
|
||||
"not supported by this driver");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (req->request.len >= MCONSOLE_MAX_DATA) {
|
||||
mconsole_reply(req, "Request too large", 1, 0);
|
||||
return 0;
|
||||
}
|
||||
if (req->request.version != MCONSOLE_VERSION) {
|
||||
mconsole_reply(req, "This driver only supports version "
|
||||
STRING(MCONSOLE_VERSION) " clients", 1, 0);
|
||||
}
|
||||
|
||||
req->request.data[req->request.len] = '\0';
|
||||
req->cmd = mconsole_parse(req);
|
||||
if (req->cmd == NULL) {
|
||||
mconsole_reply(req, "Unknown command", 1, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mconsole_reply_len(struct mc_request *req, const char *str, int total,
|
||||
int err, int more)
|
||||
{
|
||||
/*
|
||||
* XXX This is a stack consumption problem. It'd be nice to
|
||||
* make it global and serialize access to it, but there are a
|
||||
* ton of callers to this function.
|
||||
*/
|
||||
struct mconsole_reply reply;
|
||||
int len, n;
|
||||
|
||||
do {
|
||||
reply.err = err;
|
||||
|
||||
/* err can only be true on the first packet */
|
||||
err = 0;
|
||||
|
||||
len = MIN(total, MCONSOLE_MAX_DATA - 1);
|
||||
|
||||
if (len == total) reply.more = more;
|
||||
else reply.more = 1;
|
||||
|
||||
memcpy(reply.data, str, len);
|
||||
reply.data[len] = '\0';
|
||||
total -= len;
|
||||
str += len;
|
||||
reply.len = len + 1;
|
||||
|
||||
len = sizeof(reply) + reply.len - sizeof(reply.data);
|
||||
|
||||
n = sendto(req->originating_fd, &reply, len, 0,
|
||||
(struct sockaddr *) req->origin, req->originlen);
|
||||
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
} while (total > 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mconsole_reply(struct mc_request *req, const char *str, int err, int more)
|
||||
{
|
||||
return mconsole_reply_len(req, str, strlen(str), err, more);
|
||||
}
|
||||
|
||||
|
||||
int mconsole_unlink_socket(void)
|
||||
{
|
||||
unlink(mconsole_socket_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int notify_sock = -1;
|
||||
|
||||
int mconsole_notify(char *sock_name, int type, const void *data, int len)
|
||||
{
|
||||
struct sockaddr_un target;
|
||||
struct mconsole_notify packet;
|
||||
int n, err = 0;
|
||||
|
||||
lock_notify();
|
||||
if (notify_sock < 0) {
|
||||
notify_sock = socket(PF_UNIX, SOCK_DGRAM, 0);
|
||||
if (notify_sock < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "mconsole_notify - socket failed, "
|
||||
"errno = %d\n", errno);
|
||||
}
|
||||
}
|
||||
unlock_notify();
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
target.sun_family = AF_UNIX;
|
||||
strcpy(target.sun_path, sock_name);
|
||||
|
||||
packet.magic = MCONSOLE_MAGIC;
|
||||
packet.version = MCONSOLE_VERSION;
|
||||
packet.type = type;
|
||||
len = (len > sizeof(packet.data)) ? sizeof(packet.data) : len;
|
||||
packet.len = len;
|
||||
memcpy(packet.data, data, len);
|
||||
|
||||
err = 0;
|
||||
len = sizeof(packet) + packet.len - sizeof(packet.data);
|
||||
n = sendto(notify_sock, &packet, len, 0, (struct sockaddr *) &target,
|
||||
sizeof(target));
|
||||
if (n < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "mconsole_notify - sendto failed, "
|
||||
"errno = %d\n", errno);
|
||||
}
|
||||
return err;
|
||||
}
|
134
arch/um/drivers/mmapper_kern.c
Normal file
134
arch/um/drivers/mmapper_kern.c
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* arch/um/drivers/mmapper_kern.c
|
||||
*
|
||||
* BRIEF MODULE DESCRIPTION
|
||||
*
|
||||
* Copyright (C) 2000 RidgeRun, Inc.
|
||||
* Author: RidgeRun, Inc.
|
||||
* Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <mem_user.h>
|
||||
|
||||
/* These are set in mmapper_init, which is called at boot time */
|
||||
static unsigned long mmapper_size;
|
||||
static unsigned long p_buf;
|
||||
static char *v_buf;
|
||||
|
||||
static ssize_t mmapper_read(struct file *file, char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
return simple_read_from_buffer(buf, count, ppos, v_buf, mmapper_size);
|
||||
}
|
||||
|
||||
static ssize_t mmapper_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
if (*ppos > mmapper_size)
|
||||
return -EINVAL;
|
||||
|
||||
return simple_write_to_buffer(v_buf, mmapper_size, ppos, buf, count);
|
||||
}
|
||||
|
||||
static long mmapper_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static int mmapper_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
int size;
|
||||
|
||||
if (vma->vm_pgoff != 0)
|
||||
goto out;
|
||||
|
||||
size = vma->vm_end - vma->vm_start;
|
||||
if (size > mmapper_size)
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* XXX A comment above remap_pfn_range says it should only be
|
||||
* called when the mm semaphore is held
|
||||
*/
|
||||
if (remap_pfn_range(vma, vma->vm_start, p_buf >> PAGE_SHIFT, size,
|
||||
vma->vm_page_prot))
|
||||
goto out;
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmapper_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmapper_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations mmapper_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = mmapper_read,
|
||||
.write = mmapper_write,
|
||||
.unlocked_ioctl = mmapper_ioctl,
|
||||
.mmap = mmapper_mmap,
|
||||
.open = mmapper_open,
|
||||
.release = mmapper_release,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
/*
|
||||
* No locking needed - only used (and modified) by below initcall and exitcall.
|
||||
*/
|
||||
static struct miscdevice mmapper_dev = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "mmapper",
|
||||
.fops = &mmapper_fops
|
||||
};
|
||||
|
||||
static int __init mmapper_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
printk(KERN_INFO "Mapper v0.1\n");
|
||||
|
||||
v_buf = (char *) find_iomem("mmapper", &mmapper_size);
|
||||
if (mmapper_size == 0) {
|
||||
printk(KERN_ERR "mmapper_init - find_iomem failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
p_buf = __pa(v_buf);
|
||||
|
||||
err = misc_register(&mmapper_dev);
|
||||
if (err) {
|
||||
printk(KERN_ERR "mmapper - misc_register failed, err = %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mmapper_exit(void)
|
||||
{
|
||||
misc_deregister(&mmapper_dev);
|
||||
}
|
||||
|
||||
module_init(mmapper_init);
|
||||
module_exit(mmapper_exit);
|
||||
|
||||
MODULE_AUTHOR("Greg Lonnon <glonnon@ridgerun.com>");
|
||||
MODULE_DESCRIPTION("DSPLinux simulator mmapper driver");
|
||||
MODULE_LICENSE("GPL");
|
911
arch/um/drivers/net_kern.c
Normal file
911
arch/um/drivers/net_kern.c
Normal file
|
@ -0,0 +1,911 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
|
||||
* James Leu (jleu@mindspring.net).
|
||||
* Copyright (C) 2001 by various other people who didn't put their name here.
|
||||
* Licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <init.h>
|
||||
#include <irq_kern.h>
|
||||
#include <irq_user.h>
|
||||
#include "mconsole_kern.h"
|
||||
#include <net_kern.h>
|
||||
#include <net_user.h>
|
||||
|
||||
#define DRIVER_NAME "uml-netdev"
|
||||
|
||||
static DEFINE_SPINLOCK(opened_lock);
|
||||
static LIST_HEAD(opened);
|
||||
|
||||
/*
|
||||
* The drop_skb is used when we can't allocate an skb. The
|
||||
* packet is read into drop_skb in order to get the data off the
|
||||
* connection to the host.
|
||||
* It is reallocated whenever a maximum packet size is seen which is
|
||||
* larger than any seen before. update_drop_skb is called from
|
||||
* eth_configure when a new interface is added.
|
||||
*/
|
||||
static DEFINE_SPINLOCK(drop_lock);
|
||||
static struct sk_buff *drop_skb;
|
||||
static int drop_max;
|
||||
|
||||
static int update_drop_skb(int max)
|
||||
{
|
||||
struct sk_buff *new;
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
spin_lock_irqsave(&drop_lock, flags);
|
||||
|
||||
if (max <= drop_max)
|
||||
goto out;
|
||||
|
||||
err = -ENOMEM;
|
||||
new = dev_alloc_skb(max);
|
||||
if (new == NULL)
|
||||
goto out;
|
||||
|
||||
skb_put(new, max);
|
||||
|
||||
kfree_skb(drop_skb);
|
||||
drop_skb = new;
|
||||
drop_max = max;
|
||||
err = 0;
|
||||
out:
|
||||
spin_unlock_irqrestore(&drop_lock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int uml_net_rx(struct net_device *dev)
|
||||
{
|
||||
struct uml_net_private *lp = netdev_priv(dev);
|
||||
int pkt_len;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* If we can't allocate memory, try again next round. */
|
||||
skb = dev_alloc_skb(lp->max_packet);
|
||||
if (skb == NULL) {
|
||||
drop_skb->dev = dev;
|
||||
/* Read a packet into drop_skb and don't do anything with it. */
|
||||
(*lp->read)(lp->fd, drop_skb, lp);
|
||||
dev->stats.rx_dropped++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
skb->dev = dev;
|
||||
skb_put(skb, lp->max_packet);
|
||||
skb_reset_mac_header(skb);
|
||||
pkt_len = (*lp->read)(lp->fd, skb, lp);
|
||||
|
||||
if (pkt_len > 0) {
|
||||
skb_trim(skb, pkt_len);
|
||||
skb->protocol = (*lp->protocol)(skb);
|
||||
|
||||
dev->stats.rx_bytes += skb->len;
|
||||
dev->stats.rx_packets++;
|
||||
netif_rx(skb);
|
||||
return pkt_len;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
return pkt_len;
|
||||
}
|
||||
|
||||
static void uml_dev_close(struct work_struct *work)
|
||||
{
|
||||
struct uml_net_private *lp =
|
||||
container_of(work, struct uml_net_private, work);
|
||||
dev_close(lp->dev);
|
||||
}
|
||||
|
||||
static irqreturn_t uml_net_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *dev = dev_id;
|
||||
struct uml_net_private *lp = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
if (!netif_running(dev))
|
||||
return IRQ_NONE;
|
||||
|
||||
spin_lock(&lp->lock);
|
||||
while ((err = uml_net_rx(dev)) > 0) ;
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR
|
||||
"Device '%s' read returned %d, shutting it down\n",
|
||||
dev->name, err);
|
||||
/* dev_close can't be called in interrupt context, and takes
|
||||
* again lp->lock.
|
||||
* And dev_close() can be safely called multiple times on the
|
||||
* same device, since it tests for (dev->flags & IFF_UP). So
|
||||
* there's no harm in delaying the device shutdown.
|
||||
* Furthermore, the workqueue will not re-enqueue an already
|
||||
* enqueued work item. */
|
||||
schedule_work(&lp->work);
|
||||
goto out;
|
||||
}
|
||||
reactivate_fd(lp->fd, UM_ETH_IRQ);
|
||||
|
||||
out:
|
||||
spin_unlock(&lp->lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int uml_net_open(struct net_device *dev)
|
||||
{
|
||||
struct uml_net_private *lp = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
if (lp->fd >= 0) {
|
||||
err = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
lp->fd = (*lp->open)(&lp->user);
|
||||
if (lp->fd < 0) {
|
||||
err = lp->fd;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt,
|
||||
IRQF_SHARED, dev->name, dev);
|
||||
if (err != 0) {
|
||||
printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err);
|
||||
err = -ENETUNREACH;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
lp->tl.data = (unsigned long) &lp->user;
|
||||
netif_start_queue(dev);
|
||||
|
||||
/* clear buffer - it can happen that the host side of the interface
|
||||
* is full when we get here. In this case, new data is never queued,
|
||||
* SIGIOs never arrive, and the net never works.
|
||||
*/
|
||||
while ((err = uml_net_rx(dev)) > 0) ;
|
||||
|
||||
spin_lock(&opened_lock);
|
||||
list_add(&lp->list, &opened);
|
||||
spin_unlock(&opened_lock);
|
||||
|
||||
return 0;
|
||||
out_close:
|
||||
if (lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
|
||||
lp->fd = -1;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int uml_net_close(struct net_device *dev)
|
||||
{
|
||||
struct uml_net_private *lp = netdev_priv(dev);
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
um_free_irq(dev->irq, dev);
|
||||
if (lp->close != NULL)
|
||||
(*lp->close)(lp->fd, &lp->user);
|
||||
lp->fd = -1;
|
||||
|
||||
spin_lock(&opened_lock);
|
||||
list_del(&lp->list);
|
||||
spin_unlock(&opened_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct uml_net_private *lp = netdev_priv(dev);
|
||||
unsigned long flags;
|
||||
int len;
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
spin_lock_irqsave(&lp->lock, flags);
|
||||
|
||||
len = (*lp->write)(lp->fd, skb, lp);
|
||||
skb_tx_timestamp(skb);
|
||||
|
||||
if (len == skb->len) {
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
dev->trans_start = jiffies;
|
||||
netif_start_queue(dev);
|
||||
|
||||
/* this is normally done in the interrupt when tx finishes */
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
else if (len == 0) {
|
||||
netif_start_queue(dev);
|
||||
dev->stats.tx_dropped++;
|
||||
}
|
||||
else {
|
||||
netif_start_queue(dev);
|
||||
printk(KERN_ERR "uml_net_start_xmit: failed(%d)\n", len);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&lp->lock, flags);
|
||||
|
||||
dev_consume_skb_any(skb);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static void uml_net_set_multicast_list(struct net_device *dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static void uml_net_tx_timeout(struct net_device *dev)
|
||||
{
|
||||
dev->trans_start = jiffies;
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
|
||||
static int uml_net_change_mtu(struct net_device *dev, int new_mtu)
|
||||
{
|
||||
dev->mtu = new_mtu;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
static void uml_net_poll_controller(struct net_device *dev)
|
||||
{
|
||||
disable_irq(dev->irq);
|
||||
uml_net_interrupt(dev->irq, dev);
|
||||
enable_irq(dev->irq);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void uml_net_get_drvinfo(struct net_device *dev,
|
||||
struct ethtool_drvinfo *info)
|
||||
{
|
||||
strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver));
|
||||
strlcpy(info->version, "42", sizeof(info->version));
|
||||
}
|
||||
|
||||
static const struct ethtool_ops uml_net_ethtool_ops = {
|
||||
.get_drvinfo = uml_net_get_drvinfo,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_ts_info = ethtool_op_get_ts_info,
|
||||
};
|
||||
|
||||
static void uml_net_user_timer_expire(unsigned long _conn)
|
||||
{
|
||||
#ifdef undef
|
||||
struct connection *conn = (struct connection *)_conn;
|
||||
|
||||
dprintk(KERN_INFO "uml_net_user_timer_expire [%p]\n", conn);
|
||||
do_connect(conn);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void setup_etheraddr(struct net_device *dev, char *str)
|
||||
{
|
||||
unsigned char *addr = dev->dev_addr;
|
||||
char *end;
|
||||
int i;
|
||||
|
||||
if (str == NULL)
|
||||
goto random;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
addr[i] = simple_strtoul(str, &end, 16);
|
||||
if ((end == str) ||
|
||||
((*end != ':') && (*end != ',') && (*end != '\0'))) {
|
||||
printk(KERN_ERR
|
||||
"setup_etheraddr: failed to parse '%s' "
|
||||
"as an ethernet address\n", str);
|
||||
goto random;
|
||||
}
|
||||
str = end + 1;
|
||||
}
|
||||
if (is_multicast_ether_addr(addr)) {
|
||||
printk(KERN_ERR
|
||||
"Attempt to assign a multicast ethernet address to a "
|
||||
"device disallowed\n");
|
||||
goto random;
|
||||
}
|
||||
if (!is_valid_ether_addr(addr)) {
|
||||
printk(KERN_ERR
|
||||
"Attempt to assign an invalid ethernet address to a "
|
||||
"device disallowed\n");
|
||||
goto random;
|
||||
}
|
||||
if (!is_local_ether_addr(addr)) {
|
||||
printk(KERN_WARNING
|
||||
"Warning: Assigning a globally valid ethernet "
|
||||
"address to a device\n");
|
||||
printk(KERN_WARNING "You should set the 2nd rightmost bit in "
|
||||
"the first byte of the MAC,\n");
|
||||
printk(KERN_WARNING "i.e. %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||
addr[0] | 0x02, addr[1], addr[2], addr[3], addr[4],
|
||||
addr[5]);
|
||||
}
|
||||
return;
|
||||
|
||||
random:
|
||||
printk(KERN_INFO
|
||||
"Choosing a random ethernet address for device %s\n", dev->name);
|
||||
eth_hw_addr_random(dev);
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(devices_lock);
|
||||
static LIST_HEAD(devices);
|
||||
|
||||
static struct platform_driver uml_net_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
static void net_device_release(struct device *dev)
|
||||
{
|
||||
struct uml_net *device = dev_get_drvdata(dev);
|
||||
struct net_device *netdev = device->dev;
|
||||
struct uml_net_private *lp = netdev_priv(netdev);
|
||||
|
||||
if (lp->remove != NULL)
|
||||
(*lp->remove)(&lp->user);
|
||||
list_del(&device->list);
|
||||
kfree(device);
|
||||
free_netdev(netdev);
|
||||
}
|
||||
|
||||
static const struct net_device_ops uml_netdev_ops = {
|
||||
.ndo_open = uml_net_open,
|
||||
.ndo_stop = uml_net_close,
|
||||
.ndo_start_xmit = uml_net_start_xmit,
|
||||
.ndo_set_rx_mode = uml_net_set_multicast_list,
|
||||
.ndo_tx_timeout = uml_net_tx_timeout,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_change_mtu = uml_net_change_mtu,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
.ndo_poll_controller = uml_net_poll_controller,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Ensures that platform_driver_register is called only once by
|
||||
* eth_configure. Will be set in an initcall.
|
||||
*/
|
||||
static int driver_registered;
|
||||
|
||||
static void eth_configure(int n, void *init, char *mac,
|
||||
struct transport *transport)
|
||||
{
|
||||
struct uml_net *device;
|
||||
struct net_device *dev;
|
||||
struct uml_net_private *lp;
|
||||
int err, size;
|
||||
|
||||
size = transport->private_size + sizeof(struct uml_net_private);
|
||||
|
||||
device = kzalloc(sizeof(*device), GFP_KERNEL);
|
||||
if (device == NULL) {
|
||||
printk(KERN_ERR "eth_configure failed to allocate struct "
|
||||
"uml_net\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev = alloc_etherdev(size);
|
||||
if (dev == NULL) {
|
||||
printk(KERN_ERR "eth_configure: failed to allocate struct "
|
||||
"net_device for eth%d\n", n);
|
||||
goto out_free_device;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&device->list);
|
||||
device->index = n;
|
||||
|
||||
/* If this name ends up conflicting with an existing registered
|
||||
* netdevice, that is OK, register_netdev{,ice}() will notice this
|
||||
* and fail.
|
||||
*/
|
||||
snprintf(dev->name, sizeof(dev->name), "eth%d", n);
|
||||
|
||||
setup_etheraddr(dev, mac);
|
||||
|
||||
printk(KERN_INFO "Netdevice %d (%pM) : ", n, dev->dev_addr);
|
||||
|
||||
lp = netdev_priv(dev);
|
||||
/* This points to the transport private data. It's still clear, but we
|
||||
* must memset it to 0 *now*. Let's help the drivers. */
|
||||
memset(lp, 0, size);
|
||||
INIT_WORK(&lp->work, uml_dev_close);
|
||||
|
||||
/* sysfs register */
|
||||
if (!driver_registered) {
|
||||
platform_driver_register(¨_net_driver);
|
||||
driver_registered = 1;
|
||||
}
|
||||
device->pdev.id = n;
|
||||
device->pdev.name = DRIVER_NAME;
|
||||
device->pdev.dev.release = net_device_release;
|
||||
dev_set_drvdata(&device->pdev.dev, device);
|
||||
if (platform_device_register(&device->pdev))
|
||||
goto out_free_netdev;
|
||||
SET_NETDEV_DEV(dev,&device->pdev.dev);
|
||||
|
||||
device->dev = dev;
|
||||
|
||||
/*
|
||||
* These just fill in a data structure, so there's no failure
|
||||
* to be worried about.
|
||||
*/
|
||||
(*transport->kern->init)(dev, init);
|
||||
|
||||
*lp = ((struct uml_net_private)
|
||||
{ .list = LIST_HEAD_INIT(lp->list),
|
||||
.dev = dev,
|
||||
.fd = -1,
|
||||
.mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0},
|
||||
.max_packet = transport->user->max_packet,
|
||||
.protocol = transport->kern->protocol,
|
||||
.open = transport->user->open,
|
||||
.close = transport->user->close,
|
||||
.remove = transport->user->remove,
|
||||
.read = transport->kern->read,
|
||||
.write = transport->kern->write,
|
||||
.add_address = transport->user->add_address,
|
||||
.delete_address = transport->user->delete_address });
|
||||
|
||||
init_timer(&lp->tl);
|
||||
spin_lock_init(&lp->lock);
|
||||
lp->tl.function = uml_net_user_timer_expire;
|
||||
memcpy(lp->mac, dev->dev_addr, sizeof(lp->mac));
|
||||
|
||||
if ((transport->user->init != NULL) &&
|
||||
((*transport->user->init)(&lp->user, dev) != 0))
|
||||
goto out_unregister;
|
||||
|
||||
dev->mtu = transport->user->mtu;
|
||||
dev->netdev_ops = ¨_netdev_ops;
|
||||
dev->ethtool_ops = ¨_net_ethtool_ops;
|
||||
dev->watchdog_timeo = (HZ >> 1);
|
||||
dev->irq = UM_ETH_IRQ;
|
||||
|
||||
err = update_drop_skb(lp->max_packet);
|
||||
if (err)
|
||||
goto out_undo_user_init;
|
||||
|
||||
rtnl_lock();
|
||||
err = register_netdevice(dev);
|
||||
rtnl_unlock();
|
||||
if (err)
|
||||
goto out_undo_user_init;
|
||||
|
||||
spin_lock(&devices_lock);
|
||||
list_add(&device->list, &devices);
|
||||
spin_unlock(&devices_lock);
|
||||
|
||||
return;
|
||||
|
||||
out_undo_user_init:
|
||||
if (transport->user->remove != NULL)
|
||||
(*transport->user->remove)(&lp->user);
|
||||
out_unregister:
|
||||
platform_device_unregister(&device->pdev);
|
||||
return; /* platform_device_unregister frees dev and device */
|
||||
out_free_netdev:
|
||||
free_netdev(dev);
|
||||
out_free_device:
|
||||
kfree(device);
|
||||
}
|
||||
|
||||
static struct uml_net *find_device(int n)
|
||||
{
|
||||
struct uml_net *device;
|
||||
struct list_head *ele;
|
||||
|
||||
spin_lock(&devices_lock);
|
||||
list_for_each(ele, &devices) {
|
||||
device = list_entry(ele, struct uml_net, list);
|
||||
if (device->index == n)
|
||||
goto out;
|
||||
}
|
||||
device = NULL;
|
||||
out:
|
||||
spin_unlock(&devices_lock);
|
||||
return device;
|
||||
}
|
||||
|
||||
static int eth_parse(char *str, int *index_out, char **str_out,
|
||||
char **error_out)
|
||||
{
|
||||
char *end;
|
||||
int n, err = -EINVAL;
|
||||
|
||||
n = simple_strtoul(str, &end, 0);
|
||||
if (end == str) {
|
||||
*error_out = "Bad device number";
|
||||
return err;
|
||||
}
|
||||
|
||||
str = end;
|
||||
if (*str != '=') {
|
||||
*error_out = "Expected '=' after device number";
|
||||
return err;
|
||||
}
|
||||
|
||||
str++;
|
||||
if (find_device(n)) {
|
||||
*error_out = "Device already configured";
|
||||
return err;
|
||||
}
|
||||
|
||||
*index_out = n;
|
||||
*str_out = str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct eth_init {
|
||||
struct list_head list;
|
||||
char *init;
|
||||
int index;
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(transports_lock);
|
||||
static LIST_HEAD(transports);
|
||||
|
||||
/* Filled in during early boot */
|
||||
static LIST_HEAD(eth_cmd_line);
|
||||
|
||||
static int check_transport(struct transport *transport, char *eth, int n,
|
||||
void **init_out, char **mac_out)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = strlen(transport->name);
|
||||
if (strncmp(eth, transport->name, len))
|
||||
return 0;
|
||||
|
||||
eth += len;
|
||||
if (*eth == ',')
|
||||
eth++;
|
||||
else if (*eth != '\0')
|
||||
return 0;
|
||||
|
||||
*init_out = kmalloc(transport->setup_size, GFP_KERNEL);
|
||||
if (*init_out == NULL)
|
||||
return 1;
|
||||
|
||||
if (!transport->setup(eth, mac_out, *init_out)) {
|
||||
kfree(*init_out);
|
||||
*init_out = NULL;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void register_transport(struct transport *new)
|
||||
{
|
||||
struct list_head *ele, *next;
|
||||
struct eth_init *eth;
|
||||
void *init;
|
||||
char *mac = NULL;
|
||||
int match;
|
||||
|
||||
spin_lock(&transports_lock);
|
||||
BUG_ON(!list_empty(&new->list));
|
||||
list_add(&new->list, &transports);
|
||||
spin_unlock(&transports_lock);
|
||||
|
||||
list_for_each_safe(ele, next, ð_cmd_line) {
|
||||
eth = list_entry(ele, struct eth_init, list);
|
||||
match = check_transport(new, eth->init, eth->index, &init,
|
||||
&mac);
|
||||
if (!match)
|
||||
continue;
|
||||
else if (init != NULL) {
|
||||
eth_configure(eth->index, init, mac, new);
|
||||
kfree(init);
|
||||
}
|
||||
list_del(ð->list);
|
||||
}
|
||||
}
|
||||
|
||||
static int eth_setup_common(char *str, int index)
|
||||
{
|
||||
struct list_head *ele;
|
||||
struct transport *transport;
|
||||
void *init;
|
||||
char *mac = NULL;
|
||||
int found = 0;
|
||||
|
||||
spin_lock(&transports_lock);
|
||||
list_for_each(ele, &transports) {
|
||||
transport = list_entry(ele, struct transport, list);
|
||||
if (!check_transport(transport, str, index, &init, &mac))
|
||||
continue;
|
||||
if (init != NULL) {
|
||||
eth_configure(index, init, mac, transport);
|
||||
kfree(init);
|
||||
}
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&transports_lock);
|
||||
return found;
|
||||
}
|
||||
|
||||
static int __init eth_setup(char *str)
|
||||
{
|
||||
struct eth_init *new;
|
||||
char *error;
|
||||
int n, err;
|
||||
|
||||
err = eth_parse(str, &n, &str, &error);
|
||||
if (err) {
|
||||
printk(KERN_ERR "eth_setup - Couldn't parse '%s' : %s\n",
|
||||
str, error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
new = alloc_bootmem(sizeof(*new));
|
||||
|
||||
INIT_LIST_HEAD(&new->list);
|
||||
new->index = n;
|
||||
new->init = str;
|
||||
|
||||
list_add_tail(&new->list, ð_cmd_line);
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("eth", eth_setup);
|
||||
__uml_help(eth_setup,
|
||||
"eth[0-9]+=<transport>,<options>\n"
|
||||
" Configure a network device.\n\n"
|
||||
);
|
||||
|
||||
static int net_config(char *str, char **error_out)
|
||||
{
|
||||
int n, err;
|
||||
|
||||
err = eth_parse(str, &n, &str, error_out);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* This string is broken up and the pieces used by the underlying
|
||||
* driver. So, it is freed only if eth_setup_common fails.
|
||||
*/
|
||||
str = kstrdup(str, GFP_KERNEL);
|
||||
if (str == NULL) {
|
||||
*error_out = "net_config failed to strdup string";
|
||||
return -ENOMEM;
|
||||
}
|
||||
err = !eth_setup_common(str, n);
|
||||
if (err)
|
||||
kfree(str);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int net_id(char **str, int *start_out, int *end_out)
|
||||
{
|
||||
char *end;
|
||||
int n;
|
||||
|
||||
n = simple_strtoul(*str, &end, 0);
|
||||
if ((*end != '\0') || (end == *str))
|
||||
return -1;
|
||||
|
||||
*start_out = n;
|
||||
*end_out = n;
|
||||
*str = end;
|
||||
return n;
|
||||
}
|
||||
|
||||
static int net_remove(int n, char **error_out)
|
||||
{
|
||||
struct uml_net *device;
|
||||
struct net_device *dev;
|
||||
struct uml_net_private *lp;
|
||||
|
||||
device = find_device(n);
|
||||
if (device == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
dev = device->dev;
|
||||
lp = netdev_priv(dev);
|
||||
if (lp->fd > 0)
|
||||
return -EBUSY;
|
||||
unregister_netdev(dev);
|
||||
platform_device_unregister(&device->pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mc_device net_mc = {
|
||||
.list = LIST_HEAD_INIT(net_mc.list),
|
||||
.name = "eth",
|
||||
.config = net_config,
|
||||
.get_config = NULL,
|
||||
.id = net_id,
|
||||
.remove = net_remove,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_INET
|
||||
static int uml_inetaddr_event(struct notifier_block *this, unsigned long event,
|
||||
void *ptr)
|
||||
{
|
||||
struct in_ifaddr *ifa = ptr;
|
||||
struct net_device *dev = ifa->ifa_dev->dev;
|
||||
struct uml_net_private *lp;
|
||||
void (*proc)(unsigned char *, unsigned char *, void *);
|
||||
unsigned char addr_buf[4], netmask_buf[4];
|
||||
|
||||
if (dev->netdev_ops->ndo_open != uml_net_open)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
lp = netdev_priv(dev);
|
||||
|
||||
proc = NULL;
|
||||
switch (event) {
|
||||
case NETDEV_UP:
|
||||
proc = lp->add_address;
|
||||
break;
|
||||
case NETDEV_DOWN:
|
||||
proc = lp->delete_address;
|
||||
break;
|
||||
}
|
||||
if (proc != NULL) {
|
||||
memcpy(addr_buf, &ifa->ifa_address, sizeof(addr_buf));
|
||||
memcpy(netmask_buf, &ifa->ifa_mask, sizeof(netmask_buf));
|
||||
(*proc)(addr_buf, netmask_buf, &lp->user);
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/* uml_net_init shouldn't be called twice on two CPUs at the same time */
|
||||
static struct notifier_block uml_inetaddr_notifier = {
|
||||
.notifier_call = uml_inetaddr_event,
|
||||
};
|
||||
|
||||
static void inet_register(void)
|
||||
{
|
||||
struct list_head *ele;
|
||||
struct uml_net_private *lp;
|
||||
struct in_device *ip;
|
||||
struct in_ifaddr *in;
|
||||
|
||||
register_inetaddr_notifier(¨_inetaddr_notifier);
|
||||
|
||||
/* Devices may have been opened already, so the uml_inetaddr_notifier
|
||||
* didn't get a chance to run for them. This fakes it so that
|
||||
* addresses which have already been set up get handled properly.
|
||||
*/
|
||||
spin_lock(&opened_lock);
|
||||
list_for_each(ele, &opened) {
|
||||
lp = list_entry(ele, struct uml_net_private, list);
|
||||
ip = lp->dev->ip_ptr;
|
||||
if (ip == NULL)
|
||||
continue;
|
||||
in = ip->ifa_list;
|
||||
while (in != NULL) {
|
||||
uml_inetaddr_event(NULL, NETDEV_UP, in);
|
||||
in = in->ifa_next;
|
||||
}
|
||||
}
|
||||
spin_unlock(&opened_lock);
|
||||
}
|
||||
#else
|
||||
static inline void inet_register(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static int uml_net_init(void)
|
||||
{
|
||||
mconsole_register_dev(&net_mc);
|
||||
inet_register();
|
||||
return 0;
|
||||
}
|
||||
|
||||
__initcall(uml_net_init);
|
||||
|
||||
static void close_devices(void)
|
||||
{
|
||||
struct list_head *ele;
|
||||
struct uml_net_private *lp;
|
||||
|
||||
spin_lock(&opened_lock);
|
||||
list_for_each(ele, &opened) {
|
||||
lp = list_entry(ele, struct uml_net_private, list);
|
||||
um_free_irq(lp->dev->irq, lp->dev);
|
||||
if ((lp->close != NULL) && (lp->fd >= 0))
|
||||
(*lp->close)(lp->fd, &lp->user);
|
||||
if (lp->remove != NULL)
|
||||
(*lp->remove)(&lp->user);
|
||||
}
|
||||
spin_unlock(&opened_lock);
|
||||
}
|
||||
|
||||
__uml_exitcall(close_devices);
|
||||
|
||||
void iter_addresses(void *d, void (*cb)(unsigned char *, unsigned char *,
|
||||
void *),
|
||||
void *arg)
|
||||
{
|
||||
struct net_device *dev = d;
|
||||
struct in_device *ip = dev->ip_ptr;
|
||||
struct in_ifaddr *in;
|
||||
unsigned char address[4], netmask[4];
|
||||
|
||||
if (ip == NULL) return;
|
||||
in = ip->ifa_list;
|
||||
while (in != NULL) {
|
||||
memcpy(address, &in->ifa_address, sizeof(address));
|
||||
memcpy(netmask, &in->ifa_mask, sizeof(netmask));
|
||||
(*cb)(address, netmask, arg);
|
||||
in = in->ifa_next;
|
||||
}
|
||||
}
|
||||
|
||||
int dev_netmask(void *d, void *m)
|
||||
{
|
||||
struct net_device *dev = d;
|
||||
struct in_device *ip = dev->ip_ptr;
|
||||
struct in_ifaddr *in;
|
||||
__be32 *mask_out = m;
|
||||
|
||||
if (ip == NULL)
|
||||
return 1;
|
||||
|
||||
in = ip->ifa_list;
|
||||
if (in == NULL)
|
||||
return 1;
|
||||
|
||||
*mask_out = in->ifa_mask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *get_output_buffer(int *len_out)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
ret = (void *) __get_free_pages(GFP_KERNEL, 0);
|
||||
if (ret) *len_out = PAGE_SIZE;
|
||||
else *len_out = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void free_output_buffer(void *buffer)
|
||||
{
|
||||
free_pages((unsigned long) buffer, 0);
|
||||
}
|
||||
|
||||
int tap_setup_common(char *str, char *type, char **dev_name, char **mac_out,
|
||||
char **gate_addr)
|
||||
{
|
||||
char *remain;
|
||||
|
||||
remain = split_if_spec(str, dev_name, mac_out, gate_addr, NULL);
|
||||
if (remain != NULL) {
|
||||
printk(KERN_ERR "tap_setup_common - Extra garbage on "
|
||||
"specification : '%s'\n", remain);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned short eth_protocol(struct sk_buff *skb)
|
||||
{
|
||||
return eth_type_trans(skb, skb->dev);
|
||||
}
|
269
arch/um/drivers/net_user.c
Normal file
269
arch/um/drivers/net_user.c
Normal file
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <net_user.h>
|
||||
#include <os.h>
|
||||
#include <um_malloc.h>
|
||||
|
||||
int tap_open_common(void *dev, char *gate_addr)
|
||||
{
|
||||
int tap_addr[4];
|
||||
|
||||
if (gate_addr == NULL)
|
||||
return 0;
|
||||
if (sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0],
|
||||
&tap_addr[1], &tap_addr[2], &tap_addr[3]) != 4) {
|
||||
printk(UM_KERN_ERR "Invalid tap IP address - '%s'\n",
|
||||
gate_addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tap_check_ips(char *gate_addr, unsigned char *eth_addr)
|
||||
{
|
||||
int tap_addr[4];
|
||||
|
||||
if ((gate_addr != NULL) &&
|
||||
(sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0],
|
||||
&tap_addr[1], &tap_addr[2], &tap_addr[3]) == 4) &&
|
||||
(eth_addr[0] == tap_addr[0]) &&
|
||||
(eth_addr[1] == tap_addr[1]) &&
|
||||
(eth_addr[2] == tap_addr[2]) &&
|
||||
(eth_addr[3] == tap_addr[3])) {
|
||||
printk(UM_KERN_ERR "The tap IP address and the UML eth IP "
|
||||
"address must be different\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Do reliable error handling as this fails frequently enough. */
|
||||
void read_output(int fd, char *output, int len)
|
||||
{
|
||||
int remain, ret, expected;
|
||||
char c;
|
||||
char *str;
|
||||
|
||||
if (output == NULL) {
|
||||
output = &c;
|
||||
len = sizeof(c);
|
||||
}
|
||||
|
||||
*output = '\0';
|
||||
ret = read(fd, &remain, sizeof(remain));
|
||||
|
||||
if (ret != sizeof(remain)) {
|
||||
if (ret < 0)
|
||||
ret = -errno;
|
||||
expected = sizeof(remain);
|
||||
str = "length";
|
||||
goto err;
|
||||
}
|
||||
|
||||
while (remain != 0) {
|
||||
expected = (remain < len) ? remain : len;
|
||||
ret = read(fd, output, expected);
|
||||
if (ret != expected) {
|
||||
if (ret < 0)
|
||||
ret = -errno;
|
||||
str = "data";
|
||||
goto err;
|
||||
}
|
||||
remain -= ret;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
if (ret < 0)
|
||||
printk(UM_KERN_ERR "read_output - read of %s failed, "
|
||||
"errno = %d\n", str, -ret);
|
||||
else
|
||||
printk(UM_KERN_ERR "read_output - read of %s failed, read only "
|
||||
"%d of %d bytes\n", str, ret, expected);
|
||||
}
|
||||
|
||||
int net_read(int fd, void *buf, int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = read(fd, buf, len);
|
||||
|
||||
if ((n < 0) && (errno == EAGAIN))
|
||||
return 0;
|
||||
else if (n == 0)
|
||||
return -ENOTCONN;
|
||||
return n;
|
||||
}
|
||||
|
||||
int net_recvfrom(int fd, void *buf, int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
CATCH_EINTR(n = recvfrom(fd, buf, len, 0, NULL, NULL));
|
||||
if (n < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
return -errno;
|
||||
}
|
||||
else if (n == 0)
|
||||
return -ENOTCONN;
|
||||
return n;
|
||||
}
|
||||
|
||||
int net_write(int fd, void *buf, int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = write(fd, buf, len);
|
||||
|
||||
if ((n < 0) && (errno == EAGAIN))
|
||||
return 0;
|
||||
else if (n == 0)
|
||||
return -ENOTCONN;
|
||||
return n;
|
||||
}
|
||||
|
||||
int net_send(int fd, void *buf, int len)
|
||||
{
|
||||
int n;
|
||||
|
||||
CATCH_EINTR(n = send(fd, buf, len, 0));
|
||||
if (n < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
return -errno;
|
||||
}
|
||||
else if (n == 0)
|
||||
return -ENOTCONN;
|
||||
return n;
|
||||
}
|
||||
|
||||
int net_sendto(int fd, void *buf, int len, void *to, int sock_len)
|
||||
{
|
||||
int n;
|
||||
|
||||
CATCH_EINTR(n = sendto(fd, buf, len, 0, (struct sockaddr *) to,
|
||||
sock_len));
|
||||
if (n < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
return -errno;
|
||||
}
|
||||
else if (n == 0)
|
||||
return -ENOTCONN;
|
||||
return n;
|
||||
}
|
||||
|
||||
struct change_pre_exec_data {
|
||||
int close_me;
|
||||
int stdout;
|
||||
};
|
||||
|
||||
static void change_pre_exec(void *arg)
|
||||
{
|
||||
struct change_pre_exec_data *data = arg;
|
||||
|
||||
close(data->close_me);
|
||||
dup2(data->stdout, 1);
|
||||
}
|
||||
|
||||
static int change_tramp(char **argv, char *output, int output_len)
|
||||
{
|
||||
int pid, fds[2], err;
|
||||
struct change_pre_exec_data pe_data;
|
||||
|
||||
err = os_pipe(fds, 1, 0);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "change_tramp - pipe failed, err = %d\n",
|
||||
-err);
|
||||
return err;
|
||||
}
|
||||
pe_data.close_me = fds[0];
|
||||
pe_data.stdout = fds[1];
|
||||
pid = run_helper(change_pre_exec, &pe_data, argv);
|
||||
|
||||
if (pid > 0) /* Avoid hang as we won't get data in failure case. */
|
||||
read_output(fds[0], output, output_len);
|
||||
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
|
||||
if (pid > 0)
|
||||
helper_wait(pid);
|
||||
return pid;
|
||||
}
|
||||
|
||||
static void change(char *dev, char *what, unsigned char *addr,
|
||||
unsigned char *netmask)
|
||||
{
|
||||
char addr_buf[sizeof("255.255.255.255\0")];
|
||||
char netmask_buf[sizeof("255.255.255.255\0")];
|
||||
char version[sizeof("nnnnn\0")];
|
||||
char *argv[] = { "uml_net", version, what, dev, addr_buf,
|
||||
netmask_buf, NULL };
|
||||
char *output;
|
||||
int output_len, pid;
|
||||
|
||||
sprintf(version, "%d", UML_NET_VERSION);
|
||||
sprintf(addr_buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
|
||||
sprintf(netmask_buf, "%d.%d.%d.%d", netmask[0], netmask[1],
|
||||
netmask[2], netmask[3]);
|
||||
|
||||
output_len = UM_KERN_PAGE_SIZE;
|
||||
output = uml_kmalloc(output_len, UM_GFP_KERNEL);
|
||||
if (output == NULL)
|
||||
printk(UM_KERN_ERR "change : failed to allocate output "
|
||||
"buffer\n");
|
||||
|
||||
pid = change_tramp(argv, output, output_len);
|
||||
if (pid < 0) {
|
||||
kfree(output);
|
||||
return;
|
||||
}
|
||||
|
||||
if (output != NULL) {
|
||||
printk("%s", output);
|
||||
kfree(output);
|
||||
}
|
||||
}
|
||||
|
||||
void open_addr(unsigned char *addr, unsigned char *netmask, void *arg)
|
||||
{
|
||||
change(arg, "add", addr, netmask);
|
||||
}
|
||||
|
||||
void close_addr(unsigned char *addr, unsigned char *netmask, void *arg)
|
||||
{
|
||||
change(arg, "del", addr, netmask);
|
||||
}
|
||||
|
||||
char *split_if_spec(char *str, ...)
|
||||
{
|
||||
char **arg, *end;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, str);
|
||||
while ((arg = va_arg(ap, char **)) != NULL) {
|
||||
if (*str == '\0')
|
||||
return NULL;
|
||||
end = strchr(str, ',');
|
||||
if (end != str)
|
||||
*arg = str;
|
||||
if (end == NULL)
|
||||
return NULL;
|
||||
*end++ = '\0';
|
||||
str = end;
|
||||
}
|
||||
va_end(ap);
|
||||
return str;
|
||||
}
|
51
arch/um/drivers/null.c
Normal file
51
arch/um/drivers/null.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include "chan_user.h"
|
||||
#include <os.h>
|
||||
|
||||
/* This address is used only as a unique identifier */
|
||||
static int null_chan;
|
||||
|
||||
static void *null_init(char *str, int device, const struct chan_opts *opts)
|
||||
{
|
||||
return &null_chan;
|
||||
}
|
||||
|
||||
static int null_open(int input, int output, int primary, void *d,
|
||||
char **dev_out)
|
||||
{
|
||||
int fd;
|
||||
|
||||
*dev_out = NULL;
|
||||
|
||||
fd = open(DEV_NULL, O_RDWR);
|
||||
return (fd < 0) ? -errno : fd;
|
||||
}
|
||||
|
||||
static int null_read(int fd, char *c_out, void *unused)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void null_free(void *data)
|
||||
{
|
||||
}
|
||||
|
||||
const struct chan_ops null_ops = {
|
||||
.type = "null",
|
||||
.init = null_init,
|
||||
.open = null_open,
|
||||
.close = generic_close,
|
||||
.read = null_read,
|
||||
.write = generic_write,
|
||||
.console_write = generic_console_write,
|
||||
.window_size = generic_window_size,
|
||||
.free = null_free,
|
||||
.winch = 0,
|
||||
};
|
113
arch/um/drivers/pcap_kern.c
Normal file
113
arch/um/drivers/pcap_kern.c
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <net_kern.h>
|
||||
#include "pcap_user.h"
|
||||
|
||||
struct pcap_init {
|
||||
char *host_if;
|
||||
int promisc;
|
||||
int optimize;
|
||||
char *filter;
|
||||
};
|
||||
|
||||
void pcap_init(struct net_device *dev, void *data)
|
||||
{
|
||||
struct uml_net_private *pri;
|
||||
struct pcap_data *ppri;
|
||||
struct pcap_init *init = data;
|
||||
|
||||
pri = netdev_priv(dev);
|
||||
ppri = (struct pcap_data *) pri->user;
|
||||
ppri->host_if = init->host_if;
|
||||
ppri->promisc = init->promisc;
|
||||
ppri->optimize = init->optimize;
|
||||
ppri->filter = init->filter;
|
||||
|
||||
printk("pcap backend, host interface %s\n", ppri->host_if);
|
||||
}
|
||||
|
||||
static int pcap_read(int fd, struct sk_buff *skb, struct uml_net_private *lp)
|
||||
{
|
||||
return pcap_user_read(fd, skb_mac_header(skb),
|
||||
skb->dev->mtu + ETH_HEADER_OTHER,
|
||||
(struct pcap_data *) &lp->user);
|
||||
}
|
||||
|
||||
static int pcap_write(int fd, struct sk_buff *skb, struct uml_net_private *lp)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
static const struct net_kern_info pcap_kern_info = {
|
||||
.init = pcap_init,
|
||||
.protocol = eth_protocol,
|
||||
.read = pcap_read,
|
||||
.write = pcap_write,
|
||||
};
|
||||
|
||||
int pcap_setup(char *str, char **mac_out, void *data)
|
||||
{
|
||||
struct pcap_init *init = data;
|
||||
char *remain, *host_if = NULL, *options[2] = { NULL, NULL };
|
||||
int i;
|
||||
|
||||
*init = ((struct pcap_init)
|
||||
{ .host_if = "eth0",
|
||||
.promisc = 1,
|
||||
.optimize = 0,
|
||||
.filter = NULL });
|
||||
|
||||
remain = split_if_spec(str, &host_if, &init->filter,
|
||||
&options[0], &options[1], mac_out, NULL);
|
||||
if (remain != NULL) {
|
||||
printk(KERN_ERR "pcap_setup - Extra garbage on "
|
||||
"specification : '%s'\n", remain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (host_if != NULL)
|
||||
init->host_if = host_if;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(options); i++) {
|
||||
if (options[i] == NULL)
|
||||
continue;
|
||||
if (!strcmp(options[i], "promisc"))
|
||||
init->promisc = 1;
|
||||
else if (!strcmp(options[i], "nopromisc"))
|
||||
init->promisc = 0;
|
||||
else if (!strcmp(options[i], "optimize"))
|
||||
init->optimize = 1;
|
||||
else if (!strcmp(options[i], "nooptimize"))
|
||||
init->optimize = 0;
|
||||
else {
|
||||
printk(KERN_ERR "pcap_setup : bad option - '%s'\n",
|
||||
options[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct transport pcap_transport = {
|
||||
.list = LIST_HEAD_INIT(pcap_transport.list),
|
||||
.name = "pcap",
|
||||
.setup = pcap_setup,
|
||||
.user = &pcap_user_info,
|
||||
.kern = &pcap_kern_info,
|
||||
.private_size = sizeof(struct pcap_data),
|
||||
.setup_size = sizeof(struct pcap_init),
|
||||
};
|
||||
|
||||
static int register_pcap(void)
|
||||
{
|
||||
register_transport(&pcap_transport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(register_pcap);
|
137
arch/um/drivers/pcap_user.c
Normal file
137
arch/um/drivers/pcap_user.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <pcap.h>
|
||||
#include <string.h>
|
||||
#include <asm/types.h>
|
||||
#include <net_user.h>
|
||||
#include "pcap_user.h"
|
||||
#include <um_malloc.h>
|
||||
|
||||
#define PCAP_FD(p) (*(int *)(p))
|
||||
|
||||
static int pcap_user_init(void *data, void *dev)
|
||||
{
|
||||
struct pcap_data *pri = data;
|
||||
pcap_t *p;
|
||||
char errors[PCAP_ERRBUF_SIZE];
|
||||
|
||||
p = pcap_open_live(pri->host_if, ETH_MAX_PACKET + ETH_HEADER_OTHER,
|
||||
pri->promisc, 0, errors);
|
||||
if (p == NULL) {
|
||||
printk(UM_KERN_ERR "pcap_user_init : pcap_open_live failed - "
|
||||
"'%s'\n", errors);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pri->dev = dev;
|
||||
pri->pcap = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcap_open(void *data)
|
||||
{
|
||||
struct pcap_data *pri = data;
|
||||
__u32 netmask;
|
||||
int err;
|
||||
|
||||
if (pri->pcap == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (pri->filter != NULL) {
|
||||
err = dev_netmask(pri->dev, &netmask);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "pcap_open : dev_netmask failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pri->compiled = uml_kmalloc(sizeof(struct bpf_program),
|
||||
UM_GFP_KERNEL);
|
||||
if (pri->compiled == NULL) {
|
||||
printk(UM_KERN_ERR "pcap_open : kmalloc failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = pcap_compile(pri->pcap,
|
||||
(struct bpf_program *) pri->compiled,
|
||||
pri->filter, pri->optimize, netmask);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "pcap_open : pcap_compile failed - "
|
||||
"'%s'\n", pcap_geterr(pri->pcap));
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = pcap_setfilter(pri->pcap, pri->compiled);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "pcap_open : pcap_setfilter "
|
||||
"failed - '%s'\n", pcap_geterr(pri->pcap));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
return PCAP_FD(pri->pcap);
|
||||
|
||||
out:
|
||||
kfree(pri->compiled);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void pcap_remove(void *data)
|
||||
{
|
||||
struct pcap_data *pri = data;
|
||||
|
||||
if (pri->compiled != NULL)
|
||||
pcap_freecode(pri->compiled);
|
||||
|
||||
if (pri->pcap != NULL)
|
||||
pcap_close(pri->pcap);
|
||||
}
|
||||
|
||||
struct pcap_handler_data {
|
||||
char *buffer;
|
||||
int len;
|
||||
};
|
||||
|
||||
static void handler(u_char *data, const struct pcap_pkthdr *header,
|
||||
const u_char *packet)
|
||||
{
|
||||
int len;
|
||||
|
||||
struct pcap_handler_data *hdata = (struct pcap_handler_data *) data;
|
||||
|
||||
len = hdata->len < header->caplen ? hdata->len : header->caplen;
|
||||
memcpy(hdata->buffer, packet, len);
|
||||
hdata->len = len;
|
||||
}
|
||||
|
||||
int pcap_user_read(int fd, void *buffer, int len, struct pcap_data *pri)
|
||||
{
|
||||
struct pcap_handler_data hdata = ((struct pcap_handler_data)
|
||||
{ .buffer = buffer,
|
||||
.len = len });
|
||||
int n;
|
||||
|
||||
n = pcap_dispatch(pri->pcap, 1, handler, (u_char *) &hdata);
|
||||
if (n < 0) {
|
||||
printk(UM_KERN_ERR "pcap_dispatch failed - %s\n",
|
||||
pcap_geterr(pri->pcap));
|
||||
return -EIO;
|
||||
}
|
||||
else if (n == 0)
|
||||
return 0;
|
||||
return hdata.len;
|
||||
}
|
||||
|
||||
const struct net_user_info pcap_user_info = {
|
||||
.init = pcap_user_init,
|
||||
.open = pcap_open,
|
||||
.close = NULL,
|
||||
.remove = pcap_remove,
|
||||
.add_address = NULL,
|
||||
.delete_address = NULL,
|
||||
.mtu = ETH_MAX_PACKET,
|
||||
.max_packet = ETH_MAX_PACKET + ETH_HEADER_OTHER,
|
||||
};
|
21
arch/um/drivers/pcap_user.h
Normal file
21
arch/um/drivers/pcap_user.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <net_user.h>
|
||||
|
||||
struct pcap_data {
|
||||
char *host_if;
|
||||
int promisc;
|
||||
int optimize;
|
||||
char *filter;
|
||||
void *compiled;
|
||||
void *pcap;
|
||||
void *dev;
|
||||
};
|
||||
|
||||
extern const struct net_user_info pcap_user_info;
|
||||
|
||||
extern int pcap_user_read(int fd, void *buf, int len, struct pcap_data *pri);
|
||||
|
20
arch/um/drivers/port.h
Normal file
20
arch/um/drivers/port.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __PORT_H__
|
||||
#define __PORT_H__
|
||||
|
||||
extern void *port_data(int port);
|
||||
extern int port_wait(void *data);
|
||||
extern void port_kern_close(void *d);
|
||||
extern int port_connection(int fd, int *socket_out, int *pid_out);
|
||||
extern int port_listen_fd(int port);
|
||||
extern void port_read(int fd, void *data);
|
||||
extern void port_kern_free(void *d);
|
||||
extern int port_rcv_fd(int fd);
|
||||
extern void port_remove_dev(void *d);
|
||||
|
||||
#endif
|
||||
|
304
arch/um/drivers/port_kern.c
Normal file
304
arch/um/drivers/port_kern.c
Normal file
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <init.h>
|
||||
#include <irq_kern.h>
|
||||
#include <os.h>
|
||||
#include "port.h"
|
||||
|
||||
struct port_list {
|
||||
struct list_head list;
|
||||
atomic_t wait_count;
|
||||
int has_connection;
|
||||
struct completion done;
|
||||
int port;
|
||||
int fd;
|
||||
spinlock_t lock;
|
||||
struct list_head pending;
|
||||
struct list_head connections;
|
||||
};
|
||||
|
||||
struct port_dev {
|
||||
struct port_list *port;
|
||||
int helper_pid;
|
||||
int telnetd_pid;
|
||||
};
|
||||
|
||||
struct connection {
|
||||
struct list_head list;
|
||||
int fd;
|
||||
int helper_pid;
|
||||
int socket[2];
|
||||
int telnetd_pid;
|
||||
struct port_list *port;
|
||||
};
|
||||
|
||||
static irqreturn_t pipe_interrupt(int irq, void *data)
|
||||
{
|
||||
struct connection *conn = data;
|
||||
int fd;
|
||||
|
||||
fd = os_rcv_fd(conn->socket[0], &conn->helper_pid);
|
||||
if (fd < 0) {
|
||||
if (fd == -EAGAIN)
|
||||
return IRQ_NONE;
|
||||
|
||||
printk(KERN_ERR "pipe_interrupt : os_rcv_fd returned %d\n",
|
||||
-fd);
|
||||
os_close_file(conn->fd);
|
||||
}
|
||||
|
||||
list_del(&conn->list);
|
||||
|
||||
conn->fd = fd;
|
||||
list_add(&conn->list, &conn->port->connections);
|
||||
|
||||
complete(&conn->port->done);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#define NO_WAITER_MSG \
|
||||
"****\n" \
|
||||
"There are currently no UML consoles waiting for port connections.\n" \
|
||||
"Either disconnect from one to make it available or activate some more\n" \
|
||||
"by enabling more consoles in the UML /etc/inittab.\n" \
|
||||
"****\n"
|
||||
|
||||
static int port_accept(struct port_list *port)
|
||||
{
|
||||
struct connection *conn;
|
||||
int fd, socket[2], pid;
|
||||
|
||||
fd = port_connection(port->fd, socket, &pid);
|
||||
if (fd < 0) {
|
||||
if (fd != -EAGAIN)
|
||||
printk(KERN_ERR "port_accept : port_connection "
|
||||
"returned %d\n", -fd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
conn = kmalloc(sizeof(*conn), GFP_ATOMIC);
|
||||
if (conn == NULL) {
|
||||
printk(KERN_ERR "port_accept : failed to allocate "
|
||||
"connection\n");
|
||||
goto out_close;
|
||||
}
|
||||
*conn = ((struct connection)
|
||||
{ .list = LIST_HEAD_INIT(conn->list),
|
||||
.fd = fd,
|
||||
.socket = { socket[0], socket[1] },
|
||||
.telnetd_pid = pid,
|
||||
.port = port });
|
||||
|
||||
if (um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt,
|
||||
IRQF_SHARED, "telnetd", conn)) {
|
||||
printk(KERN_ERR "port_accept : failed to get IRQ for "
|
||||
"telnetd\n");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (atomic_read(&port->wait_count) == 0) {
|
||||
os_write_file(fd, NO_WAITER_MSG, sizeof(NO_WAITER_MSG));
|
||||
printk(KERN_ERR "No one waiting for port\n");
|
||||
}
|
||||
list_add(&conn->list, &port->pending);
|
||||
return 1;
|
||||
|
||||
out_free:
|
||||
kfree(conn);
|
||||
out_close:
|
||||
os_close_file(fd);
|
||||
os_kill_process(pid, 1);
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_MUTEX(ports_mutex);
|
||||
static LIST_HEAD(ports);
|
||||
|
||||
static void port_work_proc(struct work_struct *unused)
|
||||
{
|
||||
struct port_list *port;
|
||||
struct list_head *ele;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
list_for_each(ele, &ports) {
|
||||
port = list_entry(ele, struct port_list, list);
|
||||
if (!port->has_connection)
|
||||
continue;
|
||||
|
||||
reactivate_fd(port->fd, ACCEPT_IRQ);
|
||||
while (port_accept(port))
|
||||
;
|
||||
port->has_connection = 0;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
DECLARE_WORK(port_work, port_work_proc);
|
||||
|
||||
static irqreturn_t port_interrupt(int irq, void *data)
|
||||
{
|
||||
struct port_list *port = data;
|
||||
|
||||
port->has_connection = 1;
|
||||
schedule_work(&port_work);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void *port_data(int port_num)
|
||||
{
|
||||
struct list_head *ele;
|
||||
struct port_list *port;
|
||||
struct port_dev *dev = NULL;
|
||||
int fd;
|
||||
|
||||
mutex_lock(&ports_mutex);
|
||||
list_for_each(ele, &ports) {
|
||||
port = list_entry(ele, struct port_list, list);
|
||||
if (port->port == port_num)
|
||||
goto found;
|
||||
}
|
||||
port = kmalloc(sizeof(struct port_list), GFP_KERNEL);
|
||||
if (port == NULL) {
|
||||
printk(KERN_ERR "Allocation of port list failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = port_listen_fd(port_num);
|
||||
if (fd < 0) {
|
||||
printk(KERN_ERR "binding to port %d failed, errno = %d\n",
|
||||
port_num, -fd);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt,
|
||||
IRQF_SHARED, "port", port)) {
|
||||
printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
*port = ((struct port_list)
|
||||
{ .list = LIST_HEAD_INIT(port->list),
|
||||
.wait_count = ATOMIC_INIT(0),
|
||||
.has_connection = 0,
|
||||
.port = port_num,
|
||||
.fd = fd,
|
||||
.pending = LIST_HEAD_INIT(port->pending),
|
||||
.connections = LIST_HEAD_INIT(port->connections) });
|
||||
spin_lock_init(&port->lock);
|
||||
init_completion(&port->done);
|
||||
list_add(&port->list, &ports);
|
||||
|
||||
found:
|
||||
dev = kmalloc(sizeof(struct port_dev), GFP_KERNEL);
|
||||
if (dev == NULL) {
|
||||
printk(KERN_ERR "Allocation of port device entry failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
*dev = ((struct port_dev) { .port = port,
|
||||
.helper_pid = -1,
|
||||
.telnetd_pid = -1 });
|
||||
goto out;
|
||||
|
||||
out_close:
|
||||
os_close_file(fd);
|
||||
out_free:
|
||||
kfree(port);
|
||||
out:
|
||||
mutex_unlock(&ports_mutex);
|
||||
return dev;
|
||||
}
|
||||
|
||||
int port_wait(void *data)
|
||||
{
|
||||
struct port_dev *dev = data;
|
||||
struct connection *conn;
|
||||
struct port_list *port = dev->port;
|
||||
int fd;
|
||||
|
||||
atomic_inc(&port->wait_count);
|
||||
while (1) {
|
||||
fd = -ERESTARTSYS;
|
||||
if (wait_for_completion_interruptible(&port->done))
|
||||
goto out;
|
||||
|
||||
spin_lock(&port->lock);
|
||||
|
||||
conn = list_entry(port->connections.next, struct connection,
|
||||
list);
|
||||
list_del(&conn->list);
|
||||
spin_unlock(&port->lock);
|
||||
|
||||
os_shutdown_socket(conn->socket[0], 1, 1);
|
||||
os_close_file(conn->socket[0]);
|
||||
os_shutdown_socket(conn->socket[1], 1, 1);
|
||||
os_close_file(conn->socket[1]);
|
||||
|
||||
/* This is done here because freeing an IRQ can't be done
|
||||
* within the IRQ handler. So, pipe_interrupt always ups
|
||||
* the semaphore regardless of whether it got a successful
|
||||
* connection. Then we loop here throwing out failed
|
||||
* connections until a good one is found.
|
||||
*/
|
||||
um_free_irq(TELNETD_IRQ, conn);
|
||||
|
||||
if (conn->fd >= 0)
|
||||
break;
|
||||
os_close_file(conn->fd);
|
||||
kfree(conn);
|
||||
}
|
||||
|
||||
fd = conn->fd;
|
||||
dev->helper_pid = conn->helper_pid;
|
||||
dev->telnetd_pid = conn->telnetd_pid;
|
||||
kfree(conn);
|
||||
out:
|
||||
atomic_dec(&port->wait_count);
|
||||
return fd;
|
||||
}
|
||||
|
||||
void port_remove_dev(void *d)
|
||||
{
|
||||
struct port_dev *dev = d;
|
||||
|
||||
if (dev->helper_pid != -1)
|
||||
os_kill_process(dev->helper_pid, 0);
|
||||
if (dev->telnetd_pid != -1)
|
||||
os_kill_process(dev->telnetd_pid, 1);
|
||||
dev->helper_pid = -1;
|
||||
dev->telnetd_pid = -1;
|
||||
}
|
||||
|
||||
void port_kern_free(void *d)
|
||||
{
|
||||
struct port_dev *dev = d;
|
||||
|
||||
port_remove_dev(dev);
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
static void free_port(void)
|
||||
{
|
||||
struct list_head *ele;
|
||||
struct port_list *port;
|
||||
|
||||
list_for_each(ele, &ports) {
|
||||
port = list_entry(ele, struct port_list, list);
|
||||
free_irq_by_fd(port->fd);
|
||||
os_close_file(port->fd);
|
||||
}
|
||||
}
|
||||
|
||||
__uml_exitcall(free_port);
|
201
arch/um/drivers/port_user.c
Normal file
201
arch/um/drivers/port_user.c
Normal file
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include "chan_user.h"
|
||||
#include <os.h>
|
||||
#include "port.h"
|
||||
#include <um_malloc.h>
|
||||
|
||||
struct port_chan {
|
||||
int raw;
|
||||
struct termios tt;
|
||||
void *kernel_data;
|
||||
char dev[sizeof("32768\0")];
|
||||
};
|
||||
|
||||
static void *port_init(char *str, int device, const struct chan_opts *opts)
|
||||
{
|
||||
struct port_chan *data;
|
||||
void *kern_data;
|
||||
char *end;
|
||||
int port;
|
||||
|
||||
if (*str != ':') {
|
||||
printk(UM_KERN_ERR "port_init : channel type 'port' must "
|
||||
"specify a port number\n");
|
||||
return NULL;
|
||||
}
|
||||
str++;
|
||||
port = strtoul(str, &end, 0);
|
||||
if ((*end != '\0') || (end == str)) {
|
||||
printk(UM_KERN_ERR "port_init : couldn't parse port '%s'\n",
|
||||
str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kern_data = port_data(port);
|
||||
if (kern_data == NULL)
|
||||
return NULL;
|
||||
|
||||
data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
goto err;
|
||||
|
||||
*data = ((struct port_chan) { .raw = opts->raw,
|
||||
.kernel_data = kern_data });
|
||||
sprintf(data->dev, "%d", port);
|
||||
|
||||
return data;
|
||||
err:
|
||||
port_kern_free(kern_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void port_free(void *d)
|
||||
{
|
||||
struct port_chan *data = d;
|
||||
|
||||
port_kern_free(data->kernel_data);
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
static int port_open(int input, int output, int primary, void *d,
|
||||
char **dev_out)
|
||||
{
|
||||
struct port_chan *data = d;
|
||||
int fd, err;
|
||||
|
||||
fd = port_wait(data->kernel_data);
|
||||
if ((fd >= 0) && data->raw) {
|
||||
CATCH_EINTR(err = tcgetattr(fd, &data->tt));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = raw(fd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
*dev_out = data->dev;
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void port_close(int fd, void *d)
|
||||
{
|
||||
struct port_chan *data = d;
|
||||
|
||||
port_remove_dev(data->kernel_data);
|
||||
os_close_file(fd);
|
||||
}
|
||||
|
||||
const struct chan_ops port_ops = {
|
||||
.type = "port",
|
||||
.init = port_init,
|
||||
.open = port_open,
|
||||
.close = port_close,
|
||||
.read = generic_read,
|
||||
.write = generic_write,
|
||||
.console_write = generic_console_write,
|
||||
.window_size = generic_window_size,
|
||||
.free = port_free,
|
||||
.winch = 1,
|
||||
};
|
||||
|
||||
int port_listen_fd(int port)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int fd, err, arg;
|
||||
|
||||
fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
return -errno;
|
||||
|
||||
arg = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0) {
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (listen(fd, 1) < 0) {
|
||||
err = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = os_set_fd_block(fd, 0);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
return fd;
|
||||
out:
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
struct port_pre_exec_data {
|
||||
int sock_fd;
|
||||
int pipe_fd;
|
||||
};
|
||||
|
||||
static void port_pre_exec(void *arg)
|
||||
{
|
||||
struct port_pre_exec_data *data = arg;
|
||||
|
||||
dup2(data->sock_fd, 0);
|
||||
dup2(data->sock_fd, 1);
|
||||
dup2(data->sock_fd, 2);
|
||||
close(data->sock_fd);
|
||||
dup2(data->pipe_fd, 3);
|
||||
shutdown(3, SHUT_RD);
|
||||
close(data->pipe_fd);
|
||||
}
|
||||
|
||||
int port_connection(int fd, int *socket, int *pid_out)
|
||||
{
|
||||
int new, err;
|
||||
char *argv[] = { "/usr/sbin/in.telnetd", "-L",
|
||||
"/usr/lib/uml/port-helper", NULL };
|
||||
struct port_pre_exec_data data;
|
||||
|
||||
new = accept(fd, NULL, 0);
|
||||
if (new < 0)
|
||||
return -errno;
|
||||
|
||||
err = os_pipe(socket, 0, 0);
|
||||
if (err < 0)
|
||||
goto out_close;
|
||||
|
||||
data = ((struct port_pre_exec_data)
|
||||
{ .sock_fd = new,
|
||||
.pipe_fd = socket[1] });
|
||||
|
||||
err = run_helper(port_pre_exec, &data, argv);
|
||||
if (err < 0)
|
||||
goto out_shutdown;
|
||||
|
||||
*pid_out = err;
|
||||
return new;
|
||||
|
||||
out_shutdown:
|
||||
shutdown(socket[0], SHUT_RDWR);
|
||||
close(socket[0]);
|
||||
shutdown(socket[1], SHUT_RDWR);
|
||||
close(socket[1]);
|
||||
out_close:
|
||||
close(new);
|
||||
return err;
|
||||
}
|
165
arch/um/drivers/pty.c
Normal file
165
arch/um/drivers/pty.c
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <sys/stat.h>
|
||||
#include "chan_user.h"
|
||||
#include <os.h>
|
||||
#include <um_malloc.h>
|
||||
|
||||
struct pty_chan {
|
||||
void (*announce)(char *dev_name, int dev);
|
||||
int dev;
|
||||
int raw;
|
||||
struct termios tt;
|
||||
char dev_name[sizeof("/dev/pts/0123456\0")];
|
||||
};
|
||||
|
||||
static void *pty_chan_init(char *str, int device, const struct chan_opts *opts)
|
||||
{
|
||||
struct pty_chan *data;
|
||||
|
||||
data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
|
||||
*data = ((struct pty_chan) { .announce = opts->announce,
|
||||
.dev = device,
|
||||
.raw = opts->raw });
|
||||
return data;
|
||||
}
|
||||
|
||||
static int pts_open(int input, int output, int primary, void *d,
|
||||
char **dev_out)
|
||||
{
|
||||
struct pty_chan *data = d;
|
||||
char *dev;
|
||||
int fd, err;
|
||||
|
||||
fd = get_pty();
|
||||
if (fd < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "open_pts : Failed to open pts\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (data->raw) {
|
||||
CATCH_EINTR(err = tcgetattr(fd, &data->tt));
|
||||
if (err)
|
||||
goto out_close;
|
||||
|
||||
err = raw(fd);
|
||||
if (err)
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
dev = ptsname(fd);
|
||||
sprintf(data->dev_name, "%s", dev);
|
||||
*dev_out = data->dev_name;
|
||||
|
||||
if (data->announce)
|
||||
(*data->announce)(dev, data->dev);
|
||||
|
||||
return fd;
|
||||
|
||||
out_close:
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int getmaster(char *line)
|
||||
{
|
||||
struct stat buf;
|
||||
char *pty, *bank, *cp;
|
||||
int master, err;
|
||||
|
||||
pty = &line[strlen("/dev/ptyp")];
|
||||
for (bank = "pqrs"; *bank; bank++) {
|
||||
line[strlen("/dev/pty")] = *bank;
|
||||
*pty = '0';
|
||||
/* Did we hit the end ? */
|
||||
if ((stat(line, &buf) < 0) && (errno == ENOENT))
|
||||
break;
|
||||
|
||||
for (cp = "0123456789abcdef"; *cp; cp++) {
|
||||
*pty = *cp;
|
||||
master = open(line, O_RDWR);
|
||||
if (master >= 0) {
|
||||
char *tp = &line[strlen("/dev/")];
|
||||
|
||||
/* verify slave side is usable */
|
||||
*tp = 't';
|
||||
err = access(line, R_OK | W_OK);
|
||||
*tp = 'p';
|
||||
if (!err)
|
||||
return master;
|
||||
close(master);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printk(UM_KERN_ERR "getmaster - no usable host pty devices\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int pty_open(int input, int output, int primary, void *d,
|
||||
char **dev_out)
|
||||
{
|
||||
struct pty_chan *data = d;
|
||||
int fd, err;
|
||||
char dev[sizeof("/dev/ptyxx\0")] = "/dev/ptyxx";
|
||||
|
||||
fd = getmaster(dev);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
if (data->raw) {
|
||||
err = raw(fd);
|
||||
if (err) {
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->announce)
|
||||
(*data->announce)(dev, data->dev);
|
||||
|
||||
sprintf(data->dev_name, "%s", dev);
|
||||
*dev_out = data->dev_name;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
const struct chan_ops pty_ops = {
|
||||
.type = "pty",
|
||||
.init = pty_chan_init,
|
||||
.open = pty_open,
|
||||
.close = generic_close,
|
||||
.read = generic_read,
|
||||
.write = generic_write,
|
||||
.console_write = generic_console_write,
|
||||
.window_size = generic_window_size,
|
||||
.free = generic_free,
|
||||
.winch = 0,
|
||||
};
|
||||
|
||||
const struct chan_ops pts_ops = {
|
||||
.type = "pts",
|
||||
.init = pty_chan_init,
|
||||
.open = pts_open,
|
||||
.close = generic_close,
|
||||
.read = generic_read,
|
||||
.write = generic_write,
|
||||
.console_write = generic_console_write,
|
||||
.window_size = generic_window_size,
|
||||
.free = generic_free,
|
||||
.winch = 0,
|
||||
};
|
167
arch/um/drivers/random.c
Normal file
167
arch/um/drivers/random.c
Normal file
|
@ -0,0 +1,167 @@
|
|||
/* Copyright (C) 2005 - 2008 Jeff Dike <jdike@{linux.intel,addtoit}.com> */
|
||||
|
||||
/* Much of this ripped from drivers/char/hw_random.c, see there for other
|
||||
* copyright.
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*/
|
||||
#include <linux/sched.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <irq_kern.h>
|
||||
#include <os.h>
|
||||
|
||||
/*
|
||||
* core module and version information
|
||||
*/
|
||||
#define RNG_VERSION "1.0.0"
|
||||
#define RNG_MODULE_NAME "hw_random"
|
||||
|
||||
#define RNG_MISCDEV_MINOR 183 /* official */
|
||||
|
||||
/* Changed at init time, in the non-modular case, and at module load
|
||||
* time, in the module case. Presumably, the module subsystem
|
||||
* protects against a module being loaded twice at the same time.
|
||||
*/
|
||||
static int random_fd = -1;
|
||||
static DECLARE_WAIT_QUEUE_HEAD(host_read_wait);
|
||||
|
||||
static int rng_dev_open (struct inode *inode, struct file *filp)
|
||||
{
|
||||
/* enforce read-only access to this chrdev */
|
||||
if ((filp->f_mode & FMODE_READ) == 0)
|
||||
return -EINVAL;
|
||||
if ((filp->f_mode & FMODE_WRITE) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static atomic_t host_sleep_count = ATOMIC_INIT(0);
|
||||
|
||||
static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size,
|
||||
loff_t *offp)
|
||||
{
|
||||
u32 data;
|
||||
int n, ret = 0, have_data;
|
||||
|
||||
while (size) {
|
||||
n = os_read_file(random_fd, &data, sizeof(data));
|
||||
if (n > 0) {
|
||||
have_data = n;
|
||||
while (have_data && size) {
|
||||
if (put_user((u8) data, buf++)) {
|
||||
ret = ret ? : -EFAULT;
|
||||
break;
|
||||
}
|
||||
size--;
|
||||
ret++;
|
||||
have_data--;
|
||||
data >>= 8;
|
||||
}
|
||||
}
|
||||
else if (n == -EAGAIN) {
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
if (filp->f_flags & O_NONBLOCK)
|
||||
return ret ? : -EAGAIN;
|
||||
|
||||
atomic_inc(&host_sleep_count);
|
||||
reactivate_fd(random_fd, RANDOM_IRQ);
|
||||
add_sigio_fd(random_fd);
|
||||
|
||||
add_wait_queue(&host_read_wait, &wait);
|
||||
set_task_state(current, TASK_INTERRUPTIBLE);
|
||||
|
||||
schedule();
|
||||
remove_wait_queue(&host_read_wait, &wait);
|
||||
|
||||
if (atomic_dec_and_test(&host_sleep_count)) {
|
||||
ignore_sigio_fd(random_fd);
|
||||
deactivate_fd(random_fd, RANDOM_IRQ);
|
||||
}
|
||||
}
|
||||
else
|
||||
return n;
|
||||
|
||||
if (signal_pending (current))
|
||||
return ret ? : -ERESTARTSYS;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations rng_chrdev_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rng_dev_open,
|
||||
.read = rng_dev_read,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
/* rng_init shouldn't be called more than once at boot time */
|
||||
static struct miscdevice rng_miscdev = {
|
||||
RNG_MISCDEV_MINOR,
|
||||
RNG_MODULE_NAME,
|
||||
&rng_chrdev_ops,
|
||||
};
|
||||
|
||||
static irqreturn_t random_interrupt(int irq, void *data)
|
||||
{
|
||||
wake_up(&host_read_wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* rng_init - initialize RNG module
|
||||
*/
|
||||
static int __init rng_init (void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
random_fd = err;
|
||||
|
||||
err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
|
||||
0, "random", NULL);
|
||||
if (err)
|
||||
goto err_out_cleanup_hw;
|
||||
|
||||
sigio_broken(random_fd, 1);
|
||||
|
||||
err = misc_register (&rng_miscdev);
|
||||
if (err) {
|
||||
printk (KERN_ERR RNG_MODULE_NAME ": misc device register "
|
||||
"failed\n");
|
||||
goto err_out_cleanup_hw;
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
|
||||
err_out_cleanup_hw:
|
||||
os_close_file(random_fd);
|
||||
random_fd = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* rng_cleanup - shutdown RNG module
|
||||
*/
|
||||
static void __exit rng_cleanup (void)
|
||||
{
|
||||
os_close_file(random_fd);
|
||||
misc_deregister (&rng_miscdev);
|
||||
}
|
||||
|
||||
module_init (rng_init);
|
||||
module_exit (rng_cleanup);
|
||||
|
||||
MODULE_DESCRIPTION("UML Host Random Number Generator (RNG) driver");
|
||||
MODULE_LICENSE("GPL");
|
20
arch/um/drivers/slip.h
Normal file
20
arch/um/drivers/slip.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef __UM_SLIP_H
|
||||
#define __UM_SLIP_H
|
||||
|
||||
#include "slip_common.h"
|
||||
|
||||
struct slip_data {
|
||||
void *dev;
|
||||
char name[sizeof("slnnnnn\0")];
|
||||
char *addr;
|
||||
char *gate_addr;
|
||||
int slave;
|
||||
struct slip_proto slip;
|
||||
};
|
||||
|
||||
extern const struct net_user_info slip_user_info;
|
||||
|
||||
extern int slip_user_read(int fd, void *buf, int len, struct slip_data *pri);
|
||||
extern int slip_user_write(int fd, void *buf, int len, struct slip_data *pri);
|
||||
|
||||
#endif
|
54
arch/um/drivers/slip_common.c
Normal file
54
arch/um/drivers/slip_common.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
#include <string.h>
|
||||
#include "slip_common.h"
|
||||
#include <net_user.h>
|
||||
|
||||
int slip_proto_read(int fd, void *buf, int len, struct slip_proto *slip)
|
||||
{
|
||||
int i, n, size, start;
|
||||
|
||||
if(slip->more > 0){
|
||||
i = 0;
|
||||
while(i < slip->more){
|
||||
size = slip_unesc(slip->ibuf[i++], slip->ibuf,
|
||||
&slip->pos, &slip->esc);
|
||||
if(size){
|
||||
memcpy(buf, slip->ibuf, size);
|
||||
memmove(slip->ibuf, &slip->ibuf[i],
|
||||
slip->more - i);
|
||||
slip->more = slip->more - i;
|
||||
return size;
|
||||
}
|
||||
}
|
||||
slip->more = 0;
|
||||
}
|
||||
|
||||
n = net_read(fd, &slip->ibuf[slip->pos],
|
||||
sizeof(slip->ibuf) - slip->pos);
|
||||
if(n <= 0)
|
||||
return n;
|
||||
|
||||
start = slip->pos;
|
||||
for(i = 0; i < n; i++){
|
||||
size = slip_unesc(slip->ibuf[start + i], slip->ibuf,&slip->pos,
|
||||
&slip->esc);
|
||||
if(size){
|
||||
memcpy(buf, slip->ibuf, size);
|
||||
memmove(slip->ibuf, &slip->ibuf[start+i+1],
|
||||
n - (i + 1));
|
||||
slip->more = n - (i + 1);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int slip_proto_write(int fd, void *buf, int len, struct slip_proto *slip)
|
||||
{
|
||||
int actual, n;
|
||||
|
||||
actual = slip_esc(buf, slip->obuf, len);
|
||||
n = net_write(fd, slip->obuf, actual);
|
||||
if(n < 0)
|
||||
return n;
|
||||
else return len;
|
||||
}
|
105
arch/um/drivers/slip_common.h
Normal file
105
arch/um/drivers/slip_common.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
#ifndef __UM_SLIP_COMMON_H
|
||||
#define __UM_SLIP_COMMON_H
|
||||
|
||||
#define BUF_SIZE 1500
|
||||
/* two bytes each for a (pathological) max packet of escaped chars + *
|
||||
* terminating END char + initial END char */
|
||||
#define ENC_BUF_SIZE (2 * BUF_SIZE + 2)
|
||||
|
||||
/* SLIP protocol characters. */
|
||||
#define SLIP_END 0300 /* indicates end of frame */
|
||||
#define SLIP_ESC 0333 /* indicates byte stuffing */
|
||||
#define SLIP_ESC_END 0334 /* ESC ESC_END means END 'data' */
|
||||
#define SLIP_ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */
|
||||
|
||||
static inline int slip_unesc(unsigned char c, unsigned char *buf, int *pos,
|
||||
int *esc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch(c){
|
||||
case SLIP_END:
|
||||
*esc = 0;
|
||||
ret=*pos;
|
||||
*pos=0;
|
||||
return(ret);
|
||||
case SLIP_ESC:
|
||||
*esc = 1;
|
||||
return(0);
|
||||
case SLIP_ESC_ESC:
|
||||
if(*esc){
|
||||
*esc = 0;
|
||||
c = SLIP_ESC;
|
||||
}
|
||||
break;
|
||||
case SLIP_ESC_END:
|
||||
if(*esc){
|
||||
*esc = 0;
|
||||
c = SLIP_END;
|
||||
}
|
||||
break;
|
||||
}
|
||||
buf[(*pos)++] = c;
|
||||
return(0);
|
||||
}
|
||||
|
||||
static inline int slip_esc(unsigned char *s, unsigned char *d, int len)
|
||||
{
|
||||
unsigned char *ptr = d;
|
||||
unsigned char c;
|
||||
|
||||
/*
|
||||
* Send an initial END character to flush out any
|
||||
* data that may have accumulated in the receiver
|
||||
* due to line noise.
|
||||
*/
|
||||
|
||||
*ptr++ = SLIP_END;
|
||||
|
||||
/*
|
||||
* For each byte in the packet, send the appropriate
|
||||
* character sequence, according to the SLIP protocol.
|
||||
*/
|
||||
|
||||
while (len-- > 0) {
|
||||
switch(c = *s++) {
|
||||
case SLIP_END:
|
||||
*ptr++ = SLIP_ESC;
|
||||
*ptr++ = SLIP_ESC_END;
|
||||
break;
|
||||
case SLIP_ESC:
|
||||
*ptr++ = SLIP_ESC;
|
||||
*ptr++ = SLIP_ESC_ESC;
|
||||
break;
|
||||
default:
|
||||
*ptr++ = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*ptr++ = SLIP_END;
|
||||
return (ptr - d);
|
||||
}
|
||||
|
||||
struct slip_proto {
|
||||
unsigned char ibuf[ENC_BUF_SIZE];
|
||||
unsigned char obuf[ENC_BUF_SIZE];
|
||||
int more; /* more data: do not read fd until ibuf has been drained */
|
||||
int pos;
|
||||
int esc;
|
||||
};
|
||||
|
||||
static inline void slip_proto_init(struct slip_proto * slip)
|
||||
{
|
||||
memset(slip->ibuf, 0, sizeof(slip->ibuf));
|
||||
memset(slip->obuf, 0, sizeof(slip->obuf));
|
||||
slip->more = 0;
|
||||
slip->pos = 0;
|
||||
slip->esc = 0;
|
||||
}
|
||||
|
||||
extern int slip_proto_read(int fd, void *buf, int len,
|
||||
struct slip_proto *slip);
|
||||
extern int slip_proto_write(int fd, void *buf, int len,
|
||||
struct slip_proto *slip);
|
||||
|
||||
#endif
|
93
arch/um/drivers/slip_kern.c
Normal file
93
arch/um/drivers/slip_kern.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <net_kern.h>
|
||||
#include "slip.h"
|
||||
|
||||
struct slip_init {
|
||||
char *gate_addr;
|
||||
};
|
||||
|
||||
static void slip_init(struct net_device *dev, void *data)
|
||||
{
|
||||
struct uml_net_private *private;
|
||||
struct slip_data *spri;
|
||||
struct slip_init *init = data;
|
||||
|
||||
private = netdev_priv(dev);
|
||||
spri = (struct slip_data *) private->user;
|
||||
|
||||
memset(spri->name, 0, sizeof(spri->name));
|
||||
spri->addr = NULL;
|
||||
spri->gate_addr = init->gate_addr;
|
||||
spri->slave = -1;
|
||||
spri->dev = dev;
|
||||
|
||||
slip_proto_init(&spri->slip);
|
||||
|
||||
dev->hard_header_len = 0;
|
||||
dev->header_ops = NULL;
|
||||
dev->addr_len = 0;
|
||||
dev->type = ARPHRD_SLIP;
|
||||
dev->tx_queue_len = 256;
|
||||
dev->flags = IFF_NOARP;
|
||||
printk("SLIP backend - SLIP IP = %s\n", spri->gate_addr);
|
||||
}
|
||||
|
||||
static unsigned short slip_protocol(struct sk_buff *skbuff)
|
||||
{
|
||||
return htons(ETH_P_IP);
|
||||
}
|
||||
|
||||
static int slip_read(int fd, struct sk_buff *skb, struct uml_net_private *lp)
|
||||
{
|
||||
return slip_user_read(fd, skb_mac_header(skb), skb->dev->mtu,
|
||||
(struct slip_data *) &lp->user);
|
||||
}
|
||||
|
||||
static int slip_write(int fd, struct sk_buff *skb, struct uml_net_private *lp)
|
||||
{
|
||||
return slip_user_write(fd, skb->data, skb->len,
|
||||
(struct slip_data *) &lp->user);
|
||||
}
|
||||
|
||||
static const struct net_kern_info slip_kern_info = {
|
||||
.init = slip_init,
|
||||
.protocol = slip_protocol,
|
||||
.read = slip_read,
|
||||
.write = slip_write,
|
||||
};
|
||||
|
||||
static int slip_setup(char *str, char **mac_out, void *data)
|
||||
{
|
||||
struct slip_init *init = data;
|
||||
|
||||
*init = ((struct slip_init) { .gate_addr = NULL });
|
||||
|
||||
if (str[0] != '\0')
|
||||
init->gate_addr = str;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct transport slip_transport = {
|
||||
.list = LIST_HEAD_INIT(slip_transport.list),
|
||||
.name = "slip",
|
||||
.setup = slip_setup,
|
||||
.user = &slip_user_info,
|
||||
.kern = &slip_kern_info,
|
||||
.private_size = sizeof(struct slip_data),
|
||||
.setup_size = sizeof(struct slip_init),
|
||||
};
|
||||
|
||||
static int register_slip(void)
|
||||
{
|
||||
register_transport(&slip_transport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(register_slip);
|
251
arch/um/drivers/slip_user.c
Normal file
251
arch/um/drivers/slip_user.c
Normal file
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/termios.h>
|
||||
#include <sys/wait.h>
|
||||
#include <net_user.h>
|
||||
#include <os.h>
|
||||
#include "slip.h"
|
||||
#include <um_malloc.h>
|
||||
|
||||
static int slip_user_init(void *data, void *dev)
|
||||
{
|
||||
struct slip_data *pri = data;
|
||||
|
||||
pri->dev = dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_up_tty(int fd)
|
||||
{
|
||||
int i;
|
||||
struct termios tios;
|
||||
|
||||
if (tcgetattr(fd, &tios) < 0) {
|
||||
printk(UM_KERN_ERR "could not get initial terminal "
|
||||
"attributes\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tios.c_cflag = CS8 | CREAD | HUPCL | CLOCAL;
|
||||
tios.c_iflag = IGNBRK | IGNPAR;
|
||||
tios.c_oflag = 0;
|
||||
tios.c_lflag = 0;
|
||||
for (i = 0; i < NCCS; i++)
|
||||
tios.c_cc[i] = 0;
|
||||
tios.c_cc[VMIN] = 1;
|
||||
tios.c_cc[VTIME] = 0;
|
||||
|
||||
cfsetospeed(&tios, B38400);
|
||||
cfsetispeed(&tios, B38400);
|
||||
|
||||
if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
|
||||
printk(UM_KERN_ERR "failed to set terminal attributes\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct slip_pre_exec_data {
|
||||
int stdin;
|
||||
int stdout;
|
||||
int close_me;
|
||||
};
|
||||
|
||||
static void slip_pre_exec(void *arg)
|
||||
{
|
||||
struct slip_pre_exec_data *data = arg;
|
||||
|
||||
if (data->stdin >= 0)
|
||||
dup2(data->stdin, 0);
|
||||
dup2(data->stdout, 1);
|
||||
if (data->close_me >= 0)
|
||||
close(data->close_me);
|
||||
}
|
||||
|
||||
static int slip_tramp(char **argv, int fd)
|
||||
{
|
||||
struct slip_pre_exec_data pe_data;
|
||||
char *output;
|
||||
int pid, fds[2], err, output_len;
|
||||
|
||||
err = os_pipe(fds, 1, 0);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "slip_tramp : pipe failed, err = %d\n",
|
||||
-err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
pe_data.stdin = fd;
|
||||
pe_data.stdout = fds[1];
|
||||
pe_data.close_me = fds[0];
|
||||
err = run_helper(slip_pre_exec, &pe_data, argv);
|
||||
if (err < 0)
|
||||
goto out_close;
|
||||
pid = err;
|
||||
|
||||
output_len = UM_KERN_PAGE_SIZE;
|
||||
output = uml_kmalloc(output_len, UM_GFP_KERNEL);
|
||||
if (output == NULL) {
|
||||
printk(UM_KERN_ERR "slip_tramp : failed to allocate output "
|
||||
"buffer\n");
|
||||
os_kill_process(pid, 1);
|
||||
err = -ENOMEM;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
close(fds[1]);
|
||||
read_output(fds[0], output, output_len);
|
||||
printk("%s", output);
|
||||
|
||||
err = helper_wait(pid);
|
||||
close(fds[0]);
|
||||
|
||||
kfree(output);
|
||||
return err;
|
||||
|
||||
out_close:
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int slip_open(void *data)
|
||||
{
|
||||
struct slip_data *pri = data;
|
||||
char version_buf[sizeof("nnnnn\0")];
|
||||
char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")];
|
||||
char *argv[] = { "uml_net", version_buf, "slip", "up", gate_buf,
|
||||
NULL };
|
||||
int sfd, mfd, err;
|
||||
|
||||
err = get_pty();
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "slip-open : Failed to open pty, err = %d\n",
|
||||
-err);
|
||||
goto out;
|
||||
}
|
||||
mfd = err;
|
||||
|
||||
err = open(ptsname(mfd), O_RDWR, 0);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "Couldn't open tty for slip line, "
|
||||
"err = %d\n", -err);
|
||||
goto out_close;
|
||||
}
|
||||
sfd = err;
|
||||
|
||||
if (set_up_tty(sfd))
|
||||
goto out_close2;
|
||||
|
||||
pri->slave = sfd;
|
||||
pri->slip.pos = 0;
|
||||
pri->slip.esc = 0;
|
||||
if (pri->gate_addr != NULL) {
|
||||
sprintf(version_buf, "%d", UML_NET_VERSION);
|
||||
strcpy(gate_buf, pri->gate_addr);
|
||||
|
||||
err = slip_tramp(argv, sfd);
|
||||
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "slip_tramp failed - err = %d\n",
|
||||
-err);
|
||||
goto out_close2;
|
||||
}
|
||||
err = os_get_ifname(pri->slave, pri->name);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "get_ifname failed, err = %d\n",
|
||||
-err);
|
||||
goto out_close2;
|
||||
}
|
||||
iter_addresses(pri->dev, open_addr, pri->name);
|
||||
}
|
||||
else {
|
||||
err = os_set_slip(sfd);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "Failed to set slip discipline "
|
||||
"encapsulation - err = %d\n", -err);
|
||||
goto out_close2;
|
||||
}
|
||||
}
|
||||
return mfd;
|
||||
out_close2:
|
||||
close(sfd);
|
||||
out_close:
|
||||
close(mfd);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void slip_close(int fd, void *data)
|
||||
{
|
||||
struct slip_data *pri = data;
|
||||
char version_buf[sizeof("nnnnn\0")];
|
||||
char *argv[] = { "uml_net", version_buf, "slip", "down", pri->name,
|
||||
NULL };
|
||||
int err;
|
||||
|
||||
if (pri->gate_addr != NULL)
|
||||
iter_addresses(pri->dev, close_addr, pri->name);
|
||||
|
||||
sprintf(version_buf, "%d", UML_NET_VERSION);
|
||||
|
||||
err = slip_tramp(argv, pri->slave);
|
||||
|
||||
if (err != 0)
|
||||
printk(UM_KERN_ERR "slip_tramp failed - errno = %d\n", -err);
|
||||
close(fd);
|
||||
close(pri->slave);
|
||||
pri->slave = -1;
|
||||
}
|
||||
|
||||
int slip_user_read(int fd, void *buf, int len, struct slip_data *pri)
|
||||
{
|
||||
return slip_proto_read(fd, buf, len, &pri->slip);
|
||||
}
|
||||
|
||||
int slip_user_write(int fd, void *buf, int len, struct slip_data *pri)
|
||||
{
|
||||
return slip_proto_write(fd, buf, len, &pri->slip);
|
||||
}
|
||||
|
||||
static void slip_add_addr(unsigned char *addr, unsigned char *netmask,
|
||||
void *data)
|
||||
{
|
||||
struct slip_data *pri = data;
|
||||
|
||||
if (pri->slave < 0)
|
||||
return;
|
||||
open_addr(addr, netmask, pri->name);
|
||||
}
|
||||
|
||||
static void slip_del_addr(unsigned char *addr, unsigned char *netmask,
|
||||
void *data)
|
||||
{
|
||||
struct slip_data *pri = data;
|
||||
|
||||
if (pri->slave < 0)
|
||||
return;
|
||||
close_addr(addr, netmask, pri->name);
|
||||
}
|
||||
|
||||
const struct net_user_info slip_user_info = {
|
||||
.init = slip_user_init,
|
||||
.open = slip_open,
|
||||
.close = slip_close,
|
||||
.remove = NULL,
|
||||
.add_address = slip_add_addr,
|
||||
.delete_address = slip_del_addr,
|
||||
.mtu = BUF_SIZE,
|
||||
.max_packet = BUF_SIZE,
|
||||
};
|
33
arch/um/drivers/slirp.h
Normal file
33
arch/um/drivers/slirp.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#ifndef __UM_SLIRP_H
|
||||
#define __UM_SLIRP_H
|
||||
|
||||
#include "slip_common.h"
|
||||
|
||||
#define SLIRP_MAX_ARGS 100
|
||||
/*
|
||||
* XXX this next definition is here because I don't understand why this
|
||||
* initializer doesn't work in slirp_kern.c:
|
||||
*
|
||||
* argv : { init->argv[ 0 ... SLIRP_MAX_ARGS-1 ] },
|
||||
*
|
||||
* or why I can't typecast like this:
|
||||
*
|
||||
* argv : (char* [SLIRP_MAX_ARGS])(init->argv),
|
||||
*/
|
||||
struct arg_list_dummy_wrapper { char *argv[SLIRP_MAX_ARGS]; };
|
||||
|
||||
struct slirp_data {
|
||||
void *dev;
|
||||
struct arg_list_dummy_wrapper argw;
|
||||
int pid;
|
||||
int slave;
|
||||
struct slip_proto slip;
|
||||
};
|
||||
|
||||
extern const struct net_user_info slirp_user_info;
|
||||
|
||||
extern int slirp_user_read(int fd, void *buf, int len, struct slirp_data *pri);
|
||||
extern int slirp_user_write(int fd, void *buf, int len,
|
||||
struct slirp_data *pri);
|
||||
|
||||
#endif
|
120
arch/um/drivers/slirp_kern.c
Normal file
120
arch/um/drivers/slirp_kern.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/string.h>
|
||||
#include <net_kern.h>
|
||||
#include <net_user.h>
|
||||
#include "slirp.h"
|
||||
|
||||
struct slirp_init {
|
||||
struct arg_list_dummy_wrapper argw; /* XXX should be simpler... */
|
||||
};
|
||||
|
||||
void slirp_init(struct net_device *dev, void *data)
|
||||
{
|
||||
struct uml_net_private *private;
|
||||
struct slirp_data *spri;
|
||||
struct slirp_init *init = data;
|
||||
int i;
|
||||
|
||||
private = netdev_priv(dev);
|
||||
spri = (struct slirp_data *) private->user;
|
||||
|
||||
spri->argw = init->argw;
|
||||
spri->pid = -1;
|
||||
spri->slave = -1;
|
||||
spri->dev = dev;
|
||||
|
||||
slip_proto_init(&spri->slip);
|
||||
|
||||
dev->hard_header_len = 0;
|
||||
dev->header_ops = NULL;
|
||||
dev->addr_len = 0;
|
||||
dev->type = ARPHRD_SLIP;
|
||||
dev->tx_queue_len = 256;
|
||||
dev->flags = IFF_NOARP;
|
||||
printk("SLIRP backend - command line:");
|
||||
for (i = 0; spri->argw.argv[i] != NULL; i++)
|
||||
printk(" '%s'",spri->argw.argv[i]);
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
static unsigned short slirp_protocol(struct sk_buff *skbuff)
|
||||
{
|
||||
return htons(ETH_P_IP);
|
||||
}
|
||||
|
||||
static int slirp_read(int fd, struct sk_buff *skb, struct uml_net_private *lp)
|
||||
{
|
||||
return slirp_user_read(fd, skb_mac_header(skb), skb->dev->mtu,
|
||||
(struct slirp_data *) &lp->user);
|
||||
}
|
||||
|
||||
static int slirp_write(int fd, struct sk_buff *skb, struct uml_net_private *lp)
|
||||
{
|
||||
return slirp_user_write(fd, skb->data, skb->len,
|
||||
(struct slirp_data *) &lp->user);
|
||||
}
|
||||
|
||||
const struct net_kern_info slirp_kern_info = {
|
||||
.init = slirp_init,
|
||||
.protocol = slirp_protocol,
|
||||
.read = slirp_read,
|
||||
.write = slirp_write,
|
||||
};
|
||||
|
||||
static int slirp_setup(char *str, char **mac_out, void *data)
|
||||
{
|
||||
struct slirp_init *init = data;
|
||||
int i=0;
|
||||
|
||||
*init = ((struct slirp_init) { .argw = { { "slirp", NULL } } });
|
||||
|
||||
str = split_if_spec(str, mac_out, NULL);
|
||||
|
||||
if (str == NULL) /* no command line given after MAC addr */
|
||||
return 1;
|
||||
|
||||
do {
|
||||
if (i >= SLIRP_MAX_ARGS - 1) {
|
||||
printk(KERN_WARNING "slirp_setup: truncating slirp "
|
||||
"arguments\n");
|
||||
break;
|
||||
}
|
||||
init->argw.argv[i++] = str;
|
||||
while(*str && *str!=',') {
|
||||
if (*str == '_')
|
||||
*str=' ';
|
||||
str++;
|
||||
}
|
||||
if (*str != ',')
|
||||
break;
|
||||
*str++ = '\0';
|
||||
} while (1);
|
||||
|
||||
init->argw.argv[i] = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct transport slirp_transport = {
|
||||
.list = LIST_HEAD_INIT(slirp_transport.list),
|
||||
.name = "slirp",
|
||||
.setup = slirp_setup,
|
||||
.user = &slirp_user_info,
|
||||
.kern = &slirp_kern_info,
|
||||
.private_size = sizeof(struct slirp_data),
|
||||
.setup_size = sizeof(struct slirp_init),
|
||||
};
|
||||
|
||||
static int register_slirp(void)
|
||||
{
|
||||
register_transport(&slirp_transport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(register_slirp);
|
125
arch/um/drivers/slirp_user.c
Normal file
125
arch/um/drivers/slirp_user.c
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <net_user.h>
|
||||
#include <os.h>
|
||||
#include "slirp.h"
|
||||
|
||||
static int slirp_user_init(void *data, void *dev)
|
||||
{
|
||||
struct slirp_data *pri = data;
|
||||
|
||||
pri->dev = dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct slirp_pre_exec_data {
|
||||
int stdin;
|
||||
int stdout;
|
||||
};
|
||||
|
||||
static void slirp_pre_exec(void *arg)
|
||||
{
|
||||
struct slirp_pre_exec_data *data = arg;
|
||||
|
||||
if (data->stdin != -1)
|
||||
dup2(data->stdin, 0);
|
||||
if (data->stdout != -1)
|
||||
dup2(data->stdout, 1);
|
||||
}
|
||||
|
||||
static int slirp_tramp(char **argv, int fd)
|
||||
{
|
||||
struct slirp_pre_exec_data pe_data;
|
||||
int pid;
|
||||
|
||||
pe_data.stdin = fd;
|
||||
pe_data.stdout = fd;
|
||||
pid = run_helper(slirp_pre_exec, &pe_data, argv);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
static int slirp_open(void *data)
|
||||
{
|
||||
struct slirp_data *pri = data;
|
||||
int fds[2], pid, err;
|
||||
|
||||
err = os_pipe(fds, 1, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = slirp_tramp(pri->argw.argv, fds[1]);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "slirp_tramp failed - errno = %d\n", -err);
|
||||
goto out;
|
||||
}
|
||||
pid = err;
|
||||
|
||||
pri->slave = fds[1];
|
||||
pri->slip.pos = 0;
|
||||
pri->slip.esc = 0;
|
||||
pri->pid = err;
|
||||
|
||||
return fds[0];
|
||||
out:
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void slirp_close(int fd, void *data)
|
||||
{
|
||||
struct slirp_data *pri = data;
|
||||
int err;
|
||||
|
||||
close(fd);
|
||||
close(pri->slave);
|
||||
|
||||
pri->slave = -1;
|
||||
|
||||
if (pri->pid<1) {
|
||||
printk(UM_KERN_ERR "slirp_close: no child process to shut "
|
||||
"down\n");
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (kill(pri->pid, SIGHUP)<0) {
|
||||
printk(UM_KERN_ERR "slirp_close: sending hangup to %d failed "
|
||||
"(%d)\n", pri->pid, errno);
|
||||
}
|
||||
#endif
|
||||
err = helper_wait(pri->pid);
|
||||
if (err < 0)
|
||||
return;
|
||||
|
||||
pri->pid = -1;
|
||||
}
|
||||
|
||||
int slirp_user_read(int fd, void *buf, int len, struct slirp_data *pri)
|
||||
{
|
||||
return slip_proto_read(fd, buf, len, &pri->slip);
|
||||
}
|
||||
|
||||
int slirp_user_write(int fd, void *buf, int len, struct slirp_data *pri)
|
||||
{
|
||||
return slip_proto_write(fd, buf, len, &pri->slip);
|
||||
}
|
||||
|
||||
const struct net_user_info slirp_user_info = {
|
||||
.init = slirp_user_init,
|
||||
.open = slirp_open,
|
||||
.close = slirp_close,
|
||||
.remove = NULL,
|
||||
.add_address = NULL,
|
||||
.delete_address = NULL,
|
||||
.mtu = BUF_SIZE,
|
||||
.max_packet = BUF_SIZE,
|
||||
};
|
199
arch/um/drivers/ssl.c
Normal file
199
arch/um/drivers/ssl.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
#include <asm/termbits.h>
|
||||
#include <asm/irq.h>
|
||||
#include "ssl.h"
|
||||
#include "chan.h"
|
||||
#include <init.h>
|
||||
#include <irq_user.h>
|
||||
#include "mconsole_kern.h"
|
||||
|
||||
static const int ssl_version = 1;
|
||||
|
||||
#define NR_PORTS 64
|
||||
|
||||
static void ssl_announce(char *dev_name, int dev)
|
||||
{
|
||||
printk(KERN_INFO "Serial line %d assigned device '%s'\n", dev,
|
||||
dev_name);
|
||||
}
|
||||
|
||||
/* Almost const, except that xterm_title may be changed in an initcall */
|
||||
static struct chan_opts opts = {
|
||||
.announce = ssl_announce,
|
||||
.xterm_title = "Serial Line #%d",
|
||||
.raw = 1,
|
||||
};
|
||||
|
||||
static int ssl_config(char *str, char **error_out);
|
||||
static int ssl_get_config(char *dev, char *str, int size, char **error_out);
|
||||
static int ssl_remove(int n, char **error_out);
|
||||
|
||||
|
||||
/* Const, except for .mc.list */
|
||||
static struct line_driver driver = {
|
||||
.name = "UML serial line",
|
||||
.device_name = "ttyS",
|
||||
.major = TTY_MAJOR,
|
||||
.minor_start = 64,
|
||||
.type = TTY_DRIVER_TYPE_SERIAL,
|
||||
.subtype = 0,
|
||||
.read_irq = SSL_IRQ,
|
||||
.read_irq_name = "ssl",
|
||||
.write_irq = SSL_WRITE_IRQ,
|
||||
.write_irq_name = "ssl-write",
|
||||
.mc = {
|
||||
.list = LIST_HEAD_INIT(driver.mc.list),
|
||||
.name = "ssl",
|
||||
.config = ssl_config,
|
||||
.get_config = ssl_get_config,
|
||||
.id = line_id,
|
||||
.remove = ssl_remove,
|
||||
},
|
||||
};
|
||||
|
||||
/* The array is initialized by line_init, at initcall time. The
|
||||
* elements are locked individually as needed.
|
||||
*/
|
||||
static char *conf[NR_PORTS];
|
||||
static char *def_conf = CONFIG_SSL_CHAN;
|
||||
static struct line serial_lines[NR_PORTS];
|
||||
|
||||
static int ssl_config(char *str, char **error_out)
|
||||
{
|
||||
return line_config(serial_lines, ARRAY_SIZE(serial_lines), str, &opts,
|
||||
error_out);
|
||||
}
|
||||
|
||||
static int ssl_get_config(char *dev, char *str, int size, char **error_out)
|
||||
{
|
||||
return line_get_config(dev, serial_lines, ARRAY_SIZE(serial_lines), str,
|
||||
size, error_out);
|
||||
}
|
||||
|
||||
static int ssl_remove(int n, char **error_out)
|
||||
{
|
||||
return line_remove(serial_lines, ARRAY_SIZE(serial_lines), n,
|
||||
error_out);
|
||||
}
|
||||
|
||||
static int ssl_install(struct tty_driver *driver, struct tty_struct *tty)
|
||||
{
|
||||
return line_install(driver, tty, &serial_lines[tty->index]);
|
||||
}
|
||||
|
||||
static const struct tty_operations ssl_ops = {
|
||||
.open = line_open,
|
||||
.close = line_close,
|
||||
.write = line_write,
|
||||
.put_char = line_put_char,
|
||||
.write_room = line_write_room,
|
||||
.chars_in_buffer = line_chars_in_buffer,
|
||||
.flush_buffer = line_flush_buffer,
|
||||
.flush_chars = line_flush_chars,
|
||||
.set_termios = line_set_termios,
|
||||
.throttle = line_throttle,
|
||||
.unthrottle = line_unthrottle,
|
||||
.install = ssl_install,
|
||||
.hangup = line_hangup,
|
||||
};
|
||||
|
||||
/* Changed by ssl_init and referenced by ssl_exit, which are both serialized
|
||||
* by being an initcall and exitcall, respectively.
|
||||
*/
|
||||
static int ssl_init_done = 0;
|
||||
|
||||
static void ssl_console_write(struct console *c, const char *string,
|
||||
unsigned len)
|
||||
{
|
||||
struct line *line = &serial_lines[c->index];
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&line->lock, flags);
|
||||
console_write_chan(line->chan_out, string, len);
|
||||
spin_unlock_irqrestore(&line->lock, flags);
|
||||
}
|
||||
|
||||
static struct tty_driver *ssl_console_device(struct console *c, int *index)
|
||||
{
|
||||
*index = c->index;
|
||||
return driver.driver;
|
||||
}
|
||||
|
||||
static int ssl_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct line *line = &serial_lines[co->index];
|
||||
|
||||
return console_open_chan(line, co);
|
||||
}
|
||||
|
||||
/* No locking for register_console call - relies on single-threaded initcalls */
|
||||
static struct console ssl_cons = {
|
||||
.name = "ttyS",
|
||||
.write = ssl_console_write,
|
||||
.device = ssl_console_device,
|
||||
.setup = ssl_console_setup,
|
||||
.flags = CON_PRINTBUFFER|CON_ANYTIME,
|
||||
.index = -1,
|
||||
};
|
||||
|
||||
static int ssl_init(void)
|
||||
{
|
||||
char *new_title;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
printk(KERN_INFO "Initializing software serial port version %d\n",
|
||||
ssl_version);
|
||||
|
||||
err = register_lines(&driver, &ssl_ops, serial_lines,
|
||||
ARRAY_SIZE(serial_lines));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
new_title = add_xterm_umid(opts.xterm_title);
|
||||
if (new_title != NULL)
|
||||
opts.xterm_title = new_title;
|
||||
|
||||
for (i = 0; i < NR_PORTS; i++) {
|
||||
char *error;
|
||||
char *s = conf[i];
|
||||
if (!s)
|
||||
s = def_conf;
|
||||
if (setup_one_line(serial_lines, i, s, &opts, &error))
|
||||
printk(KERN_ERR "setup_one_line failed for "
|
||||
"device %d : %s\n", i, error);
|
||||
}
|
||||
|
||||
ssl_init_done = 1;
|
||||
register_console(&ssl_cons);
|
||||
return 0;
|
||||
}
|
||||
late_initcall(ssl_init);
|
||||
|
||||
static void ssl_exit(void)
|
||||
{
|
||||
if (!ssl_init_done)
|
||||
return;
|
||||
close_lines(serial_lines, ARRAY_SIZE(serial_lines));
|
||||
}
|
||||
__uml_exitcall(ssl_exit);
|
||||
|
||||
static int ssl_chan_setup(char *str)
|
||||
{
|
||||
line_setup(conf, NR_PORTS, &def_conf, str, "serial line");
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("ssl", ssl_chan_setup);
|
||||
__channel_help(ssl_chan_setup, "ssl");
|
13
arch/um/drivers/ssl.h
Normal file
13
arch/um/drivers/ssl.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __SSL_H__
|
||||
#define __SSL_H__
|
||||
|
||||
extern int ssl_read(int fd, int line);
|
||||
extern void ssl_receive_char(int line, char ch);
|
||||
|
||||
#endif
|
||||
|
62
arch/um/drivers/stderr_console.c
Normal file
62
arch/um/drivers/stderr_console.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
|
||||
#include "chan_user.h"
|
||||
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
/* trivial console driver -- simply dump everything to stderr */
|
||||
|
||||
/*
|
||||
* Don't register by default -- as this registers very early in the
|
||||
* boot process it becomes the default console.
|
||||
*
|
||||
* Initialized at init time.
|
||||
*/
|
||||
static int use_stderr_console = 0;
|
||||
|
||||
static void stderr_console_write(struct console *console, const char *string,
|
||||
unsigned len)
|
||||
{
|
||||
generic_write(2 /* stderr */, string, len, NULL);
|
||||
}
|
||||
|
||||
static struct console stderr_console = {
|
||||
.name = "stderr",
|
||||
.write = stderr_console_write,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
};
|
||||
|
||||
static int __init stderr_console_init(void)
|
||||
{
|
||||
if (use_stderr_console)
|
||||
register_console(&stderr_console);
|
||||
return 0;
|
||||
}
|
||||
console_initcall(stderr_console_init);
|
||||
|
||||
static int stderr_setup(char *str)
|
||||
{
|
||||
if (!str)
|
||||
return 0;
|
||||
use_stderr_console = simple_strtoul(str,&str,0);
|
||||
return 1;
|
||||
}
|
||||
__setup("stderr=", stderr_setup);
|
||||
|
||||
/* The previous behavior of not unregistering led to /dev/console being
|
||||
* impossible to open. My FC5 filesystem started having init die, and the
|
||||
* system panicing because of this. Unregistering causes the real
|
||||
* console to become the default console, and /dev/console can then be
|
||||
* opened. Making this an initcall makes this happen late enough that
|
||||
* there is no added value in dumping everything to stderr, and the
|
||||
* normal console is good enough to show you all available output.
|
||||
*/
|
||||
static int __init unregister_stderr(void)
|
||||
{
|
||||
unregister_console(&stderr_console);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__initcall(unregister_stderr);
|
199
arch/um/drivers/stdio_console.c
Normal file
199
arch/um/drivers/stdio_console.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <linux/posix_types.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <asm/current.h>
|
||||
#include <asm/irq.h>
|
||||
#include "stdio_console.h"
|
||||
#include "chan.h"
|
||||
#include <irq_user.h>
|
||||
#include "mconsole_kern.h"
|
||||
#include <init.h>
|
||||
|
||||
#define MAX_TTYS (16)
|
||||
|
||||
static void stdio_announce(char *dev_name, int dev)
|
||||
{
|
||||
printk(KERN_INFO "Virtual console %d assigned device '%s'\n", dev,
|
||||
dev_name);
|
||||
}
|
||||
|
||||
/* Almost const, except that xterm_title may be changed in an initcall */
|
||||
static struct chan_opts opts = {
|
||||
.announce = stdio_announce,
|
||||
.xterm_title = "Virtual Console #%d",
|
||||
.raw = 1,
|
||||
};
|
||||
|
||||
static int con_config(char *str, char **error_out);
|
||||
static int con_get_config(char *dev, char *str, int size, char **error_out);
|
||||
static int con_remove(int n, char **con_remove);
|
||||
|
||||
|
||||
/* Const, except for .mc.list */
|
||||
static struct line_driver driver = {
|
||||
.name = "UML console",
|
||||
.device_name = "tty",
|
||||
.major = TTY_MAJOR,
|
||||
.minor_start = 0,
|
||||
.type = TTY_DRIVER_TYPE_CONSOLE,
|
||||
.subtype = SYSTEM_TYPE_CONSOLE,
|
||||
.read_irq = CONSOLE_IRQ,
|
||||
.read_irq_name = "console",
|
||||
.write_irq = CONSOLE_WRITE_IRQ,
|
||||
.write_irq_name = "console-write",
|
||||
.mc = {
|
||||
.list = LIST_HEAD_INIT(driver.mc.list),
|
||||
.name = "con",
|
||||
.config = con_config,
|
||||
.get_config = con_get_config,
|
||||
.id = line_id,
|
||||
.remove = con_remove,
|
||||
},
|
||||
};
|
||||
|
||||
/* The array is initialized by line_init, at initcall time. The
|
||||
* elements are locked individually as needed.
|
||||
*/
|
||||
static char *vt_conf[MAX_TTYS];
|
||||
static char *def_conf;
|
||||
static struct line vts[MAX_TTYS];
|
||||
|
||||
static int con_config(char *str, char **error_out)
|
||||
{
|
||||
return line_config(vts, ARRAY_SIZE(vts), str, &opts, error_out);
|
||||
}
|
||||
|
||||
static int con_get_config(char *dev, char *str, int size, char **error_out)
|
||||
{
|
||||
return line_get_config(dev, vts, ARRAY_SIZE(vts), str, size, error_out);
|
||||
}
|
||||
|
||||
static int con_remove(int n, char **error_out)
|
||||
{
|
||||
return line_remove(vts, ARRAY_SIZE(vts), n, error_out);
|
||||
}
|
||||
|
||||
/* Set in an initcall, checked in an exitcall */
|
||||
static int con_init_done = 0;
|
||||
|
||||
static int con_install(struct tty_driver *driver, struct tty_struct *tty)
|
||||
{
|
||||
return line_install(driver, tty, &vts[tty->index]);
|
||||
}
|
||||
|
||||
static const struct tty_operations console_ops = {
|
||||
.open = line_open,
|
||||
.install = con_install,
|
||||
.close = line_close,
|
||||
.write = line_write,
|
||||
.put_char = line_put_char,
|
||||
.write_room = line_write_room,
|
||||
.chars_in_buffer = line_chars_in_buffer,
|
||||
.flush_buffer = line_flush_buffer,
|
||||
.flush_chars = line_flush_chars,
|
||||
.set_termios = line_set_termios,
|
||||
.throttle = line_throttle,
|
||||
.unthrottle = line_unthrottle,
|
||||
.hangup = line_hangup,
|
||||
};
|
||||
|
||||
static void uml_console_write(struct console *console, const char *string,
|
||||
unsigned len)
|
||||
{
|
||||
struct line *line = &vts[console->index];
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&line->lock, flags);
|
||||
console_write_chan(line->chan_out, string, len);
|
||||
spin_unlock_irqrestore(&line->lock, flags);
|
||||
}
|
||||
|
||||
static struct tty_driver *uml_console_device(struct console *c, int *index)
|
||||
{
|
||||
*index = c->index;
|
||||
return driver.driver;
|
||||
}
|
||||
|
||||
static int uml_console_setup(struct console *co, char *options)
|
||||
{
|
||||
struct line *line = &vts[co->index];
|
||||
|
||||
return console_open_chan(line, co);
|
||||
}
|
||||
|
||||
/* No locking for register_console call - relies on single-threaded initcalls */
|
||||
static struct console stdiocons = {
|
||||
.name = "tty",
|
||||
.write = uml_console_write,
|
||||
.device = uml_console_device,
|
||||
.setup = uml_console_setup,
|
||||
.flags = CON_PRINTBUFFER|CON_ANYTIME,
|
||||
.index = -1,
|
||||
};
|
||||
|
||||
static int stdio_init(void)
|
||||
{
|
||||
char *new_title;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
err = register_lines(&driver, &console_ops, vts,
|
||||
ARRAY_SIZE(vts));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
printk(KERN_INFO "Initialized stdio console driver\n");
|
||||
|
||||
new_title = add_xterm_umid(opts.xterm_title);
|
||||
if(new_title != NULL)
|
||||
opts.xterm_title = new_title;
|
||||
|
||||
for (i = 0; i < MAX_TTYS; i++) {
|
||||
char *error;
|
||||
char *s = vt_conf[i];
|
||||
if (!s)
|
||||
s = def_conf;
|
||||
if (!s)
|
||||
s = i ? CONFIG_CON_CHAN : CONFIG_CON_ZERO_CHAN;
|
||||
if (setup_one_line(vts, i, s, &opts, &error))
|
||||
printk(KERN_ERR "setup_one_line failed for "
|
||||
"device %d : %s\n", i, error);
|
||||
}
|
||||
|
||||
con_init_done = 1;
|
||||
register_console(&stdiocons);
|
||||
return 0;
|
||||
}
|
||||
late_initcall(stdio_init);
|
||||
|
||||
static void console_exit(void)
|
||||
{
|
||||
if (!con_init_done)
|
||||
return;
|
||||
close_lines(vts, ARRAY_SIZE(vts));
|
||||
}
|
||||
__uml_exitcall(console_exit);
|
||||
|
||||
static int console_chan_setup(char *str)
|
||||
{
|
||||
line_setup(vt_conf, MAX_TTYS, &def_conf, str, "console");
|
||||
return 1;
|
||||
}
|
||||
__setup("con", console_chan_setup);
|
||||
__channel_help(console_chan_setup, "con");
|
11
arch/um/drivers/stdio_console.h
Normal file
11
arch/um/drivers/stdio_console.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __STDIO_CONSOLE_H
|
||||
#define __STDIO_CONSOLE_H
|
||||
|
||||
extern void save_console_flags(void);
|
||||
#endif
|
||||
|
81
arch/um/drivers/tty.c
Normal file
81
arch/um/drivers/tty.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include "chan_user.h"
|
||||
#include <os.h>
|
||||
#include <um_malloc.h>
|
||||
|
||||
struct tty_chan {
|
||||
char *dev;
|
||||
int raw;
|
||||
struct termios tt;
|
||||
};
|
||||
|
||||
static void *tty_chan_init(char *str, int device, const struct chan_opts *opts)
|
||||
{
|
||||
struct tty_chan *data;
|
||||
|
||||
if (*str != ':') {
|
||||
printk(UM_KERN_ERR "tty_init : channel type 'tty' must specify "
|
||||
"a device\n");
|
||||
return NULL;
|
||||
}
|
||||
str++;
|
||||
|
||||
data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
*data = ((struct tty_chan) { .dev = str,
|
||||
.raw = opts->raw });
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int tty_open(int input, int output, int primary, void *d,
|
||||
char **dev_out)
|
||||
{
|
||||
struct tty_chan *data = d;
|
||||
int fd, err, mode = 0;
|
||||
|
||||
if (input && output)
|
||||
mode = O_RDWR;
|
||||
else if (input)
|
||||
mode = O_RDONLY;
|
||||
else if (output)
|
||||
mode = O_WRONLY;
|
||||
|
||||
fd = open(data->dev, mode);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
if (data->raw) {
|
||||
CATCH_EINTR(err = tcgetattr(fd, &data->tt));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = raw(fd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
*dev_out = data->dev;
|
||||
return fd;
|
||||
}
|
||||
|
||||
const struct chan_ops tty_ops = {
|
||||
.type = "tty",
|
||||
.init = tty_chan_init,
|
||||
.open = tty_open,
|
||||
.close = generic_close,
|
||||
.read = generic_read,
|
||||
.write = generic_write,
|
||||
.console_write = generic_console_write,
|
||||
.window_size = generic_window_size,
|
||||
.free = generic_free,
|
||||
.winch = 0,
|
||||
};
|
15
arch/um/drivers/ubd.h
Normal file
15
arch/um/drivers/ubd.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
|
||||
* Copyright (C) 2001 RidgeRun, Inc (glonnon@ridgerun.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __UM_UBD_USER_H
|
||||
#define __UM_UBD_USER_H
|
||||
|
||||
extern int start_io_thread(unsigned long sp, int *fds_out);
|
||||
extern int io_thread(void *arg);
|
||||
extern int kernel_fd;
|
||||
|
||||
#endif
|
||||
|
1505
arch/um/drivers/ubd_kern.c
Normal file
1505
arch/um/drivers/ubd_kern.c
Normal file
File diff suppressed because it is too large
Load diff
59
arch/um/drivers/ubd_user.c
Normal file
59
arch/um/drivers/ubd_user.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/param.h>
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
|
||||
#include "ubd.h"
|
||||
#include <os.h>
|
||||
|
||||
int start_io_thread(unsigned long sp, int *fd_out)
|
||||
{
|
||||
int pid, fds[2], err;
|
||||
|
||||
err = os_pipe(fds, 1, 1);
|
||||
if(err < 0){
|
||||
printk("start_io_thread - os_pipe failed, err = %d\n", -err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
kernel_fd = fds[0];
|
||||
*fd_out = fds[1];
|
||||
|
||||
err = os_set_fd_block(*fd_out, 0);
|
||||
if (err) {
|
||||
printk("start_io_thread - failed to set nonblocking I/O.\n");
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
pid = clone(io_thread, (void *) sp, CLONE_FILES | CLONE_VM, NULL);
|
||||
if(pid < 0){
|
||||
err = -errno;
|
||||
printk("start_io_thread - clone failed : errno = %d\n", errno);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
return(pid);
|
||||
|
||||
out_close:
|
||||
os_close_file(fds[0]);
|
||||
os_close_file(fds[1]);
|
||||
kernel_fd = -1;
|
||||
*fd_out = -1;
|
||||
out:
|
||||
return err;
|
||||
}
|
27
arch/um/drivers/umcast.h
Normal file
27
arch/um/drivers/umcast.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_UMCAST_H
|
||||
#define __DRIVERS_UMCAST_H
|
||||
|
||||
#include <net_user.h>
|
||||
|
||||
struct umcast_data {
|
||||
char *addr;
|
||||
unsigned short lport;
|
||||
unsigned short rport;
|
||||
void *listen_addr;
|
||||
void *remote_addr;
|
||||
int ttl;
|
||||
int unicast;
|
||||
void *dev;
|
||||
};
|
||||
|
||||
extern const struct net_user_info umcast_user_info;
|
||||
|
||||
extern int umcast_user_write(int fd, void *buf, int len,
|
||||
struct umcast_data *pri);
|
||||
|
||||
#endif
|
188
arch/um/drivers/umcast_kern.c
Normal file
188
arch/um/drivers/umcast_kern.c
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* user-mode-linux networking multicast transport
|
||||
* Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org>
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
*
|
||||
* based on the existing uml-networking code, which is
|
||||
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
|
||||
* James Leu (jleu@mindspring.net).
|
||||
* Copyright (C) 2001 by various other people who didn't put their name here.
|
||||
*
|
||||
* Licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include "umcast.h"
|
||||
#include <net_kern.h>
|
||||
|
||||
struct umcast_init {
|
||||
char *addr;
|
||||
int lport;
|
||||
int rport;
|
||||
int ttl;
|
||||
bool unicast;
|
||||
};
|
||||
|
||||
static void umcast_init(struct net_device *dev, void *data)
|
||||
{
|
||||
struct uml_net_private *pri;
|
||||
struct umcast_data *dpri;
|
||||
struct umcast_init *init = data;
|
||||
|
||||
pri = netdev_priv(dev);
|
||||
dpri = (struct umcast_data *) pri->user;
|
||||
dpri->addr = init->addr;
|
||||
dpri->lport = init->lport;
|
||||
dpri->rport = init->rport;
|
||||
dpri->unicast = init->unicast;
|
||||
dpri->ttl = init->ttl;
|
||||
dpri->dev = dev;
|
||||
|
||||
if (dpri->unicast) {
|
||||
printk(KERN_INFO "ucast backend address: %s:%u listen port: "
|
||||
"%u\n", dpri->addr, dpri->rport, dpri->lport);
|
||||
} else {
|
||||
printk(KERN_INFO "mcast backend multicast address: %s:%u, "
|
||||
"TTL:%u\n", dpri->addr, dpri->lport, dpri->ttl);
|
||||
}
|
||||
}
|
||||
|
||||
static int umcast_read(int fd, struct sk_buff *skb, struct uml_net_private *lp)
|
||||
{
|
||||
return net_recvfrom(fd, skb_mac_header(skb),
|
||||
skb->dev->mtu + ETH_HEADER_OTHER);
|
||||
}
|
||||
|
||||
static int umcast_write(int fd, struct sk_buff *skb, struct uml_net_private *lp)
|
||||
{
|
||||
return umcast_user_write(fd, skb->data, skb->len,
|
||||
(struct umcast_data *) &lp->user);
|
||||
}
|
||||
|
||||
static const struct net_kern_info umcast_kern_info = {
|
||||
.init = umcast_init,
|
||||
.protocol = eth_protocol,
|
||||
.read = umcast_read,
|
||||
.write = umcast_write,
|
||||
};
|
||||
|
||||
static int mcast_setup(char *str, char **mac_out, void *data)
|
||||
{
|
||||
struct umcast_init *init = data;
|
||||
char *port_str = NULL, *ttl_str = NULL, *remain;
|
||||
char *last;
|
||||
|
||||
*init = ((struct umcast_init)
|
||||
{ .addr = "239.192.168.1",
|
||||
.lport = 1102,
|
||||
.ttl = 1 });
|
||||
|
||||
remain = split_if_spec(str, mac_out, &init->addr, &port_str, &ttl_str,
|
||||
NULL);
|
||||
if (remain != NULL) {
|
||||
printk(KERN_ERR "mcast_setup - Extra garbage on "
|
||||
"specification : '%s'\n", remain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (port_str != NULL) {
|
||||
init->lport = simple_strtoul(port_str, &last, 10);
|
||||
if ((*last != '\0') || (last == port_str)) {
|
||||
printk(KERN_ERR "mcast_setup - Bad port : '%s'\n",
|
||||
port_str);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ttl_str != NULL) {
|
||||
init->ttl = simple_strtoul(ttl_str, &last, 10);
|
||||
if ((*last != '\0') || (last == ttl_str)) {
|
||||
printk(KERN_ERR "mcast_setup - Bad ttl : '%s'\n",
|
||||
ttl_str);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
init->unicast = false;
|
||||
init->rport = init->lport;
|
||||
|
||||
printk(KERN_INFO "Configured mcast device: %s:%u-%u\n", init->addr,
|
||||
init->lport, init->ttl);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ucast_setup(char *str, char **mac_out, void *data)
|
||||
{
|
||||
struct umcast_init *init = data;
|
||||
char *lport_str = NULL, *rport_str = NULL, *remain;
|
||||
char *last;
|
||||
|
||||
*init = ((struct umcast_init)
|
||||
{ .addr = "",
|
||||
.lport = 1102,
|
||||
.rport = 1102 });
|
||||
|
||||
remain = split_if_spec(str, mac_out, &init->addr,
|
||||
&lport_str, &rport_str, NULL);
|
||||
if (remain != NULL) {
|
||||
printk(KERN_ERR "ucast_setup - Extra garbage on "
|
||||
"specification : '%s'\n", remain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lport_str != NULL) {
|
||||
init->lport = simple_strtoul(lport_str, &last, 10);
|
||||
if ((*last != '\0') || (last == lport_str)) {
|
||||
printk(KERN_ERR "ucast_setup - Bad listen port : "
|
||||
"'%s'\n", lport_str);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (rport_str != NULL) {
|
||||
init->rport = simple_strtoul(rport_str, &last, 10);
|
||||
if ((*last != '\0') || (last == rport_str)) {
|
||||
printk(KERN_ERR "ucast_setup - Bad remote port : "
|
||||
"'%s'\n", rport_str);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
init->unicast = true;
|
||||
|
||||
printk(KERN_INFO "Configured ucast device: :%u -> %s:%u\n",
|
||||
init->lport, init->addr, init->rport);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct transport mcast_transport = {
|
||||
.list = LIST_HEAD_INIT(mcast_transport.list),
|
||||
.name = "mcast",
|
||||
.setup = mcast_setup,
|
||||
.user = &umcast_user_info,
|
||||
.kern = &umcast_kern_info,
|
||||
.private_size = sizeof(struct umcast_data),
|
||||
.setup_size = sizeof(struct umcast_init),
|
||||
};
|
||||
|
||||
static struct transport ucast_transport = {
|
||||
.list = LIST_HEAD_INIT(ucast_transport.list),
|
||||
.name = "ucast",
|
||||
.setup = ucast_setup,
|
||||
.user = &umcast_user_info,
|
||||
.kern = &umcast_kern_info,
|
||||
.private_size = sizeof(struct umcast_data),
|
||||
.setup_size = sizeof(struct umcast_init),
|
||||
};
|
||||
|
||||
static int register_umcast(void)
|
||||
{
|
||||
register_transport(&mcast_transport);
|
||||
register_transport(&ucast_transport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(register_umcast);
|
184
arch/um/drivers/umcast_user.c
Normal file
184
arch/um/drivers/umcast_user.c
Normal file
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* user-mode-linux networking multicast transport
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* based on the existing uml-networking code, which is
|
||||
* Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and
|
||||
* James Leu (jleu@mindspring.net).
|
||||
* Copyright (C) 2001 by various other people who didn't put their name here.
|
||||
*
|
||||
* Licensed under the GPL.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include "umcast.h"
|
||||
#include <net_user.h>
|
||||
#include <um_malloc.h>
|
||||
|
||||
static struct sockaddr_in *new_addr(char *addr, unsigned short port)
|
||||
{
|
||||
struct sockaddr_in *sin;
|
||||
|
||||
sin = uml_kmalloc(sizeof(struct sockaddr_in), UM_GFP_KERNEL);
|
||||
if (sin == NULL) {
|
||||
printk(UM_KERN_ERR "new_addr: allocation of sockaddr_in "
|
||||
"failed\n");
|
||||
return NULL;
|
||||
}
|
||||
sin->sin_family = AF_INET;
|
||||
if (addr)
|
||||
sin->sin_addr.s_addr = in_aton(addr);
|
||||
else
|
||||
sin->sin_addr.s_addr = INADDR_ANY;
|
||||
sin->sin_port = htons(port);
|
||||
return sin;
|
||||
}
|
||||
|
||||
static int umcast_user_init(void *data, void *dev)
|
||||
{
|
||||
struct umcast_data *pri = data;
|
||||
|
||||
pri->remote_addr = new_addr(pri->addr, pri->rport);
|
||||
if (pri->unicast)
|
||||
pri->listen_addr = new_addr(NULL, pri->lport);
|
||||
else
|
||||
pri->listen_addr = pri->remote_addr;
|
||||
pri->dev = dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void umcast_remove(void *data)
|
||||
{
|
||||
struct umcast_data *pri = data;
|
||||
|
||||
kfree(pri->listen_addr);
|
||||
if (pri->unicast)
|
||||
kfree(pri->remote_addr);
|
||||
pri->listen_addr = pri->remote_addr = NULL;
|
||||
}
|
||||
|
||||
static int umcast_open(void *data)
|
||||
{
|
||||
struct umcast_data *pri = data;
|
||||
struct sockaddr_in *lsin = pri->listen_addr;
|
||||
struct sockaddr_in *rsin = pri->remote_addr;
|
||||
struct ip_mreq mreq;
|
||||
int fd, yes = 1, err = -EINVAL;
|
||||
|
||||
|
||||
if ((!pri->unicast && lsin->sin_addr.s_addr == 0) ||
|
||||
(rsin->sin_addr.s_addr == 0) ||
|
||||
(lsin->sin_port == 0) || (rsin->sin_port == 0))
|
||||
goto out;
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
if (fd < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "umcast_open : data socket failed, "
|
||||
"errno = %d\n", errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "umcast_open: SO_REUSEADDR failed, "
|
||||
"errno = %d\n", errno);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (!pri->unicast) {
|
||||
/* set ttl according to config */
|
||||
if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl,
|
||||
sizeof(pri->ttl)) < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_TTL "
|
||||
"failed, error = %d\n", errno);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/* set LOOP, so data does get fed back to local sockets */
|
||||
if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP,
|
||||
&yes, sizeof(yes)) < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "umcast_open: IP_MULTICAST_LOOP "
|
||||
"failed, error = %d\n", errno);
|
||||
goto out_close;
|
||||
}
|
||||
}
|
||||
|
||||
/* bind socket to the address */
|
||||
if (bind(fd, (struct sockaddr *) lsin, sizeof(*lsin)) < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "umcast_open : data bind failed, "
|
||||
"errno = %d\n", errno);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (!pri->unicast) {
|
||||
/* subscribe to the multicast group */
|
||||
mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
|
||||
mreq.imr_interface.s_addr = 0;
|
||||
if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP,
|
||||
&mreq, sizeof(mreq)) < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "umcast_open: IP_ADD_MEMBERSHIP "
|
||||
"failed, error = %d\n", errno);
|
||||
printk(UM_KERN_ERR "There appears not to be a "
|
||||
"multicast-capable network interface on the "
|
||||
"host.\n");
|
||||
printk(UM_KERN_ERR "eth0 should be configured in order "
|
||||
"to use the multicast transport.\n");
|
||||
goto out_close;
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
|
||||
out_close:
|
||||
close(fd);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void umcast_close(int fd, void *data)
|
||||
{
|
||||
struct umcast_data *pri = data;
|
||||
|
||||
if (!pri->unicast) {
|
||||
struct ip_mreq mreq;
|
||||
struct sockaddr_in *lsin = pri->listen_addr;
|
||||
|
||||
mreq.imr_multiaddr.s_addr = lsin->sin_addr.s_addr;
|
||||
mreq.imr_interface.s_addr = 0;
|
||||
if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP,
|
||||
&mreq, sizeof(mreq)) < 0) {
|
||||
printk(UM_KERN_ERR "umcast_close: IP_DROP_MEMBERSHIP "
|
||||
"failed, error = %d\n", errno);
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int umcast_user_write(int fd, void *buf, int len, struct umcast_data *pri)
|
||||
{
|
||||
struct sockaddr_in *data_addr = pri->remote_addr;
|
||||
|
||||
return net_sendto(fd, buf, len, data_addr, sizeof(*data_addr));
|
||||
}
|
||||
|
||||
const struct net_user_info umcast_user_info = {
|
||||
.init = umcast_user_init,
|
||||
.open = umcast_open,
|
||||
.close = umcast_close,
|
||||
.remove = umcast_remove,
|
||||
.add_address = NULL,
|
||||
.delete_address = NULL,
|
||||
.mtu = ETH_MAX_PACKET,
|
||||
.max_packet = ETH_MAX_PACKET + ETH_HEADER_OTHER,
|
||||
};
|
32
arch/um/drivers/vde.h
Normal file
32
arch/um/drivers/vde.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (C) 2007 Luca Bigliardi (shammash@artha.org).
|
||||
* Licensed under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef __UM_VDE_H__
|
||||
#define __UM_VDE_H__
|
||||
|
||||
struct vde_data {
|
||||
char *vde_switch;
|
||||
char *descr;
|
||||
void *args;
|
||||
void *conn;
|
||||
void *dev;
|
||||
};
|
||||
|
||||
struct vde_init {
|
||||
char *vde_switch;
|
||||
char *descr;
|
||||
int port;
|
||||
char *group;
|
||||
int mode;
|
||||
};
|
||||
|
||||
extern const struct net_user_info vde_user_info;
|
||||
|
||||
extern void vde_init_libstuff(struct vde_data *vpri, struct vde_init *init);
|
||||
|
||||
extern int vde_user_read(void *conn, void *buf, int len);
|
||||
extern int vde_user_write(void *conn, void *buf, int len);
|
||||
|
||||
#endif
|
129
arch/um/drivers/vde_kern.c
Normal file
129
arch/um/drivers/vde_kern.c
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (C) 2007 Luca Bigliardi (shammash@artha.org).
|
||||
* Licensed under the GPL.
|
||||
*
|
||||
* Transport usage:
|
||||
* ethN=vde,<vde_switch>,<mac addr>,<port>,<group>,<mode>,<description>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <net_kern.h>
|
||||
#include <net_user.h>
|
||||
#include "vde.h"
|
||||
|
||||
static void vde_init(struct net_device *dev, void *data)
|
||||
{
|
||||
struct vde_init *init = data;
|
||||
struct uml_net_private *pri;
|
||||
struct vde_data *vpri;
|
||||
|
||||
pri = netdev_priv(dev);
|
||||
vpri = (struct vde_data *) pri->user;
|
||||
|
||||
vpri->vde_switch = init->vde_switch;
|
||||
vpri->descr = init->descr ? init->descr : "UML vde_transport";
|
||||
vpri->args = NULL;
|
||||
vpri->conn = NULL;
|
||||
vpri->dev = dev;
|
||||
|
||||
printk("vde backend - %s, ", vpri->vde_switch ?
|
||||
vpri->vde_switch : "(default socket)");
|
||||
|
||||
vde_init_libstuff(vpri, init);
|
||||
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
static int vde_read(int fd, struct sk_buff *skb, struct uml_net_private *lp)
|
||||
{
|
||||
struct vde_data *pri = (struct vde_data *) &lp->user;
|
||||
|
||||
if (pri->conn != NULL)
|
||||
return vde_user_read(pri->conn, skb_mac_header(skb),
|
||||
skb->dev->mtu + ETH_HEADER_OTHER);
|
||||
|
||||
printk(KERN_ERR "vde_read - we have no VDECONN to read from");
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
static int vde_write(int fd, struct sk_buff *skb, struct uml_net_private *lp)
|
||||
{
|
||||
struct vde_data *pri = (struct vde_data *) &lp->user;
|
||||
|
||||
if (pri->conn != NULL)
|
||||
return vde_user_write((void *)pri->conn, skb->data,
|
||||
skb->len);
|
||||
|
||||
printk(KERN_ERR "vde_write - we have no VDECONN to write to");
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
static const struct net_kern_info vde_kern_info = {
|
||||
.init = vde_init,
|
||||
.protocol = eth_protocol,
|
||||
.read = vde_read,
|
||||
.write = vde_write,
|
||||
};
|
||||
|
||||
static int vde_setup(char *str, char **mac_out, void *data)
|
||||
{
|
||||
struct vde_init *init = data;
|
||||
char *remain, *port_str = NULL, *mode_str = NULL, *last;
|
||||
|
||||
*init = ((struct vde_init)
|
||||
{ .vde_switch = NULL,
|
||||
.descr = NULL,
|
||||
.port = 0,
|
||||
.group = NULL,
|
||||
.mode = 0 });
|
||||
|
||||
remain = split_if_spec(str, &init->vde_switch, mac_out, &port_str,
|
||||
&init->group, &mode_str, &init->descr, NULL);
|
||||
|
||||
if (remain != NULL)
|
||||
printk(KERN_WARNING "vde_setup - Ignoring extra data :"
|
||||
"'%s'\n", remain);
|
||||
|
||||
if (port_str != NULL) {
|
||||
init->port = simple_strtoul(port_str, &last, 10);
|
||||
if ((*last != '\0') || (last == port_str)) {
|
||||
printk(KERN_ERR "vde_setup - Bad port : '%s'\n",
|
||||
port_str);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode_str != NULL) {
|
||||
init->mode = simple_strtoul(mode_str, &last, 8);
|
||||
if ((*last != '\0') || (last == mode_str)) {
|
||||
printk(KERN_ERR "vde_setup - Bad mode : '%s'\n",
|
||||
mode_str);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printk(KERN_INFO "Configured vde device: %s\n", init->vde_switch ?
|
||||
init->vde_switch : "(default socket)");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct transport vde_transport = {
|
||||
.list = LIST_HEAD_INIT(vde_transport.list),
|
||||
.name = "vde",
|
||||
.setup = vde_setup,
|
||||
.user = &vde_user_info,
|
||||
.kern = &vde_kern_info,
|
||||
.private_size = sizeof(struct vde_data),
|
||||
.setup_size = sizeof(struct vde_init),
|
||||
};
|
||||
|
||||
static int register_vde(void)
|
||||
{
|
||||
register_transport(&vde_transport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(register_vde);
|
125
arch/um/drivers/vde_user.c
Normal file
125
arch/um/drivers/vde_user.c
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (C) 2007 Luca Bigliardi (shammash@artha.org).
|
||||
* Licensed under the GPL.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
#include <libvdeplug.h>
|
||||
#include <net_user.h>
|
||||
#include <um_malloc.h>
|
||||
#include "vde.h"
|
||||
|
||||
static int vde_user_init(void *data, void *dev)
|
||||
{
|
||||
struct vde_data *pri = data;
|
||||
VDECONN *conn = NULL;
|
||||
int err = -EINVAL;
|
||||
|
||||
pri->dev = dev;
|
||||
|
||||
conn = vde_open(pri->vde_switch, pri->descr, pri->args);
|
||||
|
||||
if (conn == NULL) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "vde_user_init: vde_open failed, "
|
||||
"errno = %d\n", errno);
|
||||
return err;
|
||||
}
|
||||
|
||||
printk(UM_KERN_INFO "vde backend - connection opened\n");
|
||||
|
||||
pri->conn = conn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vde_user_open(void *data)
|
||||
{
|
||||
struct vde_data *pri = data;
|
||||
|
||||
if (pri->conn != NULL)
|
||||
return vde_datafd(pri->conn);
|
||||
|
||||
printk(UM_KERN_WARNING "vde_open - we have no VDECONN to open");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void vde_remove(void *data)
|
||||
{
|
||||
struct vde_data *pri = data;
|
||||
|
||||
if (pri->conn != NULL) {
|
||||
printk(UM_KERN_INFO "vde backend - closing connection\n");
|
||||
vde_close(pri->conn);
|
||||
pri->conn = NULL;
|
||||
kfree(pri->args);
|
||||
pri->args = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
printk(UM_KERN_WARNING "vde_remove - we have no VDECONN to remove");
|
||||
}
|
||||
|
||||
const struct net_user_info vde_user_info = {
|
||||
.init = vde_user_init,
|
||||
.open = vde_user_open,
|
||||
.close = NULL,
|
||||
.remove = vde_remove,
|
||||
.add_address = NULL,
|
||||
.delete_address = NULL,
|
||||
.mtu = ETH_MAX_PACKET,
|
||||
.max_packet = ETH_MAX_PACKET + ETH_HEADER_OTHER,
|
||||
};
|
||||
|
||||
void vde_init_libstuff(struct vde_data *vpri, struct vde_init *init)
|
||||
{
|
||||
struct vde_open_args *args;
|
||||
|
||||
vpri->args = uml_kmalloc(sizeof(struct vde_open_args), UM_GFP_KERNEL);
|
||||
if (vpri->args == NULL) {
|
||||
printk(UM_KERN_ERR "vde_init_libstuff - vde_open_args "
|
||||
"allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
args = vpri->args;
|
||||
|
||||
args->port = init->port;
|
||||
args->group = init->group;
|
||||
args->mode = init->mode ? init->mode : 0700;
|
||||
|
||||
args->port ? printk("port %d", args->port) :
|
||||
printk("undefined port");
|
||||
}
|
||||
|
||||
int vde_user_read(void *conn, void *buf, int len)
|
||||
{
|
||||
VDECONN *vconn = conn;
|
||||
int rv;
|
||||
|
||||
if (vconn == NULL)
|
||||
return 0;
|
||||
|
||||
rv = vde_recv(vconn, buf, len, 0);
|
||||
if (rv < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
return -errno;
|
||||
}
|
||||
else if (rv == 0)
|
||||
return -ENOTCONN;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int vde_user_write(void *conn, void *buf, int len)
|
||||
{
|
||||
VDECONN *vconn = conn;
|
||||
|
||||
if (vconn == NULL)
|
||||
return 0;
|
||||
|
||||
return vde_send(vconn, buf, len, 0);
|
||||
}
|
||||
|
223
arch/um/drivers/xterm.c
Normal file
223
arch/um/drivers/xterm.c
Normal file
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include "chan_user.h"
|
||||
#include <os.h>
|
||||
#include <um_malloc.h>
|
||||
#include "xterm.h"
|
||||
|
||||
struct xterm_chan {
|
||||
int pid;
|
||||
int helper_pid;
|
||||
char *title;
|
||||
int device;
|
||||
int raw;
|
||||
struct termios tt;
|
||||
};
|
||||
|
||||
static void *xterm_init(char *str, int device, const struct chan_opts *opts)
|
||||
{
|
||||
struct xterm_chan *data;
|
||||
|
||||
data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
*data = ((struct xterm_chan) { .pid = -1,
|
||||
.helper_pid = -1,
|
||||
.device = device,
|
||||
.title = opts->xterm_title,
|
||||
.raw = opts->raw } );
|
||||
return data;
|
||||
}
|
||||
|
||||
/* Only changed by xterm_setup, which is a setup */
|
||||
static char *terminal_emulator = "xterm";
|
||||
static char *title_switch = "-T";
|
||||
static char *exec_switch = "-e";
|
||||
|
||||
static int __init xterm_setup(char *line, int *add)
|
||||
{
|
||||
*add = 0;
|
||||
terminal_emulator = line;
|
||||
|
||||
line = strchr(line, ',');
|
||||
if (line == NULL)
|
||||
return 0;
|
||||
|
||||
*line++ = '\0';
|
||||
if (*line)
|
||||
title_switch = line;
|
||||
|
||||
line = strchr(line, ',');
|
||||
if (line == NULL)
|
||||
return 0;
|
||||
|
||||
*line++ = '\0';
|
||||
if (*line)
|
||||
exec_switch = line;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__uml_setup("xterm=", xterm_setup,
|
||||
"xterm=<terminal emulator>,<title switch>,<exec switch>\n"
|
||||
" Specifies an alternate terminal emulator to use for the debugger,\n"
|
||||
" consoles, and serial lines when they are attached to the xterm channel.\n"
|
||||
" The values are the terminal emulator binary, the switch it uses to set\n"
|
||||
" its title, and the switch it uses to execute a subprocess,\n"
|
||||
" respectively. The title switch must have the form '<switch> title',\n"
|
||||
" not '<switch>=title'. Similarly, the exec switch must have the form\n"
|
||||
" '<switch> command arg1 arg2 ...'.\n"
|
||||
" The default values are 'xterm=xterm,-T,-e'. Values for gnome-terminal\n"
|
||||
" are 'xterm=gnome-terminal,-t,-x'.\n\n"
|
||||
);
|
||||
|
||||
static int xterm_open(int input, int output, int primary, void *d,
|
||||
char **dev_out)
|
||||
{
|
||||
struct xterm_chan *data = d;
|
||||
int pid, fd, new, err;
|
||||
char title[256], file[] = "/tmp/xterm-pipeXXXXXX";
|
||||
char *argv[] = { terminal_emulator, title_switch, title, exec_switch,
|
||||
OS_LIB_PATH "/uml/port-helper", "-uml-socket",
|
||||
file, NULL };
|
||||
|
||||
if (access(argv[4], X_OK) < 0)
|
||||
argv[4] = "port-helper";
|
||||
|
||||
/*
|
||||
* Check that DISPLAY is set, this doesn't guarantee the xterm
|
||||
* will work but w/o it we can be pretty sure it won't.
|
||||
*/
|
||||
if (getenv("DISPLAY") == NULL) {
|
||||
printk(UM_KERN_ERR "xterm_open: $DISPLAY not set.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* This business of getting a descriptor to a temp file,
|
||||
* deleting the file and closing the descriptor is just to get
|
||||
* a known-unused name for the Unix socket that we really
|
||||
* want.
|
||||
*/
|
||||
fd = mkstemp(file);
|
||||
if (fd < 0) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "xterm_open : mkstemp failed, errno = %d\n",
|
||||
errno);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (unlink(file)) {
|
||||
err = -errno;
|
||||
printk(UM_KERN_ERR "xterm_open : unlink failed, errno = %d\n",
|
||||
errno);
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
fd = os_create_unix_socket(file, sizeof(file), 1);
|
||||
if (fd < 0) {
|
||||
printk(UM_KERN_ERR "xterm_open : create_unix_socket failed, "
|
||||
"errno = %d\n", -fd);
|
||||
return fd;
|
||||
}
|
||||
|
||||
sprintf(title, data->title, data->device);
|
||||
pid = run_helper(NULL, NULL, argv);
|
||||
if (pid < 0) {
|
||||
err = pid;
|
||||
printk(UM_KERN_ERR "xterm_open : run_helper failed, "
|
||||
"errno = %d\n", -err);
|
||||
goto out_close1;
|
||||
}
|
||||
|
||||
err = os_set_fd_block(fd, 0);
|
||||
if (err < 0) {
|
||||
printk(UM_KERN_ERR "xterm_open : failed to set descriptor "
|
||||
"non-blocking, err = %d\n", -err);
|
||||
goto out_kill;
|
||||
}
|
||||
|
||||
new = xterm_fd(fd, &data->helper_pid);
|
||||
if (new < 0) {
|
||||
err = new;
|
||||
printk(UM_KERN_ERR "xterm_open : os_rcv_fd failed, err = %d\n",
|
||||
-err);
|
||||
goto out_kill;
|
||||
}
|
||||
|
||||
err = os_set_fd_block(new, 0);
|
||||
if (err) {
|
||||
printk(UM_KERN_ERR "xterm_open : failed to set xterm "
|
||||
"descriptor non-blocking, err = %d\n", -err);
|
||||
goto out_close2;
|
||||
}
|
||||
|
||||
CATCH_EINTR(err = tcgetattr(new, &data->tt));
|
||||
if (err) {
|
||||
new = err;
|
||||
goto out_close2;
|
||||
}
|
||||
|
||||
if (data->raw) {
|
||||
err = raw(new);
|
||||
if (err) {
|
||||
new = err;
|
||||
goto out_close2;
|
||||
}
|
||||
}
|
||||
|
||||
unlink(file);
|
||||
data->pid = pid;
|
||||
*dev_out = NULL;
|
||||
|
||||
return new;
|
||||
|
||||
out_close2:
|
||||
close(new);
|
||||
out_kill:
|
||||
os_kill_process(pid, 1);
|
||||
out_close1:
|
||||
close(fd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void xterm_close(int fd, void *d)
|
||||
{
|
||||
struct xterm_chan *data = d;
|
||||
|
||||
if (data->pid != -1)
|
||||
os_kill_process(data->pid, 1);
|
||||
data->pid = -1;
|
||||
|
||||
if (data->helper_pid != -1)
|
||||
os_kill_process(data->helper_pid, 0);
|
||||
data->helper_pid = -1;
|
||||
|
||||
os_close_file(fd);
|
||||
}
|
||||
|
||||
const struct chan_ops xterm_ops = {
|
||||
.type = "xterm",
|
||||
.init = xterm_init,
|
||||
.open = xterm_open,
|
||||
.close = xterm_close,
|
||||
.read = generic_read,
|
||||
.write = generic_write,
|
||||
.console_write = generic_console_write,
|
||||
.window_size = generic_window_size,
|
||||
.free = generic_free,
|
||||
.winch = 1,
|
||||
};
|
12
arch/um/drivers/xterm.h
Normal file
12
arch/um/drivers/xterm.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#ifndef __XTERM_H__
|
||||
#define __XTERM_H__
|
||||
|
||||
extern int xterm_fd(int socket, int *pid_out);
|
||||
|
||||
#endif
|
||||
|
75
arch/um/drivers/xterm_kern.c
Normal file
75
arch/um/drivers/xterm_kern.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
||||
* Licensed under the GPL
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <asm/irq.h>
|
||||
#include <irq_kern.h>
|
||||
#include <os.h>
|
||||
|
||||
struct xterm_wait {
|
||||
struct completion ready;
|
||||
int fd;
|
||||
int pid;
|
||||
int new_fd;
|
||||
};
|
||||
|
||||
static irqreturn_t xterm_interrupt(int irq, void *data)
|
||||
{
|
||||
struct xterm_wait *xterm = data;
|
||||
int fd;
|
||||
|
||||
fd = os_rcv_fd(xterm->fd, &xterm->pid);
|
||||
if (fd == -EAGAIN)
|
||||
return IRQ_NONE;
|
||||
|
||||
xterm->new_fd = fd;
|
||||
complete(&xterm->ready);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int xterm_fd(int socket, int *pid_out)
|
||||
{
|
||||
struct xterm_wait *data;
|
||||
int err, ret;
|
||||
|
||||
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
printk(KERN_ERR "xterm_fd : failed to allocate xterm_wait\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* This is a locked semaphore... */
|
||||
*data = ((struct xterm_wait) { .fd = socket,
|
||||
.pid = -1,
|
||||
.new_fd = -1 });
|
||||
init_completion(&data->ready);
|
||||
|
||||
err = um_request_irq(XTERM_IRQ, socket, IRQ_READ, xterm_interrupt,
|
||||
IRQF_SHARED, "xterm", data);
|
||||
if (err) {
|
||||
printk(KERN_ERR "xterm_fd : failed to get IRQ for xterm, "
|
||||
"err = %d\n", err);
|
||||
ret = err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* ... so here we wait for an xterm interrupt.
|
||||
*
|
||||
* XXX Note, if the xterm doesn't work for some reason (eg. DISPLAY
|
||||
* isn't set) this will hang... */
|
||||
wait_for_completion(&data->ready);
|
||||
|
||||
um_free_irq(XTERM_IRQ, data);
|
||||
|
||||
ret = data->new_fd;
|
||||
*pid_out = data->pid;
|
||||
out:
|
||||
kfree(data);
|
||||
|
||||
return ret;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue