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 + -
显示快捷键?