aboutsummaryrefslogtreecommitdiffstats
path: root/package/button-hotplug/src/button-hotplug.c
blob: 0259a3fbcd874dd664924e66b8886a2d84d8e3f5 (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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*
 *  Button Hotplug driver
 *
 *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
 *
 *  Based on the diag.c - GPIO interface driver for Broadcom boards
 *    Copyright (C) 2006 Mike Baker <mbm@openwrt.org>,
 *    Copyright (C) 2006-2007 Felix Fietkau <nbd@openwrt.org>
 *    Copyright (C) 2008 Andy Boyett <agb@openwrt.org>
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License version 2 as published
 *  by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/input.h>

#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <net/sock.h>

#define DRV_NAME	"button-hotplug"
#define DRV_VERSION	"0.4.0"
#define DRV_DESC	"Button Hotplug driver"

#define BH_SKB_SIZE	2048

#define PFX	DRV_NAME ": "

#undef BH_DEBUG

#ifdef BH_DEBUG
#define BH_DBG(fmt, args...) printk(KERN_DEBUG "%s: " fmt, DRV_NAME, ##args )
#else
#define BH_DBG(fmt, args...) do {} while (0)
#endif

#define BH_ERR(fmt, args...) printk(KERN_ERR "%s: " fmt, DRV_NAME, ##args )

#ifndef BIT_MASK
#define BIT_MASK(nr)            (1UL << ((nr) % BITS_PER_LONG))
#endif

struct bh_priv {
	unsigned long		*seen;
	struct input_handle	handle;
};

struct bh_event {
	const char		*name;
	char			*action;
	unsigned long		seen;

	struct sk_buff		*skb;
	struct work_struct	work;
};

struct bh_map {
	unsigned int	code;
	const char	*name;
};

extern struct sock *uevent_sock;
extern u64 uevent_next_seqnum(void);

#define BH_MAP(_code, _name)		\
	{				\
		.code = (_code),	\
		.name = (_name),	\
	}

static struct bh_map button_map[] = {
	BH_MAP(BTN_0,		"BTN_0"),
	BH_MAP(BTN_1,		"BTN_1"),
	BH_MAP(BTN_2,		"BTN_2"),
	BH_MAP(BTN_3,		"BTN_3"),
	BH_MAP(BTN_4,		"BTN_4"),
	BH_MAP(BTN_5,		"BTN_5"),
	BH_MAP(BTN_6,		"BTN_6"),
	BH_MAP(BTN_7,		"BTN_7"),
	BH_MAP(BTN_8,		"BTN_8"),
	BH_MAP(BTN_9,		"BTN_9"),
	BH_MAP(KEY_RESTART,	"reset"),
#ifdef KEY_WPS_BUTTON
	BH_MAP(KEY_WPS_BUTTON,	"wps"),
#endif /* KEY_WPS_BUTTON */
};

/* -------------------------------------------------------------------------*/

static int bh_event_add_var(struct bh_event *event, int argv,
		const char *format, ...)
{
	static char buf[128];
	char *s;
	va_list args;
	int len;

	if (argv)
		return 0;

	va_start(args, format);
	len = vsnprintf(buf, sizeof(buf), format, args);
	va_end(args);

	if (len >= sizeof(buf)) {
		BH_ERR("buffer size too small\n");
		WARN_ON(1);
		return -ENOMEM;
	}

	s = skb_put(event->skb, len + 1);
	strcpy(s, buf);

	BH_DBG("added variable '%s'\n", s);

	return 0;
}

static int button_hotplug_fill_event(struct bh_event *event)
{
	int ret;

	ret = bh_event_add_var(event, 0, "HOME=%s", "/");
	if (ret)
		return ret;

	ret = bh_event_add_var(event, 0, "PATH=%s",
					"/sbin:/bin:/usr/sbin:/usr/bin");
	if (ret)
		return ret;

	ret = bh_event_add_var(event, 0, "SUBSYSTEM=%s", "button");
	if (ret)
		return ret;

	ret = bh_event_add_var(event, 0, "ACTION=%s", event->action);
	if (ret)
		return ret;

	ret = bh_event_add_var(event, 0, "BUTTON=%s", event->name);
	if (ret)
		return ret;

	ret = bh_event_add_var(event, 0, "SEEN=%ld", event->seen);
	if (ret)
		return ret;

	ret = bh_event_add_var(event, 0, "SEQNUM=%llu", uevent_next_seqnum());

	return ret;
}

static void button_hotplug_work(struct work_struct *work)
{
	struct bh_event *event = container_of(work, struct bh_event, work);
	int ret = 0;

	if (!uevent_sock)
		goto out_free_event;

	event->skb = alloc_skb(BH_SKB_SIZE, GFP_KERNEL);
	if (!event->skb)
		goto out_free_event;

	ret = bh_event_add_var(event, 0, "%s@", event->action);
	if (ret)
		goto out_free_skb;

	ret = button_hotplug_fill_event(event);
	if (ret)
		goto out_free_skb;

	NETLINK_CB(event->skb).dst_group = 1;
	netlink_broadcast(uevent_sock, event->skb, 0, 1, GFP_KERNEL);

 out_free_skb:
	if (ret) {
		BH_ERR("work error %d\n", ret);
		kfree_skb(event->skb);
	}
 out_free_event:
	kfree(event);
}

static int button_hotplug_create_event(const char *name, unsigned long seen,
		int pressed)
{
	struct bh_event *event;

	BH_DBG("create event, name=%s, seen=%lu, pressed=%d\n",
		name, seen, pressed);

	event = kzalloc(sizeof(*event), GFP_KERNEL);
	if (!event)
		return -ENOMEM;

	event->name = name;
	event->seen = seen;
	event->action = pressed ? "pressed" : "released";

	INIT_WORK(&event->work, (void *)(void *)button_hotplug_work);
	schedule_work(&event->work);

	return 0;
}

/* -------------------------------------------------------------------------*/

#ifdef	CONFIG_HOTPLUG
static int button_get_index(unsigned int code)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(button_map); i++)
		if (button_map[i].code == code)
			return i;

	return -1;
}
static void button_hotplug_event(struct input_handle *handle,
			   unsigned int type, unsigned int code, int value)
{
	struct bh_priv *priv = handle->private;
	unsigned long seen = jiffies;
	int btn;

	BH_DBG("event type=%u, code=%u, value=%d\n", type, code, value);

	if (type != EV_KEY)
		return;

	btn = button_get_index(code);
	if (btn < 0)
		return;

	button_hotplug_create_event(button_map[btn].name,
			(seen - priv->seen[btn]) / HZ, value);
	priv->seen[btn] = seen;
}
#else
static void button_hotplug_event(struct input_handle *handle,
			   unsigned int type, unsigned int code, int value)
{
}
#endif	/* CONFIG_HOTPLUG */

