/* ----------------------------------------------------------------------------- * 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 separately 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 initially zero so no * need to reinitialized 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 */ #ifdef CONFIG_DEBUG_FS 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); } /*------------------------------------------------------------------------------ * Context: process */ static 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 */ static 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 */ static 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; } /*------------------------------------------------------------------------------ */ static 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 */