forked from luck/tmp_suning_uos_patched
USB: add binary API to usbmon
This patch adds a new, "binary" API in addition to the old, text API usbmon had before. The new API allows for less CPU use, and it allows to capture all data from a packet where old API only captured 32 bytes at most. There are some limitations and conditions to this, e.g. in case someone constructs a URB with 1GB of data, it's not likely to be captured, because even the huge buffers of the new reader are finite. Nonetheless, I expect this new capability to capture all data for all real life scenarios. The downside is, a special user mode application is required where cat(1) worked before. I have sample code at http://people.redhat.com/zaitcev/linux/ and Paolo Abeni is working on patching libpcap. This patch was initially written by Paolo and later I tweaked it, and we had a little back-and-forth. So this is a jointly authored patch, but I am submitting this I am responsible for the bugs. Signed-off-by: Paolo Abeni <paolo.abeni@email.it> Signed-off-by: Pete Zaitcev <zaitcev@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
a8ef36bc0a
commit
6f23ee1fef
|
@ -77,7 +77,7 @@ that the file size is not excessive for your favourite editor.
|
||||||
|
|
||||||
The '1t' type data consists of a stream of events, such as URB submission,
|
The '1t' type data consists of a stream of events, such as URB submission,
|
||||||
URB callback, submission error. Every event is a text line, which consists
|
URB callback, submission error. Every event is a text line, which consists
|
||||||
of whitespace separated words. The number of position of words may depend
|
of whitespace separated words. The number or position of words may depend
|
||||||
on the event type, but there is a set of words, common for all types.
|
on the event type, but there is a set of words, common for all types.
|
||||||
|
|
||||||
Here is the list of words, from left to right:
|
Here is the list of words, from left to right:
|
||||||
|
@ -170,4 +170,152 @@ dd65f0e8 4128379808 C Bo:005:02 0 31 >
|
||||||
|
|
||||||
* Raw binary format and API
|
* Raw binary format and API
|
||||||
|
|
||||||
TBD
|
The overall architecture of the API is about the same as the one above,
|
||||||
|
only the events are delivered in binary format. Each event is sent in
|
||||||
|
the following structure (its name is made up, so that we can refer to it):
|
||||||
|
|
||||||
|
struct usbmon_packet {
|
||||||
|
u64 id; /* 0: URB ID - from submission to callback */
|
||||||
|
unsigned char type; /* 8: Same as text; extensible. */
|
||||||
|
unsigned char xfer_type; /* ISO (0), Intr, Control, Bulk (3) */
|
||||||
|
unsigned char epnum; /* Endpoint number and transfer direction */
|
||||||
|
unsigned char devnum; /* Device address */
|
||||||
|
u16 busnum; /* 12: Bus number */
|
||||||
|
char flag_setup; /* 14: Same as text */
|
||||||
|
char flag_data; /* 15: Same as text; Binary zero is OK. */
|
||||||
|
s64 ts_sec; /* 16: gettimeofday */
|
||||||
|
s32 ts_usec; /* 24: gettimeofday */
|
||||||
|
int status; /* 28: */
|
||||||
|
unsigned int length; /* 32: Length of data (submitted or actual) */
|
||||||
|
unsigned int len_cap; /* 36: Delivered length */
|
||||||
|
unsigned char setup[8]; /* 40: Only for Control 'S' */
|
||||||
|
}; /* 48 bytes total */
|
||||||
|
|
||||||
|
These events can be received from a character device by reading with read(2),
|
||||||
|
with an ioctl(2), or by accessing the buffer with mmap.
|
||||||
|
|
||||||
|
The character device is usually called /dev/usbmonN, where N is the USB bus
|
||||||
|
number. Number zero (/dev/usbmon0) is special and means "all buses".
|
||||||
|
However, this feature is not implemented yet. Note that specific naming
|
||||||
|
policy is set by your Linux distribution.
|
||||||
|
|
||||||
|
If you create /dev/usbmon0 by hand, make sure that it is owned by root
|
||||||
|
and has mode 0600. Otherwise, unpriviledged users will be able to snoop
|
||||||
|
keyboard traffic.
|
||||||
|
|
||||||
|
The following ioctl calls are available, with MON_IOC_MAGIC 0x92:
|
||||||
|
|
||||||
|
MON_IOCQ_URB_LEN, defined as _IO(MON_IOC_MAGIC, 1)
|
||||||
|
|
||||||
|
This call returns the length of data in the next event. Note that majority of
|
||||||
|
events contain no data, so if this call returns zero, it does not mean that
|
||||||
|
no events are available.
|
||||||
|
|
||||||
|
MON_IOCG_STATS, defined as _IOR(MON_IOC_MAGIC, 3, struct mon_bin_stats)
|
||||||
|
|
||||||
|
The argument is a pointer to the following structure:
|
||||||
|
|
||||||
|
struct mon_bin_stats {
|
||||||
|
u32 queued;
|
||||||
|
u32 dropped;
|
||||||
|
};
|
||||||
|
|
||||||
|
The member "queued" refers to the number of events currently queued in the
|
||||||
|
buffer (and not to the number of events processed since the last reset).
|
||||||
|
|
||||||
|
The member "dropped" is the number of events lost since the last call
|
||||||
|
to MON_IOCG_STATS.
|
||||||
|
|
||||||
|
MON_IOCT_RING_SIZE, defined as _IO(MON_IOC_MAGIC, 4)
|
||||||
|
|
||||||
|
This call sets the buffer size. The argument is the size in bytes.
|
||||||
|
The size may be rounded down to the next chunk (or page). If the requested
|
||||||
|
size is out of [unspecified] bounds for this kernel, the call fails with
|
||||||
|
-EINVAL.
|
||||||
|
|
||||||
|
MON_IOCQ_RING_SIZE, defined as _IO(MON_IOC_MAGIC, 5)
|
||||||
|
|
||||||
|
This call returns the current size of the buffer in bytes.
|
||||||
|
|
||||||
|
MON_IOCX_GET, defined as _IOW(MON_IOC_MAGIC, 6, struct mon_get_arg)
|
||||||
|
|
||||||
|
This call waits for events to arrive if none were in the kernel buffer,
|
||||||
|
then returns the first event. Its argument is a pointer to the following
|
||||||
|
structure:
|
||||||
|
|
||||||
|
struct mon_get_arg {
|
||||||
|
struct usbmon_packet *hdr;
|
||||||
|
void *data;
|
||||||
|
size_t alloc; /* Length of data (can be zero) */
|
||||||
|
};
|
||||||
|
|
||||||
|
Before the call, hdr, data, and alloc should be filled. Upon return, the area
|
||||||
|
pointed by hdr contains the next event structure, and the data buffer contains
|
||||||
|
the data, if any. The event is removed from the kernel buffer.
|
||||||
|
|
||||||
|
MON_IOCX_MFETCH, defined as _IOWR(MON_IOC_MAGIC, 7, struct mon_mfetch_arg)
|
||||||
|
|
||||||
|
This ioctl is primarily used when the application accesses the buffer
|
||||||
|
with mmap(2). Its argument is a pointer to the following structure:
|
||||||
|
|
||||||
|
struct mon_mfetch_arg {
|
||||||
|
uint32_t *offvec; /* Vector of events fetched */
|
||||||
|
uint32_t nfetch; /* Number of events to fetch (out: fetched) */
|
||||||
|
uint32_t nflush; /* Number of events to flush */
|
||||||
|
};
|
||||||
|
|
||||||
|
The ioctl operates in 3 stages.
|
||||||
|
|
||||||
|
First, it removes and discards up to nflush events from the kernel buffer.
|
||||||
|
The actual number of events discarded is returned in nflush.
|
||||||
|
|
||||||
|
Second, it waits for an event to be present in the buffer, unless the pseudo-
|
||||||
|
device is open with O_NONBLOCK.
|
||||||
|
|
||||||
|
Third, it extracts up to nfetch offsets into the mmap buffer, and stores
|
||||||
|
them into the offvec. The actual number of event offsets is stored into
|
||||||
|
the nfetch.
|
||||||
|
|
||||||
|
MON_IOCH_MFLUSH, defined as _IO(MON_IOC_MAGIC, 8)
|
||||||
|
|
||||||
|
This call removes a number of events from the kernel buffer. Its argument
|
||||||
|
is the number of events to remove. If the buffer contains fewer events
|
||||||
|
than requested, all events present are removed, and no error is reported.
|
||||||
|
This works when no events are available too.
|
||||||
|
|
||||||
|
FIONBIO
|
||||||
|
|
||||||
|
The ioctl FIONBIO may be implemented in the future, if there's a need.
|
||||||
|
|
||||||
|
In addition to ioctl(2) and read(2), the special file of binary API can
|
||||||
|
be polled with select(2) and poll(2). But lseek(2) does not work.
|
||||||
|
|
||||||
|
* Memory-mapped access of the kernel buffer for the binary API
|
||||||
|
|
||||||
|
The basic idea is simple:
|
||||||
|
|
||||||
|
To prepare, map the buffer by getting the current size, then using mmap(2).
|
||||||
|
Then, execute a loop similar to the one written in pseudo-code below:
|
||||||
|
|
||||||
|
struct mon_mfetch_arg fetch;
|
||||||
|
struct usbmon_packet *hdr;
|
||||||
|
int nflush = 0;
|
||||||
|
for (;;) {
|
||||||
|
fetch.offvec = vec; // Has N 32-bit words
|
||||||
|
fetch.nfetch = N; // Or less than N
|
||||||
|
fetch.nflush = nflush;
|
||||||
|
ioctl(fd, MON_IOCX_MFETCH, &fetch); // Process errors, too
|
||||||
|
nflush = fetch.nfetch; // This many packets to flush when done
|
||||||
|
for (i = 0; i < nflush; i++) {
|
||||||
|
hdr = (struct ubsmon_packet *) &mmap_area[vec[i]];
|
||||||
|
if (hdr->type == '@') // Filler packet
|
||||||
|
continue;
|
||||||
|
caddr_t data = &mmap_area[vec[i]] + 64;
|
||||||
|
process_packet(hdr, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Thus, the main idea is to execute only one ioctl per N events.
|
||||||
|
|
||||||
|
Although the buffer is circular, the returned headers and data do not cross
|
||||||
|
the end of the buffer, so the above pseudo-code does not need any gathering.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# Makefile for USB Core files and filesystem
|
# Makefile for USB Core files and filesystem
|
||||||
#
|
#
|
||||||
|
|
||||||
usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_dma.o
|
usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_bin.o mon_dma.o
|
||||||
|
|
||||||
# This does not use CONFIG_USB_MON because we want this to use a tristate.
|
# This does not use CONFIG_USB_MON because we want this to use a tristate.
|
||||||
obj-$(CONFIG_USB) += usbmon.o
|
obj-$(CONFIG_USB) += usbmon.o
|
||||||
|
|
1172
drivers/usb/mon/mon_bin.c
Normal file
1172
drivers/usb/mon/mon_bin.c
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -48,6 +48,36 @@ char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len)
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
return 0;
|
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__ */
|
#endif /* __i386__ */
|
||||||
|
|
||||||
#ifndef MON_HAS_UNMAP
|
#ifndef MON_HAS_UNMAP
|
||||||
|
@ -55,4 +85,11 @@ char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len)
|
||||||
{
|
{
|
||||||
return 'D';
|
return 'D';
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
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 */
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <linux/debugfs.h>
|
|
||||||
#include <linux/smp_lock.h>
|
#include <linux/smp_lock.h>
|
||||||
#include <linux/notifier.h>
|
#include <linux/notifier.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
@ -22,11 +21,10 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb);
|
||||||
static void mon_stop(struct mon_bus *mbus);
|
static void mon_stop(struct mon_bus *mbus);
|
||||||
static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
|
static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
|
||||||
static void mon_bus_drop(struct kref *r);
|
static void mon_bus_drop(struct kref *r);
|
||||||
static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus);
|
static void mon_bus_init(struct usb_bus *ubus);
|
||||||
|
|
||||||
DEFINE_MUTEX(mon_lock);
|
DEFINE_MUTEX(mon_lock);
|
||||||
|
|
||||||
static struct dentry *mon_dir; /* /dbg/usbmon */
|
|
||||||
static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */
|
static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -200,7 +198,7 @@ static void mon_stop(struct mon_bus *mbus)
|
||||||
*/
|
*/
|
||||||
static void mon_bus_add(struct usb_bus *ubus)
|
static void mon_bus_add(struct usb_bus *ubus)
|
||||||
{
|
{
|
||||||
mon_bus_init(mon_dir, ubus);
|
mon_bus_init(ubus);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -212,8 +210,8 @@ static void mon_bus_remove(struct usb_bus *ubus)
|
||||||
|
|
||||||
mutex_lock(&mon_lock);
|
mutex_lock(&mon_lock);
|
||||||
list_del(&mbus->bus_link);
|
list_del(&mbus->bus_link);
|
||||||
debugfs_remove(mbus->dent_t);
|
if (mbus->text_inited)
|
||||||
debugfs_remove(mbus->dent_s);
|
mon_text_del(mbus);
|
||||||
|
|
||||||
mon_dissolve(mbus, ubus);
|
mon_dissolve(mbus, ubus);
|
||||||
kref_put(&mbus->ref, mon_bus_drop);
|
kref_put(&mbus->ref, mon_bus_drop);
|
||||||
|
@ -281,13 +279,9 @@ static void mon_bus_drop(struct kref *r)
|
||||||
* - refcount USB bus struct
|
* - refcount USB bus struct
|
||||||
* - link
|
* - link
|
||||||
*/
|
*/
|
||||||
static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
|
static void mon_bus_init(struct usb_bus *ubus)
|
||||||
{
|
{
|
||||||
struct dentry *d;
|
|
||||||
struct mon_bus *mbus;
|
struct mon_bus *mbus;
|
||||||
enum { NAMESZ = 10 };
|
|
||||||
char name[NAMESZ];
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if ((mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL)
|
if ((mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL)
|
||||||
goto err_alloc;
|
goto err_alloc;
|
||||||
|
@ -303,57 +297,54 @@ static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
|
||||||
ubus->mon_bus = mbus;
|
ubus->mon_bus = mbus;
|
||||||
mbus->uses_dma = ubus->uses_dma;
|
mbus->uses_dma = ubus->uses_dma;
|
||||||
|
|
||||||
rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
|
mbus->text_inited = mon_text_add(mbus, ubus);
|
||||||
if (rc <= 0 || rc >= NAMESZ)
|
// mon_bin_add(...)
|
||||||
goto err_print_t;
|
|
||||||
d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_text);
|
|
||||||
if (d == NULL)
|
|
||||||
goto err_create_t;
|
|
||||||
mbus->dent_t = d;
|
|
||||||
|
|
||||||
rc = snprintf(name, NAMESZ, "%ds", ubus->busnum);
|
|
||||||
if (rc <= 0 || rc >= NAMESZ)
|
|
||||||
goto err_print_s;
|
|
||||||
d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_stat);
|
|
||||||
if (d == NULL)
|
|
||||||
goto err_create_s;
|
|
||||||
mbus->dent_s = d;
|
|
||||||
|
|
||||||
mutex_lock(&mon_lock);
|
mutex_lock(&mon_lock);
|
||||||
list_add_tail(&mbus->bus_link, &mon_buses);
|
list_add_tail(&mbus->bus_link, &mon_buses);
|
||||||
mutex_unlock(&mon_lock);
|
mutex_unlock(&mon_lock);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
err_create_s:
|
|
||||||
err_print_s:
|
|
||||||
debugfs_remove(mbus->dent_t);
|
|
||||||
err_create_t:
|
|
||||||
err_print_t:
|
|
||||||
kfree(mbus);
|
|
||||||
err_alloc:
|
err_alloc:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Search a USB bus by number. Notice that USB bus numbers start from one,
|
||||||
|
* which we may later use to identify "all" with zero.
|
||||||
|
*
|
||||||
|
* This function must be called with mon_lock held.
|
||||||
|
*
|
||||||
|
* This is obviously inefficient and may be revised in the future.
|
||||||
|
*/
|
||||||
|
struct mon_bus *mon_bus_lookup(unsigned int num)
|
||||||
|
{
|
||||||
|
struct list_head *p;
|
||||||
|
struct mon_bus *mbus;
|
||||||
|
|
||||||
|
list_for_each (p, &mon_buses) {
|
||||||
|
mbus = list_entry(p, struct mon_bus, bus_link);
|
||||||
|
if (mbus->u_bus->busnum == num) {
|
||||||
|
return mbus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int __init mon_init(void)
|
static int __init mon_init(void)
|
||||||
{
|
{
|
||||||
struct usb_bus *ubus;
|
struct usb_bus *ubus;
|
||||||
struct dentry *mondir;
|
int rc;
|
||||||
|
|
||||||
mondir = debugfs_create_dir("usbmon", NULL);
|
if ((rc = mon_text_init()) != 0)
|
||||||
if (IS_ERR(mondir)) {
|
goto err_text;
|
||||||
printk(KERN_NOTICE TAG ": debugfs is not available\n");
|
if ((rc = mon_bin_init()) != 0)
|
||||||
return -ENODEV;
|
goto err_bin;
|
||||||
}
|
|
||||||
if (mondir == NULL) {
|
|
||||||
printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
mon_dir = mondir;
|
|
||||||
|
|
||||||
if (usb_mon_register(&mon_ops_0) != 0) {
|
if (usb_mon_register(&mon_ops_0) != 0) {
|
||||||
printk(KERN_NOTICE TAG ": unable to register with the core\n");
|
printk(KERN_NOTICE TAG ": unable to register with the core\n");
|
||||||
debugfs_remove(mondir);
|
rc = -ENODEV;
|
||||||
return -ENODEV;
|
goto err_reg;
|
||||||
}
|
}
|
||||||
// MOD_INC_USE_COUNT(which_module?);
|
// MOD_INC_USE_COUNT(which_module?);
|
||||||
|
|
||||||
|
@ -361,10 +352,17 @@ static int __init mon_init(void)
|
||||||
|
|
||||||
mutex_lock(&usb_bus_list_lock);
|
mutex_lock(&usb_bus_list_lock);
|
||||||
list_for_each_entry (ubus, &usb_bus_list, bus_list) {
|
list_for_each_entry (ubus, &usb_bus_list, bus_list) {
|
||||||
mon_bus_init(mondir, ubus);
|
mon_bus_init(ubus);
|
||||||
}
|
}
|
||||||
mutex_unlock(&usb_bus_list_lock);
|
mutex_unlock(&usb_bus_list_lock);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_reg:
|
||||||
|
mon_bin_exit();
|
||||||
|
err_bin:
|
||||||
|
mon_text_exit();
|
||||||
|
err_text:
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit mon_exit(void)
|
static void __exit mon_exit(void)
|
||||||
|
@ -381,8 +379,8 @@ static void __exit mon_exit(void)
|
||||||
mbus = list_entry(p, struct mon_bus, bus_link);
|
mbus = list_entry(p, struct mon_bus, bus_link);
|
||||||
list_del(p);
|
list_del(p);
|
||||||
|
|
||||||
debugfs_remove(mbus->dent_t);
|
if (mbus->text_inited)
|
||||||
debugfs_remove(mbus->dent_s);
|
mon_text_del(mbus);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This never happens, because the open/close paths in
|
* This never happens, because the open/close paths in
|
||||||
|
@ -401,7 +399,8 @@ static void __exit mon_exit(void)
|
||||||
}
|
}
|
||||||
mutex_unlock(&mon_lock);
|
mutex_unlock(&mon_lock);
|
||||||
|
|
||||||
debugfs_remove(mon_dir);
|
mon_text_exit();
|
||||||
|
mon_bin_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(mon_init);
|
module_init(mon_init);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <linux/time.h>
|
#include <linux/time.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/debugfs.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
|
|
||||||
#include "usb_mon.h"
|
#include "usb_mon.h"
|
||||||
|
@ -63,6 +64,8 @@ struct mon_reader_text {
|
||||||
char slab_name[SLAB_NAME_SZ];
|
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);
|
static void mon_text_ctor(void *, struct kmem_cache *, unsigned long);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -436,7 +439,7 @@ static int mon_text_release(struct inode *inode, struct file *file)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct file_operations mon_fops_text = {
|
static const struct file_operations mon_fops_text = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.open = mon_text_open,
|
.open = mon_text_open,
|
||||||
.llseek = no_llseek,
|
.llseek = no_llseek,
|
||||||
|
@ -447,6 +450,47 @@ const struct file_operations mon_fops_text = {
|
||||||
.release = mon_text_release,
|
.release = mon_text_release,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
|
||||||
|
{
|
||||||
|
struct dentry *d;
|
||||||
|
enum { NAMESZ = 10 };
|
||||||
|
char name[NAMESZ];
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
|
||||||
|
if (rc <= 0 || rc >= NAMESZ)
|
||||||
|
goto err_print_t;
|
||||||
|
d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text);
|
||||||
|
if (d == NULL)
|
||||||
|
goto err_create_t;
|
||||||
|
mbus->dent_t = d;
|
||||||
|
|
||||||
|
/* XXX The stats do not belong to here (text API), but oh well... */
|
||||||
|
rc = snprintf(name, NAMESZ, "%ds", ubus->busnum);
|
||||||
|
if (rc <= 0 || rc >= NAMESZ)
|
||||||
|
goto err_print_s;
|
||||||
|
d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat);
|
||||||
|
if (d == NULL)
|
||||||
|
goto err_create_s;
|
||||||
|
mbus->dent_s = d;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
err_create_s:
|
||||||
|
err_print_s:
|
||||||
|
debugfs_remove(mbus->dent_t);
|
||||||
|
mbus->dent_t = NULL;
|
||||||
|
err_create_t:
|
||||||
|
err_print_t:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mon_text_del(struct mon_bus *mbus)
|
||||||
|
{
|
||||||
|
debugfs_remove(mbus->dent_t);
|
||||||
|
debugfs_remove(mbus->dent_s);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Slab interface: constructor.
|
* Slab interface: constructor.
|
||||||
*/
|
*/
|
||||||
|
@ -459,3 +503,24 @@ static void mon_text_ctor(void *mem, struct kmem_cache *slab, unsigned long sfla
|
||||||
memset(mem, 0xe5, sizeof(struct mon_event_text));
|
memset(mem, 0xe5, sizeof(struct mon_event_text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int __init mon_text_init(void)
|
||||||
|
{
|
||||||
|
struct dentry *mondir;
|
||||||
|
|
||||||
|
mondir = debugfs_create_dir("usbmon", NULL);
|
||||||
|
if (IS_ERR(mondir)) {
|
||||||
|
printk(KERN_NOTICE TAG ": debugfs is not available\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
if (mondir == NULL) {
|
||||||
|
printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
mon_dir = mondir;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __exit mon_text_exit(void)
|
||||||
|
{
|
||||||
|
debugfs_remove(mon_dir);
|
||||||
|
}
|
||||||
|
|
|
@ -17,9 +17,11 @@
|
||||||
struct mon_bus {
|
struct mon_bus {
|
||||||
struct list_head bus_link;
|
struct list_head bus_link;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
struct usb_bus *u_bus;
|
||||||
|
|
||||||
|
int text_inited;
|
||||||
struct dentry *dent_s; /* Debugging file */
|
struct dentry *dent_s; /* Debugging file */
|
||||||
struct dentry *dent_t; /* Text interface file */
|
struct dentry *dent_t; /* Text interface file */
|
||||||
struct usb_bus *u_bus;
|
|
||||||
int uses_dma;
|
int uses_dma;
|
||||||
|
|
||||||
/* Ref */
|
/* Ref */
|
||||||
|
@ -48,13 +50,35 @@ struct mon_reader {
|
||||||
void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
|
void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
|
||||||
void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
|
void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
|
||||||
|
|
||||||
|
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 __init mon_text_init(void);
|
||||||
|
void __exit mon_text_exit(void);
|
||||||
|
int __init mon_bin_init(void);
|
||||||
|
void __exit 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);
|
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 struct mutex mon_lock;
|
||||||
|
|
||||||
extern const struct file_operations mon_fops_text;
|
|
||||||
extern const struct file_operations mon_fops_stat;
|
extern const struct file_operations mon_fops_stat;
|
||||||
|
|
||||||
#endif /* __USB_MON_H */
|
#endif /* __USB_MON_H */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user