Commit | Line | Data |
---|---|---|
56ff32fe CK |
1 | /* ----------------------------------------------------------------------------- |
2 | * Copyright (c) 2011 Ozmo Inc | |
3 | * Released under the GNU General Public License Version 2 (GPLv2). | |
4 | * ----------------------------------------------------------------------------- | |
5 | */ | |
6 | #include "ozconfig.h" | |
7 | #ifdef WANT_EVENT_TRACE | |
68a75f3f RG |
8 | #include <linux/module.h> |
9 | #include <linux/debugfs.h> | |
56ff32fe CK |
10 | #include <linux/jiffies.h> |
11 | #include <linux/uaccess.h> | |
12 | #include "oztrace.h" | |
13 | #include "ozevent.h" | |
68a75f3f | 14 | #include "ozappif.h" |
56ff32fe | 15 | /*------------------------------------------------------------------------------ |
68a75f3f | 16 | * Although the event mask is logically part of the oz_evtdev structure, it is |
ba07275f | 17 | * needed outside of this file so define it separately to avoid the need to |
68a75f3f | 18 | * export definition of struct oz_evtdev. |
56ff32fe | 19 | */ |
68a75f3f | 20 | u32 g_evt_mask; |
56ff32fe CK |
21 | /*------------------------------------------------------------------------------ |
22 | */ | |
23 | #define OZ_MAX_EVTS 2048 /* Must be power of 2 */ | |
68a75f3f RG |
24 | struct oz_evtdev { |
25 | struct dentry *root_dir; | |
26 | int evt_in; | |
27 | int evt_out; | |
28 | int missed_events; | |
29 | int present; | |
30 | atomic_t users; | |
31 | spinlock_t lock; | |
32 | struct oz_event evts[OZ_MAX_EVTS]; | |
33 | }; | |
34 | ||
35 | static struct oz_evtdev g_evtdev; | |
36 | ||
56ff32fe CK |
37 | /*------------------------------------------------------------------------------ |
38 | * Context: process | |
39 | */ | |
40 | void oz_event_init(void) | |
41 | { | |
ba07275f MI |
42 | /* Because g_evtdev is static external all fields initially zero so no |
43 | * need to reinitialized those. | |
68a75f3f | 44 | */ |
56ff32fe | 45 | oz_trace("Event tracing initialized\n"); |
68a75f3f RG |
46 | spin_lock_init(&g_evtdev.lock); |
47 | atomic_set(&g_evtdev.users, 0); | |
56ff32fe CK |
48 | } |
49 | /*------------------------------------------------------------------------------ | |
50 | * Context: process | |
51 | */ | |
52 | void oz_event_term(void) | |
53 | { | |
54 | oz_trace("Event tracing terminated\n"); | |
55 | } | |
56 | /*------------------------------------------------------------------------------ | |
57 | * Context: any | |
58 | */ | |
59 | void oz_event_log2(u8 evt, u8 ctx1, u16 ctx2, void *ctx3, unsigned ctx4) | |
60 | { | |
61 | unsigned long irqstate; | |
62 | int ix; | |
68a75f3f RG |
63 | spin_lock_irqsave(&g_evtdev.lock, irqstate); |
64 | ix = (g_evtdev.evt_in + 1) & (OZ_MAX_EVTS - 1); | |
65 | if (ix != g_evtdev.evt_out) { | |
66 | struct oz_event *e = &g_evtdev.evts[g_evtdev.evt_in]; | |
56ff32fe CK |
67 | e->jiffies = jiffies; |
68 | e->evt = evt; | |
69 | e->ctx1 = ctx1; | |
70 | e->ctx2 = ctx2; | |
68a75f3f | 71 | e->ctx3 = (__u32)(unsigned long)ctx3; |
56ff32fe | 72 | e->ctx4 = ctx4; |
68a75f3f | 73 | g_evtdev.evt_in = ix; |
56ff32fe | 74 | } else { |
68a75f3f | 75 | g_evtdev.missed_events++; |
56ff32fe | 76 | } |
68a75f3f | 77 | spin_unlock_irqrestore(&g_evtdev.lock, irqstate); |
56ff32fe CK |
78 | } |
79 | /*------------------------------------------------------------------------------ | |
80 | * Context: process | |
81 | */ | |
68a75f3f | 82 | static void oz_events_clear(struct oz_evtdev *dev) |
56ff32fe | 83 | { |
68a75f3f RG |
84 | unsigned long irqstate; |
85 | oz_trace("Clearing events\n"); | |
86 | spin_lock_irqsave(&dev->lock, irqstate); | |
87 | dev->evt_in = dev->evt_out = 0; | |
88 | dev->missed_events = 0; | |
89 | spin_unlock_irqrestore(&dev->lock, irqstate); | |
90 | } | |
91 | #ifdef CONFIG_DEBUG_FS | |
92 | /*------------------------------------------------------------------------------ | |
93 | * Context: process | |
94 | */ | |
95 | int oz_events_open(struct inode *inode, struct file *filp) | |
96 | { | |
97 | oz_trace("oz_evt_open()\n"); | |
98 | oz_trace("Open flags: 0x%x\n", filp->f_flags); | |
99 | if (atomic_add_return(1, &g_evtdev.users) == 1) { | |
100 | oz_events_clear(&g_evtdev); | |
101 | return nonseekable_open(inode, filp); | |
102 | } else { | |
103 | atomic_dec(&g_evtdev.users); | |
104 | return -EBUSY; | |
56ff32fe | 105 | } |
68a75f3f RG |
106 | } |
107 | /*------------------------------------------------------------------------------ | |
108 | * Context: process | |
109 | */ | |
110 | int oz_events_release(struct inode *inode, struct file *filp) | |
111 | { | |
112 | oz_events_clear(&g_evtdev); | |
113 | atomic_dec(&g_evtdev.users); | |
114 | g_evt_mask = 0; | |
115 | oz_trace("oz_evt_release()\n"); | |
56ff32fe CK |
116 | return 0; |
117 | } | |
118 | /*------------------------------------------------------------------------------ | |
119 | * Context: process | |
120 | */ | |
68a75f3f RG |
121 | ssize_t oz_events_read(struct file *filp, char __user *buf, size_t count, |
122 | loff_t *fpos) | |
56ff32fe | 123 | { |
68a75f3f RG |
124 | struct oz_evtdev *dev = &g_evtdev; |
125 | int rc = 0; | |
126 | int nb_evts = count / sizeof(struct oz_event); | |
127 | int n; | |
128 | int sz; | |
129 | ||
130 | n = dev->evt_in - dev->evt_out; | |
131 | if (n < 0) | |
132 | n += OZ_MAX_EVTS; | |
133 | if (nb_evts > n) | |
134 | nb_evts = n; | |
135 | if (nb_evts == 0) | |
136 | goto out; | |
137 | n = OZ_MAX_EVTS - dev->evt_out; | |
138 | if (n > nb_evts) | |
139 | n = nb_evts; | |
140 | sz = n * sizeof(struct oz_event); | |
141 | if (copy_to_user(buf, &dev->evts[dev->evt_out], sz)) { | |
142 | rc = -EFAULT; | |
143 | goto out; | |
144 | } | |
145 | if (n == nb_evts) | |
146 | goto out2; | |
147 | n = nb_evts - n; | |
148 | if (copy_to_user(buf + sz, dev->evts, n * sizeof(struct oz_event))) { | |
149 | rc = -EFAULT; | |
150 | goto out; | |
151 | } | |
152 | out2: | |
153 | dev->evt_out = (dev->evt_out + nb_evts) & (OZ_MAX_EVTS - 1); | |
154 | rc = nb_evts * sizeof(struct oz_event); | |
155 | out: | |
156 | return rc; | |
56ff32fe | 157 | } |
68a75f3f RG |
158 | /*------------------------------------------------------------------------------ |
159 | */ | |
160 | const struct file_operations oz_events_fops = { | |
161 | .owner = THIS_MODULE, | |
162 | .open = oz_events_open, | |
163 | .release = oz_events_release, | |
164 | .read = oz_events_read, | |
165 | }; | |
166 | /*------------------------------------------------------------------------------ | |
167 | * Context: process | |
168 | */ | |
169 | void oz_debugfs_init(void) | |
170 | { | |
171 | struct dentry *parent; | |
56ff32fe | 172 | |
68a75f3f RG |
173 | parent = debugfs_create_dir("ozwpan", NULL); |
174 | if (parent == NULL) { | |
175 | oz_trace("Failed to create debugfs directory ozmo\n"); | |
176 | return; | |
177 | } else { | |
178 | g_evtdev.root_dir = parent; | |
179 | if (debugfs_create_file("events", S_IRUSR, parent, NULL, | |
180 | &oz_events_fops) == NULL) | |
181 | oz_trace("Failed to create file ozmo/events\n"); | |
182 | if (debugfs_create_x32("event_mask", S_IRUSR | S_IWUSR, parent, | |
183 | &g_evt_mask) == NULL) | |
184 | oz_trace("Failed to create file ozmo/event_mask\n"); | |
185 | } | |
186 | } | |
187 | /*------------------------------------------------------------------------------ | |
188 | * Context: process | |
189 | */ | |
190 | void oz_debugfs_remove(void) | |
191 | { | |
192 | debugfs_remove_recursive(g_evtdev.root_dir); | |
193 | } | |
194 | #endif /* CONFIG_DEBUG_FS */ | |
195 | #endif /* WANT_EVENT_TRACE */ |