diff options
Diffstat (limited to 'drivers/usb/mon')
| -rw-r--r-- | drivers/usb/mon/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/usb/mon/Makefile | 7 | ||||
| -rw-r--r-- | drivers/usb/mon/mon_bin.c | 455 | ||||
| -rw-r--r-- | drivers/usb/mon/mon_dma.c | 95 | ||||
| -rw-r--r-- | drivers/usb/mon/mon_main.c | 176 | ||||
| -rw-r--r-- | drivers/usb/mon/mon_stat.c | 21 | ||||
| -rw-r--r-- | drivers/usb/mon/mon_text.c | 423 | ||||
| -rw-r--r-- | drivers/usb/mon/usb_mon.h | 25 |
8 files changed, 786 insertions, 426 deletions
diff --git a/drivers/usb/mon/Kconfig b/drivers/usb/mon/Kconfig index deb9ddffa40..5c6ffa2a612 100644 --- a/drivers/usb/mon/Kconfig +++ b/drivers/usb/mon/Kconfig @@ -3,14 +3,10 @@ # config USB_MON - bool "USB Monitor" - depends on USB!=n - default y + tristate "USB Monitor" help - If you say Y here, a component which captures the USB traffic + If you select this option, a component which captures the USB traffic between peripheral-specific drivers and HC drivers will be built. For more information, see <file:Documentation/usb/usbmon.txt>. - This is somewhat experimental at this time, but it should be safe. - - If unsure, say Y. + If unsure, say Y, if allowed, otherwise M. diff --git a/drivers/usb/mon/Makefile b/drivers/usb/mon/Makefile index 90c59535778..8ed24ab0869 100644 --- a/drivers/usb/mon/Makefile +++ b/drivers/usb/mon/Makefile @@ -1,8 +1,7 @@ # -# Makefile for USB Core files and filesystem +# Makefile for USB monitor # -usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_bin.o mon_dma.o +usbmon-y := mon_main.o mon_stat.o mon_text.o mon_bin.o -# This does not use CONFIG_USB_MON because we want this to use a tristate. -obj-$(CONFIG_USB) += usbmon.o +obj-$(CONFIG_USB_MON) += usbmon.o diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index b2bedd974ac..9a62e89d6dc 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -4,17 +4,20 @@ * This is a binary format reader. * * Copyright (C) 2006 Paolo Abeni (paolo.abeni@email.it) - * Copyright (C) 2006 Pete Zaitcev (zaitcev@redhat.com) + * Copyright (C) 2006,2007 Pete Zaitcev (zaitcev@redhat.com) */ #include <linux/kernel.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/cdev.h> +#include <linux/export.h> #include <linux/usb.h> #include <linux/poll.h> #include <linux/compat.h> #include <linux/mm.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> #include <asm/uaccess.h> @@ -36,9 +39,13 @@ #define MON_IOCX_GET _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get) #define MON_IOCX_MFETCH _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch) #define MON_IOCH_MFLUSH _IO(MON_IOC_MAGIC, 8) +/* #9 was MON_IOCT_SETAPI */ +#define MON_IOCX_GETX _IOW(MON_IOC_MAGIC, 10, struct mon_bin_get) + #ifdef CONFIG_COMPAT #define MON_IOCX_GET32 _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get32) #define MON_IOCX_MFETCH32 _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch32) +#define MON_IOCX_GETX32 _IOW(MON_IOC_MAGIC, 10, struct mon_bin_get32) #endif /* @@ -90,7 +97,29 @@ struct mon_bin_hdr { int status; unsigned int len_urb; /* Length of data (submitted or actual) */ unsigned int len_cap; /* Delivered length */ - unsigned char setup[SETUP_LEN]; /* Only for Control S-type */ + union { + unsigned char setup[SETUP_LEN]; /* Only for Control S-type */ + struct iso_rec { + int error_count; + int numdesc; + } iso; + } s; + int interval; + int start_frame; + unsigned int xfer_flags; + unsigned int ndesc; /* Actual number of ISO descriptors */ +}; + +/* + * ISO vector, packed into the head of data stream. + * This has to take 16 bytes to make sure that the end of buffer + * wrap is not happening in the middle of a descriptor. + */ +struct mon_bin_isodesc { + int iso_status; + unsigned int iso_off; + unsigned int iso_len; + u32 _pad; }; /* per file statistic */ @@ -100,7 +129,7 @@ struct mon_bin_stats { }; struct mon_bin_get { - struct mon_bin_hdr __user *hdr; /* Only 48 bytes, not 64. */ + struct mon_bin_hdr __user *hdr; /* Can be 48 bytes or 64. */ void __user *data; size_t alloc; /* Length of data (can be zero) */ }; @@ -129,6 +158,11 @@ struct mon_bin_mfetch32 { #define PKT_ALIGN 64 #define PKT_SIZE 64 +#define PKT_SZ_API0 48 /* API 0 (2.6.20) size */ +#define PKT_SZ_API1 64 /* API 1 size: extra fields */ + +#define ISODESC_MAX 128 /* Same number as usbfs allows, 2048 bytes. */ + /* max number of USB bus supported */ #define MON_BIN_MAX_MINOR 128 @@ -172,6 +206,11 @@ static inline struct mon_bin_hdr *MON_OFF2HDR(const struct mon_reader_bin *rp, #define MON_RING_EMPTY(rp) ((rp)->b_cnt == 0) +static unsigned char xfer_to_pipe[4] = { + PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT +}; + +static struct class *mon_bin_class; static dev_t mon_bin_dev0; static struct cdev mon_bin_cdev; @@ -183,9 +222,8 @@ static void mon_free_buff(struct mon_pgmap *map, int npages); /* * This is a "chunked memcpy". It does not manipulate any counters. - * But it returns the new offset for repeated application. */ -unsigned int mon_copy_to_buff(const struct mon_reader_bin *this, +static unsigned int mon_copy_to_buff(const struct mon_reader_bin *this, unsigned int off, const unsigned char *from, unsigned int length) { unsigned int step_len; @@ -313,12 +351,12 @@ static unsigned int mon_buff_area_alloc_contiguous(struct mon_reader_bin *rp, /* * Return a few (kilo-)bytes to the head of the buffer. - * This is used if a DMA fetch fails. + * This is used if a data fetch fails. */ static void mon_buff_area_shrink(struct mon_reader_bin *rp, unsigned int size) { - size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1); + /* size &= ~(PKT_ALIGN-1); -- we're called with aligned size */ rp->b_cnt -= size; if (rp->b_in < size) rp->b_in += rp->b_size; @@ -353,42 +391,106 @@ static inline char mon_bin_get_setup(unsigned char *setupb, const struct urb *urb, char ev_type) { - if (!usb_pipecontrol(urb->pipe) || ev_type != 'S') - return '-'; - - if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP) - return mon_dmapeek(setupb, urb->setup_dma, SETUP_LEN); if (urb->setup_packet == NULL) return 'Z'; - memcpy(setupb, urb->setup_packet, SETUP_LEN); return 0; } -static char mon_bin_get_data(const struct mon_reader_bin *rp, - unsigned int offset, struct urb *urb, unsigned int length) +static unsigned int mon_bin_get_data(const struct mon_reader_bin *rp, + unsigned int offset, struct urb *urb, unsigned int length, + char *flag) { + int i; + struct scatterlist *sg; + unsigned int this_len; + + *flag = 0; + if (urb->num_sgs == 0) { + if (urb->transfer_buffer == NULL) { + *flag = 'Z'; + return length; + } + mon_copy_to_buff(rp, offset, urb->transfer_buffer, length); + length = 0; - if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) { - mon_dmapeek_vec(rp, offset, urb->transfer_dma, length); - return 0; + } else { + /* If IOMMU coalescing occurred, we cannot trust sg_page */ + if (urb->transfer_flags & URB_DMA_SG_COMBINED) { + *flag = 'D'; + return length; + } + + /* Copy up to the first non-addressable segment */ + for_each_sg(urb->sg, sg, urb->num_sgs, i) { + if (length == 0 || PageHighMem(sg_page(sg))) + break; + this_len = min_t(unsigned int, sg->length, length); + offset = mon_copy_to_buff(rp, offset, sg_virt(sg), + this_len); + length -= this_len; + } + if (i == 0) + *flag = 'D'; } - if (urb->transfer_buffer == NULL) - return 'Z'; + return length; +} - mon_copy_to_buff(rp, offset, urb->transfer_buffer, length); - return 0; +/* + * This is the look-ahead pass in case of 'C Zi', when actual_length cannot + * be used to determine the length of the whole contiguous buffer. + */ +static unsigned int mon_bin_collate_isodesc(const struct mon_reader_bin *rp, + struct urb *urb, unsigned int ndesc) +{ + struct usb_iso_packet_descriptor *fp; + unsigned int length; + + length = 0; + fp = urb->iso_frame_desc; + while (ndesc-- != 0) { + if (fp->actual_length != 0) { + if (fp->offset + fp->actual_length > length) + length = fp->offset + fp->actual_length; + } + fp++; + } + return length; +} + +static void mon_bin_get_isodesc(const struct mon_reader_bin *rp, + unsigned int offset, struct urb *urb, char ev_type, unsigned int ndesc) +{ + struct mon_bin_isodesc *dp; + struct usb_iso_packet_descriptor *fp; + + fp = urb->iso_frame_desc; + while (ndesc-- != 0) { + dp = (struct mon_bin_isodesc *) + (rp->b_vec[offset / CHUNK_SIZE].ptr + offset % CHUNK_SIZE); + dp->iso_status = fp->status; + dp->iso_off = fp->offset; + dp->iso_len = (ev_type == 'S') ? fp->length : fp->actual_length; + dp->_pad = 0; + if ((offset += sizeof(struct mon_bin_isodesc)) >= rp->b_size) + offset = 0; + fp++; + } } static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, - char ev_type) + char ev_type, int status) { - unsigned long flags; + const struct usb_endpoint_descriptor *epd = &urb->ep->desc; struct timeval ts; + unsigned long flags; unsigned int urb_length; unsigned int offset; unsigned int length; + unsigned int delta; + unsigned int ndesc, lendesc; + unsigned char dir; struct mon_bin_hdr *ep; char data_tag = 0; @@ -403,25 +505,49 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, urb->transfer_buffer_length : urb->actual_length; length = urb_length; + if (usb_endpoint_xfer_isoc(epd)) { + if (urb->number_of_packets < 0) { + ndesc = 0; + } else if (urb->number_of_packets >= ISODESC_MAX) { + ndesc = ISODESC_MAX; + } else { + ndesc = urb->number_of_packets; + } + if (ev_type == 'C' && usb_urb_dir_in(urb)) + length = mon_bin_collate_isodesc(rp, urb, ndesc); + } else { + ndesc = 0; + } + lendesc = ndesc*sizeof(struct mon_bin_isodesc); + + /* not an issue unless there's a subtle bug in a HCD somewhere */ + if (length >= urb->transfer_buffer_length) + length = urb->transfer_buffer_length; + if (length >= rp->b_size/5) length = rp->b_size/5; - if (usb_pipein(urb->pipe)) { + if (usb_urb_dir_in(urb)) { if (ev_type == 'S') { length = 0; data_tag = '<'; } + /* Cannot rely on endpoint number in case of control ep.0 */ + dir = USB_DIR_IN; } else { if (ev_type == 'C') { length = 0; data_tag = '>'; } + dir = 0; } - if (rp->mmap_active) - offset = mon_buff_area_alloc_contiguous(rp, length + PKT_SIZE); - else - offset = mon_buff_area_alloc(rp, length + PKT_SIZE); + if (rp->mmap_active) { + offset = mon_buff_area_alloc_contiguous(rp, + length + PKT_SIZE + lendesc); + } else { + offset = mon_buff_area_alloc(rp, length + PKT_SIZE + lendesc); + } if (offset == ~0) { rp->cnt_lost++; spin_unlock_irqrestore(&rp->b_lock, flags); @@ -436,24 +562,48 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, */ memset(ep, 0, PKT_SIZE); ep->type = ev_type; - ep->xfer_type = usb_pipetype(urb->pipe); - /* We use the fact that usb_pipein() returns 0x80 */ - ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe); - ep->devnum = usb_pipedevice(urb->pipe); - ep->busnum = rp->r.m_bus->u_bus->busnum; + ep->xfer_type = xfer_to_pipe[usb_endpoint_type(epd)]; + ep->epnum = dir | usb_endpoint_num(epd); + ep->devnum = urb->dev->devnum; + ep->busnum = urb->dev->bus->busnum; ep->id = (unsigned long) urb; ep->ts_sec = ts.tv_sec; ep->ts_usec = ts.tv_usec; - ep->status = urb->status; + ep->status = status; ep->len_urb = urb_length; - ep->len_cap = length; + ep->len_cap = length + lendesc; + ep->xfer_flags = urb->transfer_flags; + + if (usb_endpoint_xfer_int(epd)) { + ep->interval = urb->interval; + } else if (usb_endpoint_xfer_isoc(epd)) { + ep->interval = urb->interval; + ep->start_frame = urb->start_frame; + ep->s.iso.error_count = urb->error_count; + ep->s.iso.numdesc = urb->number_of_packets; + } + + if (usb_endpoint_xfer_control(epd) && ev_type == 'S') { + ep->flag_setup = mon_bin_get_setup(ep->s.setup, urb, ev_type); + } else { + ep->flag_setup = '-'; + } + + if (ndesc != 0) { + ep->ndesc = ndesc; + mon_bin_get_isodesc(rp, offset, urb, ev_type, ndesc); + if ((offset += lendesc) >= rp->b_size) + offset -= rp->b_size; + } - ep->flag_setup = mon_bin_get_setup(ep->setup, urb, ev_type); if (length != 0) { - ep->flag_data = mon_bin_get_data(rp, offset, urb, length); - if (ep->flag_data != 0) { /* Yes, it's 0x00, not '0' */ - ep->len_cap = 0; - mon_buff_area_shrink(rp, length); + length = mon_bin_get_data(rp, offset, urb, length, + &ep->flag_data); + if (length > 0) { + delta = (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1); + ep->len_cap -= length; + delta -= (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1); + mon_buff_area_shrink(rp, delta); } } else { ep->flag_data = data_tag; @@ -467,22 +617,25 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, static void mon_bin_submit(void *data, struct urb *urb) { struct mon_reader_bin *rp = data; - mon_bin_event(rp, urb, 'S'); + mon_bin_event(rp, urb, 'S', -EINPROGRESS); } -static void mon_bin_complete(void *data, struct urb *urb) +static void mon_bin_complete(void *data, struct urb *urb, int status) { struct mon_reader_bin *rp = data; - mon_bin_event(rp, urb, 'C'); + mon_bin_event(rp, urb, 'C', status); } static void mon_bin_error(void *data, struct urb *urb, int error) { struct mon_reader_bin *rp = data; + struct timeval ts; unsigned long flags; unsigned int offset; struct mon_bin_hdr *ep; + do_gettimeofday(&ts); + spin_lock_irqsave(&rp->b_lock, flags); offset = mon_buff_area_alloc(rp, PKT_SIZE); @@ -496,12 +649,14 @@ static void mon_bin_error(void *data, struct urb *urb, int error) memset(ep, 0, PKT_SIZE); ep->type = 'E'; - ep->xfer_type = usb_pipetype(urb->pipe); - /* We use the fact that usb_pipein() returns 0x80 */ - ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe); - ep->devnum = usb_pipedevice(urb->pipe); - ep->busnum = rp->r.m_bus->u_bus->busnum; + ep->xfer_type = xfer_to_pipe[usb_endpoint_type(&urb->ep->desc)]; + ep->epnum = usb_urb_dir_in(urb) ? USB_DIR_IN : 0; + ep->epnum |= usb_endpoint_num(&urb->ep->desc); + ep->devnum = urb->dev->devnum; + ep->busnum = urb->dev->bus->busnum; ep->id = (unsigned long) urb; + ep->ts_sec = ts.tv_sec; + ep->ts_usec = ts.tv_usec; ep->status = error; ep->flag_setup = '-'; @@ -515,7 +670,6 @@ static void mon_bin_error(void *data, struct urb *urb, int error) static int mon_bin_open(struct inode *inode, struct file *file) { struct mon_bus *mbus; - struct usb_bus *ubus; struct mon_reader_bin *rp; size_t size; int rc; @@ -525,7 +679,7 @@ static int mon_bin_open(struct inode *inode, struct file *file) mutex_unlock(&mon_lock); return -ENODEV; } - if ((ubus = mbus->u_bus) == NULL) { + if (mbus != &mon_bus0 && mbus->u_bus == NULL) { printk(KERN_ERR TAG ": consistency error on open\n"); mutex_unlock(&mon_lock); return -ENODEV; @@ -539,7 +693,6 @@ static int mon_bin_open(struct inode *inode, struct file *file) spin_lock_init(&rp->b_lock); init_waitqueue_head(&rp->b_wait); mutex_init(&rp->fetch_lock); - rp->b_size = BUFF_DFL; size = sizeof(struct mon_pgmap) * (rp->b_size/CHUNK_SIZE); @@ -578,7 +731,8 @@ err_alloc: * Returns zero or error. */ static int mon_bin_get_event(struct file *file, struct mon_reader_bin *rp, - struct mon_bin_hdr __user *hdr, void __user *data, unsigned int nbytes) + struct mon_bin_hdr __user *hdr, unsigned int hdrbytes, + void __user *data, unsigned int nbytes) { unsigned long flags; struct mon_bin_hdr *ep; @@ -595,7 +749,7 @@ static int mon_bin_get_event(struct file *file, struct mon_reader_bin *rp, ep = MON_OFF2HDR(rp, rp->b_out); - if (copy_to_user(hdr, ep, sizeof(struct mon_bin_hdr))) { + if (copy_to_user(hdr, ep, hdrbytes)) { mutex_unlock(&rp->fetch_lock); return -EFAULT; } @@ -643,6 +797,7 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { struct mon_reader_bin *rp = file->private_data; + unsigned int hdrbytes = PKT_SZ_API0; unsigned long flags; struct mon_bin_hdr *ep; unsigned int offset; @@ -660,8 +815,8 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf, ep = MON_OFF2HDR(rp, rp->b_out); - if (rp->b_read < sizeof(struct mon_bin_hdr)) { - step_len = min(nbytes, sizeof(struct mon_bin_hdr) - rp->b_read); + if (rp->b_read < hdrbytes) { + step_len = min(nbytes, (size_t)(hdrbytes - rp->b_read)); ptr = ((char *)ep) + rp->b_read; if (step_len && copy_to_user(buf, ptr, step_len)) { mutex_unlock(&rp->fetch_lock); @@ -673,10 +828,13 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf, done += step_len; } - if (rp->b_read >= sizeof(struct mon_bin_hdr)) { - step_len = min(nbytes, (size_t)ep->len_cap); + if (rp->b_read >= hdrbytes) { + step_len = ep->len_cap; + step_len -= rp->b_read - hdrbytes; + if (step_len > nbytes) + step_len = nbytes; offset = rp->b_out + PKT_SIZE; - offset += rp->b_read - sizeof(struct mon_bin_hdr); + offset += rp->b_read - hdrbytes; if (offset >= rp->b_size) offset -= rp->b_size; if (copy_from_buf(rp, offset, buf, step_len)) { @@ -692,7 +850,7 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf, /* * Check if whole packet was read, and if so, jump to the next one. */ - if (rp->b_read >= sizeof(struct mon_bin_hdr) + ep->len_cap) { + if (rp->b_read >= hdrbytes + ep->len_cap) { spin_lock_irqsave(&rp->b_lock, flags); mon_buff_area_free(rp, PKT_SIZE + ep->len_cap); spin_unlock_irqrestore(&rp->b_lock, flags); @@ -819,8 +977,7 @@ static int mon_bin_queued(struct mon_reader_bin *rp) /* */ -static int mon_bin_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct mon_reader_bin *rp = file->private_data; // struct mon_bus* mbus = rp->r.m_bus; @@ -875,7 +1032,7 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, mutex_lock(&rp->fetch_lock); spin_lock_irqsave(&rp->b_lock, flags); - mon_free_buff(rp->b_vec, size/CHUNK_SIZE); + mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE); kfree(rp->b_vec); rp->b_vec = vec; rp->b_size = size; @@ -891,6 +1048,7 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, break; case MON_IOCX_GET: + case MON_IOCX_GETX: { struct mon_bin_get getb; @@ -900,26 +1058,12 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, if (getb.alloc > 0x10000000) /* Want to cast to u32 */ return -EINVAL; - ret = mon_bin_get_event(file, rp, - getb.hdr, getb.data, (unsigned int)getb.alloc); + ret = mon_bin_get_event(file, rp, getb.hdr, + (cmd == MON_IOCX_GET)? PKT_SZ_API0: PKT_SZ_API1, + getb.data, (unsigned int)getb.alloc); } break; -#ifdef CONFIG_COMPAT - case MON_IOCX_GET32: { - struct mon_bin_get32 getb; - - if (copy_from_user(&getb, (void __user *)arg, - sizeof(struct mon_bin_get32))) - return -EFAULT; - - ret = mon_bin_get_event(file, rp, - compat_ptr(getb.hdr32), compat_ptr(getb.data32), - getb.alloc32); - } - break; -#endif - case MON_IOCX_MFETCH: { struct mon_bin_mfetch mfetch; @@ -946,7 +1090,59 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, } break; + case MON_IOCG_STATS: { + struct mon_bin_stats __user *sp; + unsigned int nevents; + unsigned int ndropped; + + spin_lock_irqsave(&rp->b_lock, flags); + ndropped = rp->cnt_lost; + rp->cnt_lost = 0; + spin_unlock_irqrestore(&rp->b_lock, flags); + nevents = mon_bin_queued(rp); + + sp = (struct mon_bin_stats __user *)arg; + if (put_user(ndropped, &sp->dropped)) + return -EFAULT; + if (put_user(nevents, &sp->queued)) + return -EFAULT; + + } + break; + + default: + return -ENOTTY; + } + + return ret; +} + #ifdef CONFIG_COMPAT +static long mon_bin_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mon_reader_bin *rp = file->private_data; + int ret; + + switch (cmd) { + + case MON_IOCX_GET32: + case MON_IOCX_GETX32: + { + struct mon_bin_get32 getb; + + if (copy_from_user(&getb, (void __user *)arg, + sizeof(struct mon_bin_get32))) + return -EFAULT; + + ret = mon_bin_get_event(file, rp, compat_ptr(getb.hdr32), + (cmd == MON_IOCX_GET32)? PKT_SZ_API0: PKT_SZ_API1, + compat_ptr(getb.data32), getb.alloc32); + if (ret < 0) + return ret; + } + return 0; + case MON_IOCX_MFETCH32: { struct mon_bin_mfetch32 mfetch; @@ -970,37 +1166,24 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, return ret; if (put_user(ret, &uptr->nfetch32)) return -EFAULT; - ret = 0; } - break; -#endif - - case MON_IOCG_STATS: { - struct mon_bin_stats __user *sp; - unsigned int nevents; - unsigned int ndropped; + return 0; - spin_lock_irqsave(&rp->b_lock, flags); - ndropped = rp->cnt_lost; - rp->cnt_lost = 0; - spin_unlock_irqrestore(&rp->b_lock, flags); - nevents = mon_bin_queued(rp); + case MON_IOCG_STATS: + return mon_bin_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); - sp = (struct mon_bin_stats __user *)arg; - if (put_user(rp->cnt_lost, &sp->dropped)) - return -EFAULT; - if (put_user(nevents, &sp->queued)) - return -EFAULT; - - } - break; + case MON_IOCQ_URB_LEN: + case MON_IOCQ_RING_SIZE: + case MON_IOCT_RING_SIZE: + case MON_IOCH_MFLUSH: + return mon_bin_ioctl(file, cmd, arg); default: - return -ENOTTY; + ; } - - return ret; + return -ENOTTY; } +#endif /* CONFIG_COMPAT */ static unsigned int mon_bin_poll(struct file *file, struct poll_table_struct *wait) @@ -1038,49 +1221,51 @@ static void mon_bin_vma_close(struct vm_area_struct *vma) /* * Map ring pages to user space. */ -struct page *mon_bin_vma_nopage(struct vm_area_struct *vma, - unsigned long address, int *type) +static int mon_bin_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct mon_reader_bin *rp = vma->vm_private_data; unsigned long offset, chunk_idx; struct page *pageptr; - offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); + offset = vmf->pgoff << PAGE_SHIFT; if (offset >= rp->b_size) - return NOPAGE_SIGBUS; + return VM_FAULT_SIGBUS; chunk_idx = offset / CHUNK_SIZE; pageptr = rp->b_vec[chunk_idx].pg; get_page(pageptr); - if (type) - *type = VM_FAULT_MINOR; - return pageptr; + vmf->page = pageptr; + return 0; } -struct vm_operations_struct mon_bin_vm_ops = { +static const struct vm_operations_struct mon_bin_vm_ops = { .open = mon_bin_vma_open, .close = mon_bin_vma_close, - .nopage = mon_bin_vma_nopage, + .fault = mon_bin_vma_fault, }; -int mon_bin_mmap(struct file *filp, struct vm_area_struct *vma) +static int mon_bin_mmap(struct file *filp, struct vm_area_struct *vma) { - /* don't do anything here: "nopage" will set up page table entries */ + /* don't do anything here: "fault" will set up page table entries */ vma->vm_ops = &mon_bin_vm_ops; - vma->vm_flags |= VM_RESERVED; + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; vma->vm_private_data = filp->private_data; mon_bin_vma_open(vma); return 0; } -struct file_operations mon_fops_binary = { +static const struct file_operations mon_fops_binary = { .owner = THIS_MODULE, .open = mon_bin_open, .llseek = no_llseek, .read = mon_bin_read, /* .write = mon_text_write, */ .poll = mon_bin_poll, - .ioctl = mon_bin_ioctl, + .unlocked_ioctl = mon_bin_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = mon_bin_compat_ioctl, +#endif .release = mon_bin_release, + .mmap = mon_bin_mmap, }; static int mon_bin_wait_event(struct file *file, struct mon_reader_bin *rp) @@ -1129,7 +1314,7 @@ static int mon_alloc_buff(struct mon_pgmap *map, int npages) return -ENOMEM; } map[n].ptr = (unsigned char *) vaddr; - map[n].pg = virt_to_page(vaddr); + map[n].pg = virt_to_page((void *) vaddr); } return 0; } @@ -1142,10 +1327,39 @@ static void mon_free_buff(struct mon_pgmap *map, int npages) free_page((unsigned long) map[n].ptr); } +int mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus) +{ + struct device *dev; + unsigned minor = ubus? ubus->busnum: 0; + + if (minor >= MON_BIN_MAX_MINOR) + return 0; + + dev = device_create(mon_bin_class, ubus ? ubus->controller : NULL, + MKDEV(MAJOR(mon_bin_dev0), minor), NULL, + "usbmon%d", minor); + if (IS_ERR(dev)) + return 0; + + mbus->classdev = dev; + return 1; +} + +void mon_bin_del(struct mon_bus *mbus) +{ + device_destroy(mon_bin_class, mbus->classdev->devt); +} + int __init mon_bin_init(void) { int rc; + mon_bin_class = class_create(THIS_MODULE, "usbmon"); + if (IS_ERR(mon_bin_class)) { + rc = PTR_ERR(mon_bin_class); + goto err_class; + } + rc = alloc_chrdev_region(&mon_bin_dev0, 0, MON_BIN_MAX_MINOR, "usbmon"); if (rc < 0) goto err_dev; @@ -1162,6 +1376,8 @@ int __init mon_bin_init(void) err_add: unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR); err_dev: + class_destroy(mon_bin_class); +err_class: return rc; } @@ -1169,4 +1385,5 @@ void mon_bin_exit(void) { cdev_del(&mon_bin_cdev); unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR); + class_destroy(mon_bin_class); } diff --git a/drivers/usb/mon/mon_dma.c b/drivers/usb/mon/mon_dma.c deleted file mode 100644 index 140cc80bd2b..00000000000 --- a/drivers/usb/mon/mon_dma.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * The USB Monitor, inspired by Dave Harding's USBMon. - * - * mon_dma.c: Library which snoops on DMA areas. - * - * Copyright (C) 2005 Pete Zaitcev (zaitcev@redhat.com) - */ -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/highmem.h> -#include <asm/page.h> - -#include <linux/usb.h> /* Only needed for declarations in usb_mon.h */ -#include "usb_mon.h" - -/* - * PC-compatibles, are, fortunately, sufficiently cache-coherent for this. - */ -#if defined(__i386__) || defined(__x86_64__) /* CONFIG_ARCH_I386 doesn't exit */ -#define MON_HAS_UNMAP 1 - -#define phys_to_page(phys) pfn_to_page((phys) >> PAGE_SHIFT) - -char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len) -{ - struct page *pg; - unsigned long flags; - unsigned char *map; - unsigned char *ptr; - - /* - * On i386, a DMA handle is the "physical" address of a page. - * In other words, the bus address is equal to physical address. - * There is no IOMMU. - */ - pg = phys_to_page(dma_addr); - - /* - * We are called from hardware IRQs in case of callbacks. - * But we can be called from softirq or process context in case - * of submissions. In such case, we need to protect KM_IRQ0. - */ - local_irq_save(flags); - map = kmap_atomic(pg, KM_IRQ0); - ptr = map + (dma_addr & (PAGE_SIZE-1)); - memcpy(dst, ptr, len); - kunmap_atomic(map, KM_IRQ0); - local_irq_restore(flags); - return 0; -} - -void mon_dmapeek_vec(const struct mon_reader_bin *rp, - unsigned int offset, dma_addr_t dma_addr, unsigned int length) -{ - unsigned long flags; - unsigned int step_len; - struct page *pg; - unsigned char *map; - unsigned long page_off, page_len; - - local_irq_save(flags); - while (length) { - /* compute number of bytes we are going to copy in this page */ - step_len = length; - page_off = dma_addr & (PAGE_SIZE-1); - page_len = PAGE_SIZE - page_off; - if (page_len < step_len) - step_len = page_len; - - /* copy data and advance pointers */ - pg = phys_to_page(dma_addr); - map = kmap_atomic(pg, KM_IRQ0); - offset = mon_copy_to_buff(rp, offset, map + page_off, step_len); - kunmap_atomic(map, KM_IRQ0); - dma_addr += step_len; - length -= step_len; - } - local_irq_restore(flags); -} - -#endif /* __i386__ */ - -#ifndef MON_HAS_UNMAP -char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len) -{ - return 'D'; -} - -void mon_dmapeek_vec(const struct mon_reader_bin *rp, - unsigned int offset, dma_addr_t dma_addr, unsigned int length) -{ - ; -} - -#endif /* MON_HAS_UNMAP */ diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index c9739e7b35e..10405119985 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -9,15 +9,14 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/usb.h> -#include <linux/smp_lock.h> +#include <linux/usb/hcd.h> +#include <linux/slab.h> #include <linux/notifier.h> #include <linux/mutex.h> #include "usb_mon.h" -#include "../core/hcd.h" -static void mon_submit(struct usb_bus *ubus, struct urb *urb); -static void mon_complete(struct usb_bus *ubus, struct urb *urb); + static void mon_stop(struct mon_bus *mbus); static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus); static void mon_bus_drop(struct kref *r); @@ -25,6 +24,7 @@ static void mon_bus_init(struct usb_bus *ubus); DEFINE_MUTEX(mon_lock); +struct mon_bus mon_bus0; /* Pseudo bus meaning "all buses" */ static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */ /* @@ -35,22 +35,19 @@ static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */ void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r) { unsigned long flags; - struct usb_bus *ubus; + struct list_head *p; spin_lock_irqsave(&mbus->lock, flags); if (mbus->nreaders == 0) { - ubus = mbus->u_bus; - if (ubus->monitored) { - /* - * Something is really broken, refuse to go on and - * possibly corrupt ops pointers or worse. - */ - printk(KERN_ERR TAG ": bus %d is already monitored\n", - ubus->busnum); - spin_unlock_irqrestore(&mbus->lock, flags); - return; + if (mbus == &mon_bus0) { + list_for_each (p, &mon_buses) { + struct mon_bus *m1; + m1 = list_entry(p, struct mon_bus, bus_link); + m1->u_bus->monitored = 1; + } + } else { + mbus->u_bus->monitored = 1; } - ubus->monitored = 1; } mbus->nreaders++; list_add_tail(&r->r_link, &mbus->r_list); @@ -80,97 +77,82 @@ void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r) /* */ -static void mon_submit(struct usb_bus *ubus, struct urb *urb) +static void mon_bus_submit(struct mon_bus *mbus, struct urb *urb) { - struct mon_bus *mbus; unsigned long flags; struct list_head *pos; struct mon_reader *r; - mbus = ubus->mon_bus; - if (mbus == NULL) - goto out_unlocked; - spin_lock_irqsave(&mbus->lock, flags); - if (mbus->nreaders == 0) - goto out_locked; - mbus->cnt_events++; list_for_each (pos, &mbus->r_list) { r = list_entry(pos, struct mon_reader, r_link); r->rnf_submit(r->r_data, urb); } - spin_unlock_irqrestore(&mbus->lock, flags); - return; +} -out_locked: - spin_unlock_irqrestore(&mbus->lock, flags); -out_unlocked: - return; +static void mon_submit(struct usb_bus *ubus, struct urb *urb) +{ + struct mon_bus *mbus; + + if ((mbus = ubus->mon_bus) != NULL) + mon_bus_submit(mbus, urb); + mon_bus_submit(&mon_bus0, urb); } /* */ -static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error) +static void mon_bus_submit_error(struct mon_bus *mbus, struct urb *urb, int error) { - struct mon_bus *mbus; unsigned long flags; struct list_head *pos; struct mon_reader *r; - mbus = ubus->mon_bus; - if (mbus == NULL) - goto out_unlocked; - spin_lock_irqsave(&mbus->lock, flags); - if (mbus->nreaders == 0) - goto out_locked; - mbus->cnt_events++; list_for_each (pos, &mbus->r_list) { r = list_entry(pos, struct mon_reader, r_link); r->rnf_error(r->r_data, urb, error); } - spin_unlock_irqrestore(&mbus->lock, flags); - return; +} -out_locked: - spin_unlock_irqrestore(&mbus->lock, flags); -out_unlocked: - return; +static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error) +{ + struct mon_bus *mbus; + + if ((mbus = ubus->mon_bus) != NULL) + mon_bus_submit_error(mbus, urb, error); + mon_bus_submit_error(&mon_bus0, urb, error); } /* */ -static void mon_complete(struct usb_bus *ubus, struct urb *urb) +static void mon_bus_complete(struct mon_bus *mbus, struct urb *urb, int status) { - struct mon_bus *mbus; unsigned long flags; struct list_head *pos; struct mon_reader *r; - mbus = ubus->mon_bus; - if (mbus == NULL) { - /* - * This should not happen. - * At this point we do not even know the bus number... - */ - printk(KERN_ERR TAG ": Null mon bus in URB, pipe 0x%x\n", - urb->pipe); - return; - } - spin_lock_irqsave(&mbus->lock, flags); mbus->cnt_events++; list_for_each (pos, &mbus->r_list) { r = list_entry(pos, struct mon_reader, r_link); - r->rnf_complete(r->r_data, urb); + r->rnf_complete(r->r_data, urb, status); } spin_unlock_irqrestore(&mbus->lock, flags); } +static void mon_complete(struct usb_bus *ubus, struct urb *urb, int status) +{ + struct mon_bus *mbus; + + if ((mbus = ubus->mon_bus) != NULL) + mon_bus_complete(mbus, urb, status); + mon_bus_complete(&mon_bus0, urb, status); +} + /* int (*unlink_urb) (struct urb *urb, int status); */ /* @@ -178,15 +160,27 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb) */ static void mon_stop(struct mon_bus *mbus) { - struct usb_bus *ubus = mbus->u_bus; + struct usb_bus *ubus; + struct list_head *p; - /* - * A stop can be called for a dissolved mon_bus in case of - * a reader staying across an rmmod foo_hcd. - */ - if (ubus != NULL) { - ubus->monitored = 0; - mb(); + if (mbus == &mon_bus0) { + list_for_each (p, &mon_buses) { + mbus = list_entry(p, struct mon_bus, bus_link); + /* + * We do not change nreaders here, so rely on mon_lock. + */ + if (mbus->nreaders == 0 && (ubus = mbus->u_bus) != NULL) + ubus->monitored = 0; + } + } else { + /* + * A stop can be called for a dissolved mon_bus in case of + * a reader staying across an rmmod foo_hcd, so test ->u_bus. + */ + if (mon_bus0.nreaders == 0 && (ubus = mbus->u_bus) != NULL) { + ubus->monitored = 0; + mb(); + } } } @@ -199,6 +193,10 @@ static void mon_stop(struct mon_bus *mbus) static void mon_bus_add(struct usb_bus *ubus) { mon_bus_init(ubus); + mutex_lock(&mon_lock); + if (mon_bus0.nreaders != 0) + ubus->monitored = 1; + mutex_unlock(&mon_lock); } /* @@ -212,6 +210,8 @@ static void mon_bus_remove(struct usb_bus *ubus) list_del(&mbus->bus_link); if (mbus->text_inited) mon_text_del(mbus); + if (mbus->bin_inited) + mon_bin_del(mbus); mon_dissolve(mbus, ubus); kref_put(&mbus->ref, mon_bus_drop); @@ -250,12 +250,7 @@ static struct usb_mon_operations mon_ops_0 = { static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus) { - /* - * Never happens, but... - */ if (ubus->monitored) { - printk(KERN_ERR TAG ": bus %d is dissolved while monitored\n", - ubus->busnum); ubus->monitored = 0; mb(); } @@ -263,6 +258,8 @@ static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus) ubus->mon_bus = NULL; mbus->u_bus = NULL; mb(); + + /* We want synchronize_irq() here, but that needs an argument. */ } /* @@ -295,10 +292,9 @@ static void mon_bus_init(struct usb_bus *ubus) */ mbus->u_bus = ubus; ubus->mon_bus = mbus; - mbus->uses_dma = ubus->uses_dma; mbus->text_inited = mon_text_add(mbus, ubus); - // mon_bin_add(...) + mbus->bin_inited = mon_bin_add(mbus, ubus); mutex_lock(&mon_lock); list_add_tail(&mbus->bus_link, &mon_buses); @@ -309,6 +305,18 @@ err_alloc: return; } +static void mon_bus0_init(void) +{ + struct mon_bus *mbus = &mon_bus0; + + kref_init(&mbus->ref); + spin_lock_init(&mbus->lock); + INIT_LIST_HEAD(&mbus->r_list); + + mbus->text_inited = mon_text_add(mbus, NULL); + mbus->bin_inited = mon_bin_add(mbus, NULL); +} + /* * Search a USB bus by number. Notice that USB bus numbers start from one, * which we may later use to identify "all" with zero. @@ -322,6 +330,9 @@ struct mon_bus *mon_bus_lookup(unsigned int num) struct list_head *p; struct mon_bus *mbus; + if (num == 0) { + return &mon_bus0; + } list_for_each (p, &mon_buses) { mbus = list_entry(p, struct mon_bus, bus_link); if (mbus->u_bus->busnum == num) { @@ -341,6 +352,8 @@ static int __init mon_init(void) if ((rc = mon_bin_init()) != 0) goto err_bin; + mon_bus0_init(); + if (usb_mon_register(&mon_ops_0) != 0) { printk(KERN_NOTICE TAG ": unable to register with the core\n"); rc = -ENODEV; @@ -348,12 +361,11 @@ static int __init mon_init(void) } // MOD_INC_USE_COUNT(which_module?); - usb_register_notify(&mon_nb); - mutex_lock(&usb_bus_list_lock); list_for_each_entry (ubus, &usb_bus_list, bus_list) { mon_bus_init(ubus); } + usb_register_notify(&mon_nb); mutex_unlock(&usb_bus_list_lock); return 0; @@ -374,6 +386,7 @@ static void __exit mon_exit(void) usb_mon_deregister(); mutex_lock(&mon_lock); + while (!list_empty(&mon_buses)) { p = mon_buses.next; mbus = list_entry(p, struct mon_bus, bus_link); @@ -381,6 +394,8 @@ static void __exit mon_exit(void) if (mbus->text_inited) mon_text_del(mbus); + if (mbus->bin_inited) + mon_bin_del(mbus); /* * This never happens, because the open/close paths in @@ -397,6 +412,13 @@ static void __exit mon_exit(void) mon_dissolve(mbus, mbus->u_bus); kref_put(&mbus->ref, mon_bus_drop); } + + mbus = &mon_bus0; + if (mbus->text_inited) + mon_text_del(mbus); + if (mbus->bin_inited) + mon_bin_del(mbus); + mutex_unlock(&mon_lock); mon_text_exit(); diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c index f6d1491256c..ebd6189a501 100644 --- a/drivers/usb/mon/mon_stat.c +++ b/drivers/usb/mon/mon_stat.c @@ -8,7 +8,10 @@ */ #include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/export.h> #include <linux/usb.h> +#include <linux/fs.h> #include <asm/uaccess.h> #include "usb_mon.h" @@ -42,23 +45,15 @@ static ssize_t mon_stat_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { struct snap *sp = file->private_data; - loff_t pos = *ppos; - int cnt; - if (pos < 0 || pos >= sp->slen) - return 0; - if (nbytes == 0) - return 0; - if ((cnt = sp->slen - pos) > nbytes) - cnt = nbytes; - if (copy_to_user(buf, sp->str + pos, cnt)) - return -EFAULT; - *ppos = pos + cnt; - return cnt; + return simple_read_from_buffer(buf, nbytes, ppos, sp->str, sp->slen); } static int mon_stat_release(struct inode *inode, struct file *file) { + struct snap *sp = file->private_data; + file->private_data = NULL; + kfree(sp); return 0; } @@ -69,6 +64,6 @@ const struct file_operations mon_fops_stat = { .read = mon_stat_read, /* .write = mon_stat_write, */ /* .poll = mon_stat_poll, */ - /* .ioctl = mon_stat_ioctl, */ + /* .unlocked_ioctl = mon_stat_ioctl, */ .release = mon_stat_release, }; diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 494ee3b9a22..ad408251d95 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -7,9 +7,12 @@ #include <linux/kernel.h> #include <linux/list.h> #include <linux/usb.h> +#include <linux/slab.h> #include <linux/time.h> +#include <linux/export.h> #include <linux/mutex.h> #include <linux/debugfs.h> +#include <linux/scatterlist.h> #include <asm/uaccess.h> #include "usb_mon.h" @@ -31,20 +34,41 @@ * to a local DoS. But we have to keep to root in order to prevent * password sniffing from HID devices. */ -#define EVENT_MAX (2*PAGE_SIZE / sizeof(struct mon_event_text)) +#define EVENT_MAX (4*PAGE_SIZE / sizeof(struct mon_event_text)) -#define PRINTF_DFL 160 +/* + * 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; + char devnum; + char epnum; + char is_in; + char xfertype; 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]; }; @@ -66,7 +90,29 @@ struct mon_reader_text { static struct dentry *mon_dir; /* Usually /sys/kernel/debug/usbmon */ -static void mon_text_ctor(void *, struct kmem_cache *, unsigned long); +static void mon_text_ctor(void *); + +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 @@ -81,11 +127,9 @@ 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') + if (ep->xfertype != USB_ENDPOINT_XFER_CONTROL || ev_type != 'S') return '-'; - if (mbus->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. */ @@ -96,37 +140,37 @@ static inline char mon_text_get_setup(struct mon_event_text *ep, 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; + void *src; if (len <= 0) return 'L'; if (len >= DATA_MAX) len = DATA_MAX; - if (usb_pipein(pipe)) { - if (ev_type == 'S') + if (ep->is_in) { + if (ev_type != 'C') return '<'; } else { - if (ev_type == 'C') + 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 (mbus->uses_dma && (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) - return mon_dmapeek(ep->data, urb->transfer_dma, len); + if (urb->num_sgs == 0) { + src = urb->transfer_buffer; + if (src == NULL) + return 'Z'; /* '0' would be not as pretty. */ + } else { + struct scatterlist *sg = urb->sg; - if (urb->transfer_buffer == NULL) - return 'Z'; /* '0' would be not as pretty. */ + if (PageHighMem(sg_page(sg))) + return 'D'; + + /* For the text interface we copy only the first sg buffer */ + len = min_t(int, sg->length, len); + src = sg_virt(sg); + } - memcpy(ep->data, urb->transfer_buffer, len); + memcpy(ep->data, src, len); return 0; } @@ -136,16 +180,19 @@ static inline unsigned int mon_get_timestamp(void) unsigned int stamp; do_gettimeofday(&tval); - stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s. */ + stamp = tval.tv_sec & 0xFFF; /* 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) + char ev_type, int status) { 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(); @@ -156,13 +203,44 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb, } ep->type = ev_type; - ep->pipe = urb->pipe; ep->id = (unsigned long) urb; + ep->busnum = urb->dev->bus->busnum; + ep->devnum = urb->dev->devnum; + ep->epnum = usb_endpoint_num(&urb->ep->desc); + ep->xfertype = usb_endpoint_type(&urb->ep->desc); + ep->is_in = usb_urb_dir_in(urb); 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; + ep->status = status; + + if (ep->xfertype == USB_ENDPOINT_XFER_INT) { + ep->interval = urb->interval; + } else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) { + ep->interval = urb->interval; + ep->start_frame = urb->start_frame; + ep->error_count = urb->error_count; + } + ep->numdesc = urb->number_of_packets; + if (ep->xfertype == USB_ENDPOINT_XFER_ISOC && + 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++; + } + /* Wasteful, but simple to understand: ISO 'C' is sparse. */ + if (ev_type == 'C') + ep->length = urb->transfer_buffer_length; + } 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, @@ -176,13 +254,13 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb, static void mon_text_submit(void *data, struct urb *urb) { struct mon_reader_text *rp = data; - mon_text_event(rp, urb, 'S'); + mon_text_event(rp, urb, 'S', -EINPROGRESS); } -static void mon_text_complete(void *data, struct urb *urb) +static void mon_text_complete(void *data, struct urb *urb, int status) { struct mon_reader_text *rp = data; - mon_text_event(rp, urb, 'C'); + mon_text_event(rp, urb, 'C', status); } static void mon_text_error(void *data, struct urb *urb, int error) @@ -197,9 +275,13 @@ static void mon_text_error(void *data, struct urb *urb, int error) } ep->type = 'E'; - ep->pipe = urb->pipe; ep->id = (unsigned long) urb; - ep->tstamp = 0; + ep->busnum = urb->dev->bus->busnum; + ep->devnum = urb->dev->devnum; + ep->epnum = usb_endpoint_num(&urb->ep->desc); + ep->xfertype = usb_endpoint_type(&urb->ep->desc); + ep->is_in = usb_urb_dir_in(urb); + ep->tstamp = mon_get_timestamp(); ep->length = 0; ep->status = error; @@ -237,13 +319,11 @@ static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp, static int mon_text_open(struct inode *inode, struct file *file) { struct mon_bus *mbus; - struct usb_bus *ubus; struct mon_reader_text *rp; int rc; mutex_lock(&mon_lock); mbus = inode->i_private; - ubus = mbus->u_bus; rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL); if (rp == NULL) { @@ -267,11 +347,10 @@ static int mon_text_open(struct inode *inode, struct file *file) rp->r.rnf_error = mon_text_error; rp->r.rnf_complete = mon_text_complete; - snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum, - (long)rp); + 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); + mon_text_ctor); if (rp->e_slab == NULL) { rc = -ENOMEM; goto err_slab; @@ -300,17 +379,75 @@ err_alloc: * 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(struct file *file, char __user *buf, +static ssize_t mon_text_read_t(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { struct mon_reader_text *rp = file->private_data; + struct mon_event_text *ep; + struct mon_text_ptr ptr; + + if (IS_ERR(ep = mon_text_read_wait(rp, file))) + return PTR_ERR(ep); + mutex_lock(&rp->printf_lock); + ptr.cnt = 0; + ptr.pbuf = rp->printf_buf; + ptr.limit = rp->printf_size; + + mon_text_read_head_t(rp, &ptr, ep); + mon_text_read_statset(rp, &ptr, ep); + ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, + " %d", ep->length); + mon_text_read_data(rp, &ptr, ep); + + if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) + ptr.cnt = -EFAULT; + mutex_unlock(&rp->printf_lock); + kmem_cache_free(rp->e_slab, ep); + return ptr.cnt; +} + +static ssize_t mon_text_read_u(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct mon_reader_text *rp = file->private_data; + struct mon_event_text *ep; + struct mon_text_ptr ptr; + + if (IS_ERR(ep = mon_text_read_wait(rp, file))) + return PTR_ERR(ep); + mutex_lock(&rp->printf_lock); + ptr.cnt = 0; + ptr.pbuf = rp->printf_buf; + ptr.limit = rp->printf_size; + + mon_text_read_head_u(rp, &ptr, ep); + if (ep->type == 'E') { + mon_text_read_statset(rp, &ptr, ep); + } else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) { + mon_text_read_isostat(rp, &ptr, ep); + mon_text_read_isodesc(rp, &ptr, ep); + } else if (ep->xfertype == USB_ENDPOINT_XFER_INT) { + mon_text_read_intstat(rp, &ptr, ep); + } else { + mon_text_read_statset(rp, &ptr, ep); + } + ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, + " %d", ep->length); + mon_text_read_data(rp, &ptr, ep); + + if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) + ptr.cnt = -EFAULT; + mutex_unlock(&rp->printf_lock); + kmem_cache_free(rp->e_slab, ep); + return ptr.cnt; +} + +static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp, + struct file *file) +{ struct mon_bus *mbus = rp->r.m_bus; DECLARE_WAITQUEUE(waita, current); struct mon_event_text *ep; - int cnt, limit; - char *pbuf; - char udir, utype; - int data_len, i; add_wait_queue(&rp->wait, &waita); set_current_state(TASK_INTERRUPTIBLE); @@ -318,7 +455,7 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, if (file->f_flags & O_NONBLOCK) { set_current_state(TASK_RUNNING); remove_wait_queue(&rp->wait, &waita); - return -EWOULDBLOCK; /* Same as EAGAIN in Linux */ + return ERR_PTR(-EWOULDBLOCK); } /* * We do not count nwaiters, because ->release is supposed @@ -327,32 +464,57 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, schedule(); if (signal_pending(current)) { remove_wait_queue(&rp->wait, &waita); - return -EINTR; + return ERR_PTR(-EINTR); } set_current_state(TASK_INTERRUPTIBLE); } set_current_state(TASK_RUNNING); remove_wait_queue(&rp->wait, &waita); + return ep; +} - mutex_lock(&rp->printf_lock); - cnt = 0; - pbuf = rp->printf_buf; - limit = rp->printf_size; - - udir = usb_pipein(ep->pipe) ? 'i' : 'o'; - switch (usb_pipetype(ep->pipe)) { - case PIPE_ISOCHRONOUS: utype = 'Z'; break; - case PIPE_INTERRUPT: utype = 'I'; break; - case PIPE_CONTROL: utype = 'C'; break; +static void mon_text_read_head_t(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + char udir, utype; + + udir = (ep->is_in ? 'i' : 'o'); + switch (ep->xfertype) { + case USB_ENDPOINT_XFER_ISOC: utype = 'Z'; break; + case USB_ENDPOINT_XFER_INT: utype = 'I'; break; + case USB_ENDPOINT_XFER_CONTROL: utype = 'C'; break; default: /* PIPE_BULK */ utype = 'B'; } - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "%lx %u %c %c%c:%03u:%02u", ep->id, ep->tstamp, ep->type, - utype, udir, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe)); + utype, udir, ep->devnum, ep->epnum); +} + +static void mon_text_read_head_u(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + char udir, utype; + + udir = (ep->is_in ? 'i' : 'o'); + switch (ep->xfertype) { + case USB_ENDPOINT_XFER_ISOC: utype = 'Z'; break; + case USB_ENDPOINT_XFER_INT: utype = 'I'; break; + case USB_ENDPOINT_XFER_CONTROL: utype = 'C'; break; + default: /* PIPE_BULK */ utype = 'B'; + } + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + "%lx %u %c %c%c:%d:%03u:%u", + ep->id, ep->tstamp, ep->type, + utype, udir, ep->busnum, ep->devnum, ep->epnum); +} + +static void mon_text_read_statset(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ if (ep->setup_flag == 0) { /* Setup packet is present and captured */ - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, " s %02x %02x %04x %04x %04x", ep->setup[0], ep->setup[1], @@ -360,40 +522,86 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, (ep->setup[5] << 8) | ep->setup[4], (ep->setup[7] << 8) | ep->setup[6]); } else if (ep->setup_flag != '-') { /* Unable to capture setup packet */ - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, " %c __ __ ____ ____ ____", ep->setup_flag); } else { /* No setup for this kind of URB */ - cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->status); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d", ep->status); } - cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->length); +} + +static void mon_text_read_intstat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%d", ep->status, ep->interval); +} + +static void mon_text_read_isostat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + if (ep->type == 'S') { + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%d:%d", ep->status, ep->interval, ep->start_frame); + } else { + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%d:%d:%d", + ep->status, ep->interval, ep->start_frame, ep->error_count); + } +} + +static void mon_text_read_isodesc(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + int ndesc; /* Display this many */ + int i; + const struct mon_iso_desc *dp; + + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d", ep->numdesc); + ndesc = ep->numdesc; + if (ndesc > ISODESC_MAX) + ndesc = ISODESC_MAX; + if (ndesc < 0) + ndesc = 0; + dp = ep->isodesc; + for (i = 0; i < ndesc; i++) { + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%u:%u", dp->status, dp->offset, dp->length); + dp++; + } +} + +static void mon_text_read_data(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + int data_len, i; if ((data_len = ep->length) > 0) { if (ep->data_flag == 0) { - cnt += snprintf(pbuf + cnt, limit - cnt, " ="); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " ="); if (data_len >= DATA_MAX) data_len = DATA_MAX; for (i = 0; i < data_len; i++) { if (i % 4 == 0) { - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, + p->limit - p->cnt, " "); } - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, + p->limit - p->cnt, "%02x", ep->data[i]); } - cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + "\n"); } else { - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, " %c\n", ep->data_flag); } } else { - cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n"); } - - if (copy_to_user(buf, rp->printf_buf, cnt)) - cnt = -EFAULT; - mutex_unlock(&rp->printf_lock); - kmem_cache_free(rp->e_slab, ep); - return cnt; } static int mon_text_release(struct inode *inode, struct file *file) @@ -439,14 +647,19 @@ static int mon_text_release(struct inode *inode, struct file *file) return 0; } -static const struct file_operations mon_fops_text = { +static const struct file_operations mon_fops_text_t = { .owner = THIS_MODULE, .open = mon_text_open, .llseek = no_llseek, - .read = mon_text_read, - /* .write = mon_text_write, */ - /* .poll = mon_text_poll, */ - /* .ioctl = mon_text_ioctl, */ + .read = mon_text_read_t, + .release = mon_text_release, +}; + +static const struct file_operations mon_fops_text_u = { + .owner = THIS_MODULE, + .open = mon_text_open, + .llseek = no_llseek, + .read = mon_text_read_u, .release = mon_text_release, }; @@ -455,18 +668,32 @@ int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus) struct dentry *d; enum { NAMESZ = 10 }; char name[NAMESZ]; + int busnum = ubus? ubus->busnum: 0; int rc; - rc = snprintf(name, NAMESZ, "%dt", ubus->busnum); + if (mon_dir == NULL) + return 0; + + if (ubus != NULL) { + rc = snprintf(name, NAMESZ, "%dt", busnum); + if (rc <= 0 || rc >= NAMESZ) + goto err_print_t; + d = debugfs_create_file(name, 0600, mon_dir, mbus, + &mon_fops_text_t); + if (d == NULL) + goto err_create_t; + mbus->dent_t = d; + } + + rc = snprintf(name, NAMESZ, "%du", busnum); if (rc <= 0 || rc >= NAMESZ) - goto err_print_t; - d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text); + goto err_print_u; + d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_u); if (d == NULL) - goto err_create_t; - mbus->dent_t = d; + goto err_create_u; + mbus->dent_u = d; - /* XXX The stats do not belong to here (text API), but oh well... */ - rc = snprintf(name, NAMESZ, "%ds", ubus->busnum); + rc = snprintf(name, NAMESZ, "%ds", busnum); if (rc <= 0 || rc >= NAMESZ) goto err_print_s; d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat); @@ -478,8 +705,14 @@ int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus) err_create_s: err_print_s: - debugfs_remove(mbus->dent_t); - mbus->dent_t = NULL; + debugfs_remove(mbus->dent_u); + mbus->dent_u = NULL; +err_create_u: +err_print_u: + if (ubus != NULL) { + debugfs_remove(mbus->dent_t); + mbus->dent_t = NULL; + } err_create_t: err_print_t: return 0; @@ -487,14 +720,16 @@ err_print_t: void mon_text_del(struct mon_bus *mbus) { - debugfs_remove(mbus->dent_t); + debugfs_remove(mbus->dent_u); + if (mbus->dent_t != NULL) + debugfs_remove(mbus->dent_t); debugfs_remove(mbus->dent_s); } /* * Slab interface: constructor. */ -static void mon_text_ctor(void *mem, struct kmem_cache *slab, unsigned long sflags) +static void mon_text_ctor(void *mem) { /* * Nothing to initialize. No, really! @@ -507,14 +742,14 @@ int __init mon_text_init(void) { struct dentry *mondir; - mondir = debugfs_create_dir("usbmon", NULL); + mondir = debugfs_create_dir("usbmon", usb_debug_root); if (IS_ERR(mondir)) { - printk(KERN_NOTICE TAG ": debugfs is not available\n"); - return -ENODEV; + /* debugfs not available, but we can use usbmon without it */ + return 0; } if (mondir == NULL) { printk(KERN_NOTICE TAG ": unable to create usbmon directory\n"); - return -ENODEV; + return -ENOMEM; } mon_dir = mondir; return 0; diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h index efdfd8993d9..df9a4df342c 100644 --- a/drivers/usb/mon/usb_mon.h +++ b/drivers/usb/mon/usb_mon.h @@ -20,9 +20,11 @@ struct mon_bus { struct usb_bus *u_bus; int text_inited; + int bin_inited; struct dentry *dent_s; /* Debugging file */ struct dentry *dent_t; /* Text interface file */ - int uses_dma; + struct dentry *dent_u; /* Second text interface file */ + struct device *classdev; /* Device in usbmon class */ /* Ref */ int nreaders; /* Under mon_lock AND mbus->lock */ @@ -44,7 +46,7 @@ struct mon_reader { void (*rnf_submit)(void *data, struct urb *urb); void (*rnf_error)(void *data, struct urb *urb, int error); - void (*rnf_complete)(void *data, struct urb *urb); + void (*rnf_complete)(void *data, struct urb *urb, int status); }; void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r); @@ -54,7 +56,8 @@ struct mon_bus *mon_bus_lookup(unsigned int num); int /*bool*/ mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus); void mon_text_del(struct mon_bus *mbus); -// void mon_bin_add(struct mon_bus *); +int /*bool*/ mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus); +void mon_bin_del(struct mon_bus *mbus); int __init mon_text_init(void); void mon_text_exit(void); @@ -62,23 +65,11 @@ int __init mon_bin_init(void); void mon_bin_exit(void); /* - * DMA interface. - * - * XXX The vectored side needs a serious re-thinking. Abstracting vectors, - * like in Paolo's original patch, produces a double pkmap. We need an idea. -*/ -extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len); - -struct mon_reader_bin; -extern void mon_dmapeek_vec(const struct mon_reader_bin *rp, - unsigned int offset, dma_addr_t dma_addr, unsigned int len); -extern unsigned int mon_copy_to_buff(const struct mon_reader_bin *rp, - unsigned int offset, const unsigned char *from, unsigned int len); - -/* */ extern struct mutex mon_lock; extern const struct file_operations mon_fops_stat; +extern struct mon_bus mon_bus0; /* Only for redundant checks */ + #endif /* __USB_MON_H */ |
