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:
Родитель
a8ef36bc0a
Коммит
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,
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
#
|
||||
|
||||
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.
|
||||
obj-$(CONFIG_USB) += usbmon.o
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -48,6 +48,36 @@ char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len)
|
|||
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
|
||||
|
@ -55,4 +85,11 @@ char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len)
|
|||
{
|
||||
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/module.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/notifier.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_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
|
||||
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);
|
||||
|
||||
static struct dentry *mon_dir; /* /dbg/usbmon */
|
||||
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)
|
||||
{
|
||||
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);
|
||||
list_del(&mbus->bus_link);
|
||||
debugfs_remove(mbus->dent_t);
|
||||
debugfs_remove(mbus->dent_s);
|
||||
if (mbus->text_inited)
|
||||
mon_text_del(mbus);
|
||||
|
||||
mon_dissolve(mbus, ubus);
|
||||
kref_put(&mbus->ref, mon_bus_drop);
|
||||
|
@ -281,13 +279,9 @@ static void mon_bus_drop(struct kref *r)
|
|||
* - refcount USB bus struct
|
||||
* - 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;
|
||||
enum { NAMESZ = 10 };
|
||||
char name[NAMESZ];
|
||||
int rc;
|
||||
|
||||
if ((mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL)
|
||||
goto err_alloc;
|
||||
|
@ -303,57 +297,54 @@ static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
|
|||
ubus->mon_bus = mbus;
|
||||
mbus->uses_dma = ubus->uses_dma;
|
||||
|
||||
rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
|
||||
if (rc <= 0 || rc >= NAMESZ)
|
||||
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;
|
||||
mbus->text_inited = mon_text_add(mbus, ubus);
|
||||
// mon_bin_add(...)
|
||||
|
||||
mutex_lock(&mon_lock);
|
||||
list_add_tail(&mbus->bus_link, &mon_buses);
|
||||
mutex_unlock(&mon_lock);
|
||||
return;
|
||||
|
||||
err_create_s:
|
||||
err_print_s:
|
||||
debugfs_remove(mbus->dent_t);
|
||||
err_create_t:
|
||||
err_print_t:
|
||||
kfree(mbus);
|
||||
err_alloc:
|
||||
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)
|
||||
{
|
||||
struct usb_bus *ubus;
|
||||
struct dentry *mondir;
|
||||
int rc;
|
||||
|
||||
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;
|
||||
if ((rc = mon_text_init()) != 0)
|
||||
goto err_text;
|
||||
if ((rc = mon_bin_init()) != 0)
|
||||
goto err_bin;
|
||||
|
||||
if (usb_mon_register(&mon_ops_0) != 0) {
|
||||
printk(KERN_NOTICE TAG ": unable to register with the core\n");
|
||||
debugfs_remove(mondir);
|
||||
return -ENODEV;
|
||||
rc = -ENODEV;
|
||||
goto err_reg;
|
||||
}
|
||||
// MOD_INC_USE_COUNT(which_module?);
|
||||
|
||||
|
@ -361,10 +352,17 @@ static int __init mon_init(void)
|
|||
|
||||
mutex_lock(&usb_bus_list_lock);
|
||||
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);
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
mon_bin_exit();
|
||||
err_bin:
|
||||
mon_text_exit();
|
||||
err_text:
|
||||
return rc;
|
||||
}
|
||||
|
||||
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);
|
||||
list_del(p);
|
||||
|
||||
debugfs_remove(mbus->dent_t);
|
||||
debugfs_remove(mbus->dent_s);
|
||||
if (mbus->text_inited)
|
||||
mon_text_del(mbus);
|
||||
|
||||
/*
|
||||
* This never happens, because the open/close paths in
|
||||
|
@ -401,7 +399,8 @@ static void __exit mon_exit(void)
|
|||
}
|
||||
mutex_unlock(&mon_lock);
|
||||
|
||||
debugfs_remove(mon_dir);
|
||||
mon_text_exit();
|
||||
mon_bin_exit();
|
||||
}
|
||||
|
||||
module_init(mon_init);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/usb.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "usb_mon.h"
|
||||
|
@ -63,6 +64,8 @@ struct mon_reader_text {
|
|||
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);
|
||||
|
||||
/*
|
||||
|
@ -436,7 +439,7 @@ static int mon_text_release(struct inode *inode, struct file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations mon_fops_text = {
|
||||
static const struct file_operations mon_fops_text = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = mon_text_open,
|
||||
.llseek = no_llseek,
|
||||
|
@ -447,6 +450,47 @@ const struct file_operations mon_fops_text = {
|
|||
.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.
|
||||
*/
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
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 list_head bus_link;
|
||||
spinlock_t lock;
|
||||
struct usb_bus *u_bus;
|
||||
|
||||
int text_inited;
|
||||
struct dentry *dent_s; /* Debugging file */
|
||||
struct dentry *dent_t; /* Text interface file */
|
||||
struct usb_bus *u_bus;
|
||||
int uses_dma;
|
||||
|
||||
/* Ref */
|
||||
|
@ -48,13 +50,35 @@ struct mon_reader {
|
|||
void mon_reader_add(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);
|
||||
|
||||
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_text;
|
||||
extern const struct file_operations mon_fops_stat;
|
||||
|
||||
#endif /* __USB_MON_H */
|
||||
|
|
Загрузка…
Ссылка в новой задаче