static int button_hotplug_connect(struct input_handler *handler,
		struct input_dev *dev, const struct input_device_id *id)
{
	struct bh_priv *priv;
	int ret;
	int i;

	for (i = 0; i < ARRAY_SIZE(button_map); i++)
		if (test_bit(button_map[i].code, dev->keybit))
			break;

	if (i == ARRAY_SIZE(button_map))
		return -ENODEV;

	priv = kzalloc(sizeof(*priv) +
		       (sizeof(unsigned long) * ARRAY_SIZE(button_map)),
		       GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->seen = (unsigned long *) &priv[1];
	priv->handle.private = priv;
	priv->handle.dev = dev;
	priv->handle.handler = handler;
	priv->handle.name = DRV_NAME;

	ret = input_register_handle(&priv->handle);
	if (ret)
		goto err_free_priv;

	ret = input_open_device(&priv->handle);
	if (ret)
		goto err_unregister_handle;

	BH_DBG("connected to %s\n", dev->name);

	return 0;

 err_unregister_handle:
	input_unregister_handle(&priv->handle);

 err_free_priv:
	kfree(priv);
	return ret;
}

static void button_hotplug_disconnect(struct input_handle *handle)
{
	struct bh_priv *priv = handle->private;

	input_close_device(handle);
	input_unregister_handle(handle);

	kfree(priv);
}

static const struct input_device_id button_hotplug_ids[] = {
	{
                .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
                .evbit = { BIT_MASK(EV_KEY) },
        },
	{
		/* Terminating entry */
	},
};

MODULE_DEVICE_TABLE(input, button_hotplug_ids);

static struct input_handler button_hotplug_handler = {
	.event =	button_hotplug_event,
	.connect =	button_hotplug_connect,
	.disconnect =	button_hotplug_disconnect,
	.name =		DRV_NAME,
	.id_table =	button_hotplug_ids,
};

/* -------------------------------------------------------------------------*/

static int __init button_hotplug_init(void)
{
	int ret;

	printk(KERN_INFO DRV_DESC " version " DRV_VERSION "\n");
	ret = input_register_handler(&button_hotplug_handler);
	if (ret)
		BH_ERR("unable to register input handler\n");

	return ret;
}
module_init(button_hotplug_init);

static void __exit button_hotplug_exit(void)
{
	input_unregister_handler(&button_hotplug_handler);
}
module_exit(button_hotplug_exit);

MODULE_DESCRIPTION(DRV_DESC);
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_LICENSE("GPL v2");
= 80, /* x87 floating-point exception-pending */ VMEXIT_EXCEPTION_AC = 81, /* alignment-check */ VMEXIT_EXCEPTION_MC = 82, /* machine-check */ VMEXIT_EXCEPTION_XF = 83, /* simd floating-point */ /* exceptions 20-31 (exitcodes 84-95) are reserved */ /* ...and the rest of the #VMEXITs */ VMEXIT_INTR = 96, VMEXIT_NMI = 97, VMEXIT_SMI = 98, VMEXIT_INIT = 99, VMEXIT_VINTR = 100, VMEXIT_CR0_SEL_WRITE = 101, VMEXIT_IDTR_READ = 102, VMEXIT_GDTR_READ = 103, VMEXIT_LDTR_READ = 104, VMEXIT_TR_READ = 105, VMEXIT_IDTR_WRITE = 106, VMEXIT_GDTR_WRITE = 107, VMEXIT_LDTR_WRITE = 108, VMEXIT_TR_WRITE = 109, VMEXIT_RDTSC = 110, VMEXIT_RDPMC = 111, VMEXIT_PUSHF = 112, VMEXIT_POPF = 113, VMEXIT_CPUID = 114, VMEXIT_RSM = 115, VMEXIT_IRET = 116, VMEXIT_SWINT = 117, VMEXIT_INVD = 118, VMEXIT_PAUSE = 119, VMEXIT_HLT = 120, VMEXIT_INVLPG = 121, VMEXIT_INVLPGA = 122, VMEXIT_IOIO = 123, VMEXIT_MSR = 124, VMEXIT_TASK_SWITCH = 125, VMEXIT_FERR_FREEZE = 126, VMEXIT_SHUTDOWN = 127, VMEXIT_VMRUN = 128, VMEXIT_VMMCALL = 129, VMEXIT_VMLOAD = 130, VMEXIT_VMSAVE = 131, VMEXIT_STGI = 132, VMEXIT_CLGI = 133, VMEXIT_SKINIT = 134, VMEXIT_RDTSCP = 135, VMEXIT_ICEBP = 136, VMEXIT_WBINVD = 137, VMEXIT_MONITOR = 138, VMEXIT_MWAIT = 139, VMEXIT_MWAIT_CONDITIONAL= 140, VMEXIT_NPF = 1024, /* nested paging fault */ VMEXIT_INVALID = -1 }; /* Definition of segment state is borrowed by the generic HVM code. */ typedef struct segment_register svm_segment_register_t; typedef union { u64 bytes; struct { u64 vector: 8; u64 type: 3; u64 ev: 1; u64 resvd1: 19; u64 v: 1; u64 errorcode:32; } fields; } __attribute__ ((packed)) eventinj_t; typedef union { u64 bytes; struct { u64 tpr: 8; u64 irq: 1; u64 rsvd0: 7; u64 prio: 4; u64 ign_tpr: 1; u64 rsvd1: 3; u64 intr_masking: 1; u64 rsvd2: 7; u64 vector: 8; u64 rsvd3: 24; } fields; } __attribute__ ((packed)) vintr_t; typedef union { u64 bytes; struct { u64 type: 1; u64 rsv0: 1; u64 str: 1; u64 rep: 1; u64 sz8: 1; u64 sz16: 1; u64 sz32: 1; u64 rsv1: 9; u64 port: 16; } fields; } __attribute__ ((packed)) ioio_info_t; typedef union { u64 bytes; struct { u64 enable:1; } fields; } __attribute__ ((packed)) lbrctrl_t; struct vmcb_struct { u32 cr_intercepts; /* offset 0x00 */ u32 dr_intercepts; /* offset 0x04 */ u32 exception_intercepts; /* offset 0x08 */ u32 general1_intercepts; /* offset 0x0C */ u32 general2_intercepts; /* offset 0x10 */ u32 res01; /* offset 0x14 */ u64 res02; /* offset 0x18 */ u64 res03; /* offset 0x20 */ u64 res04; /* offset 0x28 */ u64 res05; /* offset 0x30 */ u64 res06; /* offset 0x38 */ u64 iopm_base_pa; /* offset 0x40 */ u64 msrpm_base_pa; /* offset 0x48 */ u64 tsc_offset; /* offset 0x50 */ u32 guest_asid; /* offset 0x58 */ u8 tlb_control; /* offset 0x5C */ u8 res07[3]; vintr_t vintr; /* offset 0x60 */ u64 interrupt_shadow; /* offset 0x68 */ u64 exitcode; /* offset 0x70 */ u64 exitinfo1; /* offset 0x78 */ u64 exitinfo2; /* offset 0x80 */ eventinj_t exitintinfo; /* offset 0x88 */ u64 np_enable; /* offset 0x90 */ u64 res08[2]; eventinj_t eventinj; /* offset 0xA8 */ u64 h_cr3; /* offset 0xB0 */ lbrctrl_t lbr_control; /* offset 0xB8 */ u64 res09; /* offset 0xC0 */ u64 nextrip; /* offset 0xC8 */ u64 res10a[102]; /* offset 0xD0 pad to save area */ svm_segment_register_t es; /* offset 1024 */ svm_segment_register_t cs; svm_segment_register_t ss; svm_segment_register_t ds; svm_segment_register_t fs; svm_segment_register_t gs; svm_segment_register_t gdtr; svm_segment_register_t ldtr; svm_segment_register_t idtr; svm_segment_register_t tr; u64 res10[5]; u8 res11[3]; u8 cpl; u32 res12; u64 efer; /* offset 1024 + 0xD0 */ u64 res13[14]; u64 cr4; /* loffset 1024 + 0x148 */ u64 cr3; u64 cr0; u64 dr7; u64 dr6; u64 rflags; u64 rip; u64 res14[11]; u64 rsp; u64 res15[3]; u64 rax; u64 star; u64 lstar; u64 cstar; u64 sfmask; u64 kerngsbase; u64 sysenter_cs; u64 sysenter_esp; u64 sysenter_eip; u64 cr2; u64 pdpe0; u64 pdpe1; u64 pdpe2; u64 pdpe3; u64 g_pat; u64 debugctlmsr; u64 lastbranchfromip; u64 lastbranchtoip; u64 lastintfromip; u64 lastinttoip; u64 res16[301]; } __attribute__ ((packed)); struct svm_domain { #if CONFIG_PAGING_LEVELS == 3 bool_t npt_4gb_warning; #endif }; struct arch_svm_struct { struct vmcb_struct *vmcb; u64 vmcb_pa; u64 asid_generation; /* ASID tracking, moved here for cache locality. */ unsigned long *msrpm; int launch_core; bool_t vmcb_in_sync; /* VMCB sync'ed with VMSAVE? */ /* Upper four bytes are undefined in the VMCB, therefore we can't * use the fields in the VMCB. Write a 64bit value and then read a 64bit * value is fine unless there's a VMRUN/VMEXIT in between which clears * the upper four bytes. */ uint64_t guest_sysenter_cs; uint64_t guest_sysenter_esp; uint64_t guest_sysenter_eip; }; struct vmcb_struct *alloc_vmcb(void); struct host_save_area *alloc_host_save_area(void); void free_vmcb(struct vmcb_struct *vmcb); int svm_create_vmcb(struct vcpu *v); void svm_destroy_vmcb(struct vcpu *v); void setup_vmcb_dump(void); void svm_disable_intercept_for_msr(struct vcpu *v, u32 msr); #endif /* ASM_X86_HVM_SVM_VMCS_H__ */ /* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */