summaryrefslogtreecommitdiffstats
path: root/target/linux/atheros-2.6/files/arch/mips/atheros/reset.c
blob: d62c5e1c4efe0a1ee7c49a8e07b2f4c07f4cc9af (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
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/kobject.h>
#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include "ar531x.h"
#include "ar5315/ar5315.h"

struct event_t {
	struct work_struct wq;
	int set;
	long int jiffies;
};

extern struct sock *uevent_sock;
extern u64 uevent_next_seqnum(void);
static int seen;

static inline void add_msg(struct sk_buff *skb, char *msg)
{
	char *scratch;
	scratch = skb_put(skb, strlen(msg) + 1);
	sprintf(scratch, msg);
}

static void hotplug_button(struct work_struct *wq)
{
	struct sk_buff *skb;
	struct event_t *event;
	size_t len;
	char *scratch, *s;
	char buf[128];
	
	event = container_of(wq, struct event_t, wq);
	if (!uevent_sock)
		goto done;

	/* allocate message with the maximum possible size */
	s = event->set ? "pressed" : "released";
	len = strlen(s) + 2;
	skb = alloc_skb(len + 2048, GFP_KERNEL);
	if (!skb)
		goto done;
	
	/* add header */
	scratch = skb_put(skb, len);
	sprintf(scratch, "%s@",s);

	/* copy keys to our continuous event payload buffer */
	add_msg(skb, "HOME=/");
	add_msg(skb, "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
	add_msg(skb, "SUBSYSTEM=button");
	add_msg(skb, "BUTTON=reset");
	add_msg(skb, (event->set ? "ACTION=pressed" : "ACTION=released"));
	sprintf(buf, "SEEN=%ld", (event->jiffies - seen)/HZ);
	add_msg(skb, buf);
	snprintf(buf, 128, "SEQNUM=%llu", uevent_next_seqnum());
	add_msg(skb, buf);

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

done:
	kfree(event);
}

static irqreturn_t button_handler(int irq, void *dev_id)
{
	static int pressed = 0;
	struct event_t *event;
	u32 gpio = ~0;

	event = (struct event_t *) kzalloc(sizeof(struct event_t), GFP_ATOMIC);
	if (!event)
		return IRQ_NONE;
	
	pressed = !pressed;

	DO_AR5315(gpio = sysRegRead(AR5315_GPIO_DI);)
	gpio &= 1 << (irq - AR531X_GPIO_IRQ_BASE);

	event->set = gpio;
	event->jiffies = jiffies;

	INIT_WORK(&event->wq, (void *)(void *)hotplug_button);
	schedule_work(&event->wq);

	seen = jiffies;

	return IRQ_HANDLED;
}

int __init ar531x_init_reset(void)
{
	struct ar531x_boarddata *bcfg;
	bcfg = (struct ar531x_boarddata *) board_config;

	seen = jiffies;
	request_irq(AR531X_GPIO_IRQ_BASE + bcfg->resetConfigGpio, &button_handler, IRQF_SAMPLE_RANDOM, "ar531x_reset", NULL);

	return 0;
}

module_init(ar531x_init_reset);