mon_text.c
来自「omap3 linux 2.6 用nocc去除了冗余代码」· C语言 代码 · 共 744 行 · 第 1/2 页
C
744 行
/* * The USB Monitor, inspired by Dave Harding's USBMon. * * This is a text format reader. */#include <linux/kernel.h>#include <linux/list.h>#include <linux/usb.h>#include <linux/time.h>#include <linux/mutex.h>#include <linux/debugfs.h>#include <asm/uaccess.h>#include "usb_mon.h"/* * No, we do not want arbitrarily long data strings. * Use the binary interface if you want to capture bulk data! */#define DATA_MAX 32/* * Defined by USB 2.0 clause 9.3, table 9.2. */#define SETUP_MAX 8/* * This limit exists to prevent OOMs when the user process stops reading. * If usbmon were available to unprivileged processes, it might be open * to a local DoS. But we have to keep to root in order to prevent * password sniffing from HID devices. */#define EVENT_MAX (4*PAGE_SIZE / sizeof(struct mon_event_text))/* * Potentially unlimited number; we limit it for similar allocations. * The usbfs limits this to 128, but we're not quite as generous. */#define ISODESC_MAX 5#define PRINTF_DFL 250 /* with 5 ISOs segs */struct mon_iso_desc { int status; unsigned int offset; unsigned int length; /* Unsigned here, signed in URB. Historic. */};struct mon_event_text { struct list_head e_link; int type; /* submit, complete, etc. */ unsigned int pipe; /* Pipe */ unsigned long id; /* From pointer, most of the time */ unsigned int tstamp; int busnum; int length; /* Depends on type: xfer length or act length */ int status; int interval; int start_frame; int error_count; char setup_flag; char data_flag; int numdesc; /* Full number */ struct mon_iso_desc isodesc[ISODESC_MAX]; unsigned char setup[SETUP_MAX]; unsigned char data[DATA_MAX];};#define SLAB_NAME_SZ 30struct mon_reader_text { struct kmem_cache *e_slab; int nevents; struct list_head e_list; struct mon_reader r; /* In C, parent class can be placed anywhere */ wait_queue_head_t wait; int printf_size; char *printf_buf; struct mutex printf_lock; char slab_name[SLAB_NAME_SZ];};static struct dentry *mon_dir; /* Usually /sys/kernel/debug/usbmon */static void mon_text_ctor(void *, struct kmem_cache *, unsigned long);struct mon_text_ptr { int cnt, limit; char *pbuf;};static struct mon_event_text * mon_text_read_wait(struct mon_reader_text *rp, struct file *file);static void mon_text_read_head_t(struct mon_reader_text *rp, struct mon_text_ptr *p, const struct mon_event_text *ep);static void mon_text_read_head_u(struct mon_reader_text *rp, struct mon_text_ptr *p, const struct mon_event_text *ep);static void mon_text_read_statset(struct mon_reader_text *rp, struct mon_text_ptr *p, const struct mon_event_text *ep);static void mon_text_read_intstat(struct mon_reader_text *rp, struct mon_text_ptr *p, const struct mon_event_text *ep);static void mon_text_read_isostat(struct mon_reader_text *rp, struct mon_text_ptr *p, const struct mon_event_text *ep);static void mon_text_read_isodesc(struct mon_reader_text *rp, struct mon_text_ptr *p, const struct mon_event_text *ep);static void mon_text_read_data(struct mon_reader_text *rp, struct mon_text_ptr *p, const struct mon_event_text *ep);/* * mon_text_submit * mon_text_complete * * May be called from an interrupt. * * This is called with the whole mon_bus locked, so no additional lock. */static inline char mon_text_get_setup(struct mon_event_text *ep, struct urb *urb, char ev_type, struct mon_bus *mbus){ if (!usb_pipecontrol(urb->pipe) || ev_type != 'S') return '-'; if (urb->dev->bus->uses_dma && (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) { return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX); } if (urb->setup_packet == NULL) return 'Z'; /* '0' would be not as pretty. */ memcpy(ep->setup, urb->setup_packet, SETUP_MAX); return 0;}static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb, int len, char ev_type, struct mon_bus *mbus){ int pipe = urb->pipe; if (len <= 0) return 'L'; if (len >= DATA_MAX) len = DATA_MAX; if (usb_pipein(pipe)) { if (ev_type != 'C') return '<'; } else { if (ev_type != 'S') return '>'; } /* * The check to see if it's safe to poke at data has an enormous * number of corner cases, but it seems that the following is * more or less safe. * * We do not even try to look at transfer_buffer, because it can * contain non-NULL garbage in case the upper level promised to * set DMA for the HCD. */ if (urb->dev->bus->uses_dma && (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { return mon_dmapeek(ep->data, urb->transfer_dma, len); } if (urb->transfer_buffer == NULL) return 'Z'; /* '0' would be not as pretty. */ memcpy(ep->data, urb->transfer_buffer, len); return 0;}static inline unsigned int mon_get_timestamp(void){ struct timeval tval; unsigned int stamp; do_gettimeofday(&tval); stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s. */ stamp = stamp * 1000000 + tval.tv_usec; return stamp;}static void mon_text_event(struct mon_reader_text *rp, struct urb *urb, char ev_type){ struct mon_event_text *ep; unsigned int stamp; struct usb_iso_packet_descriptor *fp; struct mon_iso_desc *dp; int i, ndesc; stamp = mon_get_timestamp(); if (rp->nevents >= EVENT_MAX || (ep = kmem_cache_alloc(rp->e_slab, GFP_ATOMIC)) == NULL) { rp->r.m_bus->cnt_text_lost++; return; } ep->type = ev_type; ep->pipe = urb->pipe; ep->id = (unsigned long) urb; ep->busnum = urb->dev->bus->busnum; ep->tstamp = stamp; ep->length = (ev_type == 'S') ? urb->transfer_buffer_length : urb->actual_length; /* Collecting status makes debugging sense for submits, too */ ep->status = urb->status; if (usb_pipeint(urb->pipe)) { ep->interval = urb->interval; } else if (usb_pipeisoc(urb->pipe)) { ep->interval = urb->interval; ep->start_frame = urb->start_frame; ep->error_count = urb->error_count; } ep->numdesc = urb->number_of_packets; if (usb_pipeisoc(urb->pipe) && urb->number_of_packets > 0) { if ((ndesc = urb->number_of_packets) > ISODESC_MAX) ndesc = ISODESC_MAX; fp = urb->iso_frame_desc; dp = ep->isodesc; for (i = 0; i < ndesc; i++) { dp->status = fp->status; dp->offset = fp->offset; dp->length = (ev_type == 'S') ? fp->length : fp->actual_length; fp++; dp++; } } ep->setup_flag = mon_text_get_setup(ep, urb, ev_type, rp->r.m_bus); ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type, rp->r.m_bus); rp->nevents++; list_add_tail(&ep->e_link, &rp->e_list); wake_up(&rp->wait);}static void mon_text_submit(void *data, struct urb *urb){ struct mon_reader_text *rp = data; mon_text_event(rp, urb, 'S');}static void mon_text_complete(void *data, struct urb *urb){ struct mon_reader_text *rp = data; mon_text_event(rp, urb, 'C');}static void mon_text_error(void *data, struct urb *urb, int error){ struct mon_reader_text *rp = data; struct mon_event_text *ep; if (rp->nevents >= EVENT_MAX || (ep = kmem_cache_alloc(rp->e_slab, GFP_ATOMIC)) == NULL) { rp->r.m_bus->cnt_text_lost++; return; } ep->type = 'E'; ep->pipe = urb->pipe; ep->id = (unsigned long) urb; ep->busnum = 0; ep->tstamp = 0; ep->length = 0; ep->status = error; ep->setup_flag = '-'; ep->data_flag = 'E'; rp->nevents++; list_add_tail(&ep->e_link, &rp->e_list); wake_up(&rp->wait);}/* * Fetch next event from the circular buffer. */static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp, struct mon_bus *mbus){ struct list_head *p; unsigned long flags; spin_lock_irqsave(&mbus->lock, flags); if (list_empty(&rp->e_list)) { spin_unlock_irqrestore(&mbus->lock, flags); return NULL; } p = rp->e_list.next; list_del(p); --rp->nevents; spin_unlock_irqrestore(&mbus->lock, flags); return list_entry(p, struct mon_event_text, e_link);}/* */static int mon_text_open(struct inode *inode, struct file *file){ struct mon_bus *mbus; struct mon_reader_text *rp; int rc; mutex_lock(&mon_lock); mbus = inode->i_private; rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL); if (rp == NULL) { rc = -ENOMEM; goto err_alloc; } INIT_LIST_HEAD(&rp->e_list); init_waitqueue_head(&rp->wait); mutex_init(&rp->printf_lock); rp->printf_size = PRINTF_DFL; rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL); if (rp->printf_buf == NULL) { rc = -ENOMEM; goto err_alloc_pr; } rp->r.m_bus = mbus; rp->r.r_data = rp; rp->r.rnf_submit = mon_text_submit; rp->r.rnf_error = mon_text_error; rp->r.rnf_complete = mon_text_complete; snprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp); rp->e_slab = kmem_cache_create(rp->slab_name, sizeof(struct mon_event_text), sizeof(long), 0, mon_text_ctor, NULL); if (rp->e_slab == NULL) { rc = -ENOMEM; goto err_slab; } mon_reader_add(mbus, &rp->r); file->private_data = rp; mutex_unlock(&mon_lock); return 0;// err_busy:// kmem_cache_destroy(rp->e_slab);err_slab: kfree(rp->printf_buf);err_alloc_pr: kfree(rp);err_alloc: mutex_unlock(&mon_lock); return rc;}/* * For simplicity, we read one record in one system call and throw out * what does not fit. This means that the following does not work: * dd if=/dbg/usbmon/0t bs=10 * Also, we do not allow seeks and do not bother advancing the offset. */static ssize_t mon_text_read_t(struct file *file, char __user *buf,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?