aboutsummaryrefslogtreecommitdiffstats
path: root/linux-2.6-xen-sparse/drivers/xen/console/xencons_ring.c
blob: ccbcf5e7db7b0ee6079f508cab150791035c0891 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <linux/version.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/major.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>

#include <asm/hypervisor.h>
#include <asm-xen/evtchn.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/err.h>
#include "xencons_ring.h"
#include <asm-xen/xen-public/io/console.h>

static int xencons_irq;
static xencons_receiver_func *xencons_receiver;

static inline struct xencons_interface *xencons_interface(void)
{
	return mfn_to_virt(xen_start_info->console_mfn);
}

int xencons_ring_send(const char *data, unsigned len)
{
	int sent = 0;
	struct xencons_interface *intf = xencons_interface();
	XENCONS_RING_IDX cons, prod;

	cons = intf->out_cons;
	prod = intf->out_prod;
	mb();
	BUG_ON((prod - cons) > sizeof(intf->out));

	while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
		intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];

	wmb();
	intf->out_prod = prod;

	/* Use evtchn: this is called early, before irq is set up. */
	notify_remote_via_evtchn(xen_start_info->console_evtchn);

	return sent;
}	

static irqreturn_t handle_input(int irq, void *unused, struct pt_regs *regs)
{
	struct xencons_interface *intf = xencons_interface();
	XENCONS_RING_IDX cons, prod;

	cons = intf->in_cons;
	prod = intf->in_prod;
	mb();
	BUG_ON((prod - cons) > sizeof(intf->in));

	while (cons != prod) {
		if (xencons_receiver != NULL)
			xencons_receiver(
				intf->in + MASK_XENCONS_IDX(cons++, intf->in),
				1, regs);
	}

	wmb();
	intf->in_cons = cons;

	return IRQ_HANDLED;
}

void xencons_ring_register_receiver(xencons_receiver_func *f)
{
	xencons_receiver = f;
}

int xencons_ring_init(void)
{
	int err;

	if (xencons_irq)
		unbind_from_irqhandler(xencons_irq, NULL);
	xencons_irq = 0;

	if (!xen_start_info->console_evtchn)
		return 0;

	err = bind_evtchn_to_irqhandler(
		xen_start_info->console_evtchn,
		handle_input, 0, "xencons", NULL);
	if (err <= 0) {
		xprintk("XEN console request irq failed %i\n", err);
		return err;
	}

	xencons_irq = err;

	return 0;
}

void xencons_resume(void)
{
	(void)xencons_ring_init();
}

/*
 * Local variables:
 *  c-file-style: "linux"
 *  indent-tabs-mode: t
 *  c-indent-level: 8
 *  c-basic-offset: 8
 *  tab-width: 8
 * End:
 */