Loading...
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 | /* -----------------------------------------------------------------------------
* Copyright (c) 2011 Ozmo Inc
* Released under the GNU General Public License Version 2 (GPLv2).
* -----------------------------------------------------------------------------
*/
#include "ozconfig.h"
#ifdef WANT_EVENT_TRACE
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/jiffies.h>
#include <linux/uaccess.h>
#include "oztrace.h"
#include "ozevent.h"
#include "ozappif.h"
/*------------------------------------------------------------------------------
* Although the event mask is logically part of the oz_evtdev structure, it is
* needed outside of this file so define it seperately to avoid the need to
* export definition of struct oz_evtdev.
*/
u32 g_evt_mask;
/*------------------------------------------------------------------------------
*/
#define OZ_MAX_EVTS 2048 /* Must be power of 2 */
struct oz_evtdev {
struct dentry *root_dir;
int evt_in;
int evt_out;
int missed_events;
int present;
atomic_t users;
spinlock_t lock;
struct oz_event evts[OZ_MAX_EVTS];
};
static struct oz_evtdev g_evtdev;
/*------------------------------------------------------------------------------
* Context: process
*/
void oz_event_init(void)
{
/* Because g_evtdev is static external all fields initally zero so no
* need to reinitialised those.
*/
oz_trace("Event tracing initialized\n");
spin_lock_init(&g_evtdev.lock);
atomic_set(&g_evtdev.users, 0);
}
/*------------------------------------------------------------------------------
* Context: process
*/
void oz_event_term(void)
{
oz_trace("Event tracing terminated\n");
}
/*------------------------------------------------------------------------------
* Context: any
*/
void oz_event_log2(u8 evt, u8 ctx1, u16 ctx2, void *ctx3, unsigned ctx4)
{
unsigned long irqstate;
int ix;
spin_lock_irqsave(&g_evtdev.lock, irqstate);
ix = (g_evtdev.evt_in + 1) & (OZ_MAX_EVTS - 1);
if (ix != g_evtdev.evt_out) {
struct oz_event *e = &g_evtdev.evts[g_evtdev.evt_in];
e->jiffies = jiffies;
e->evt = evt;
e->ctx1 = ctx1;
e->ctx2 = ctx2;
e->ctx3 = (__u32)(unsigned long)ctx3;
e->ctx4 = ctx4;
g_evtdev.evt_in = ix;
} else {
g_evtdev.missed_events++;
}
spin_unlock_irqrestore(&g_evtdev.lock, irqstate);
}
/*------------------------------------------------------------------------------
* Context: process
*/
static void oz_events_clear(struct oz_evtdev *dev)
{
unsigned long irqstate;
oz_trace("Clearing events\n");
spin_lock_irqsave(&dev->lock, irqstate);
dev->evt_in = dev->evt_out = 0;
dev->missed_events = 0;
spin_unlock_irqrestore(&dev->lock, irqstate);
}
#ifdef CONFIG_DEBUG_FS
/*------------------------------------------------------------------------------
* Context: process
*/
int oz_events_open(struct inode *inode, struct file *filp)
{
oz_trace("oz_evt_open()\n");
oz_trace("Open flags: 0x%x\n", filp->f_flags);
if (atomic_add_return(1, &g_evtdev.users) == 1) {
oz_events_clear(&g_evtdev);
return nonseekable_open(inode, filp);
} else {
atomic_dec(&g_evtdev.users);
return -EBUSY;
}
}
/*------------------------------------------------------------------------------
* Context: process
*/
int oz_events_release(struct inode *inode, struct file *filp)
{
oz_events_clear(&g_evtdev);
atomic_dec(&g_evtdev.users);
g_evt_mask = 0;
oz_trace("oz_evt_release()\n");
return 0;
}
/*------------------------------------------------------------------------------
* Context: process
*/
ssize_t oz_events_read(struct file *filp, char __user *buf, size_t count,
loff_t *fpos)
{
struct oz_evtdev *dev = &g_evtdev;
int rc = 0;
int nb_evts = count / sizeof(struct oz_event);
int n;
int sz;
n = dev->evt_in - dev->evt_out;
if (n < 0)
n += OZ_MAX_EVTS;
if (nb_evts > n)
nb_evts = n;
if (nb_evts == 0)
goto out;
n = OZ_MAX_EVTS - dev->evt_out;
if (n > nb_evts)
n = nb_evts;
sz = n * sizeof(struct oz_event);
if (copy_to_user(buf, &dev->evts[dev->evt_out], sz)) {
rc = -EFAULT;
goto out;
}
if (n == nb_evts)
goto out2;
n = nb_evts - n;
if (copy_to_user(buf + sz, dev->evts, n * sizeof(struct oz_event))) {
rc = -EFAULT;
goto out;
}
out2:
dev->evt_out = (dev->evt_out + nb_evts) & (OZ_MAX_EVTS - 1);
rc = nb_evts * sizeof(struct oz_event);
out:
return rc;
}
/*------------------------------------------------------------------------------
*/
const struct file_operations oz_events_fops = {
.owner = THIS_MODULE,
.open = oz_events_open,
.release = oz_events_release,
.read = oz_events_read,
};
/*------------------------------------------------------------------------------
* Context: process
*/
void oz_debugfs_init(void)
{
struct dentry *parent;
parent = debugfs_create_dir("ozwpan", NULL);
if (parent == NULL) {
oz_trace("Failed to create debugfs directory ozmo\n");
return;
} else {
g_evtdev.root_dir = parent;
if (debugfs_create_file("events", S_IRUSR, parent, NULL,
&oz_events_fops) == NULL)
oz_trace("Failed to create file ozmo/events\n");
if (debugfs_create_x32("event_mask", S_IRUSR | S_IWUSR, parent,
&g_evt_mask) == NULL)
oz_trace("Failed to create file ozmo/event_mask\n");
}
}
/*------------------------------------------------------------------------------
* Context: process
*/
void oz_debugfs_remove(void)
{
debugfs_remove_recursive(g_evtdev.root_dir);
}
#endif /* CONFIG_DEBUG_FS */
#endif /* WANT_EVENT_TRACE */
|