USB: usbmon: Add binary API v1
This patch adds an extension to the binary API so it reaches parity with existing text API (so-called "1u"). The extension delivers additional data, such as ISO descriptors and the interrupt interval. Signed-Off-By: Pete Zaitcev <zaitcev@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
1ded7ea47b
commit
471c604daf
2 changed files with 136 additions and 33 deletions
|
@ -229,16 +229,26 @@ struct usbmon_packet {
|
|||
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 */
|
||||
union { /* 40: */
|
||||
unsigned char setup[SETUP_LEN]; /* Only for Control S-type */
|
||||
struct iso_rec { /* Only for ISO */
|
||||
int error_count;
|
||||
int numdesc;
|
||||
} iso;
|
||||
} s;
|
||||
int interval; /* 48: Only for Interrupt and ISO */
|
||||
int start_frame; /* 52: For ISO */
|
||||
unsigned int xfer_flags; /* 56: copy of URB's transfer_flags */
|
||||
unsigned int ndesc; /* 60: Actual number of ISO descriptors */
|
||||
}; /* 64 total length */
|
||||
|
||||
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.
|
||||
with an ioctl(2), or by accessing the buffer with mmap. However, read(2)
|
||||
only returns first 48 bytes for compatibility reasons.
|
||||
|
||||
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.
|
||||
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
|
||||
|
@ -279,9 +289,10 @@ size is out of [unspecified] bounds for this kernel, the call fails with
|
|||
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)
|
||||
MON_IOCX_GETX, defined as _IOW(MON_IOC_MAGIC, 10, 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
|
||||
These calls wait for events to arrive if none were in the kernel buffer,
|
||||
then return the first event. The argument is a pointer to the following
|
||||
structure:
|
||||
|
||||
struct mon_get_arg {
|
||||
|
@ -294,6 +305,8 @@ 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.
|
||||
|
||||
The MON_IOCX_GET copies 48 bytes, MON_IOCX_GETX copies 64 bytes.
|
||||
|
||||
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
|
||||
|
|
|
@ -37,10 +37,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
|
||||
|
||||
/*
|
||||
|
@ -92,7 +95,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 */
|
||||
|
@ -102,7 +127,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) */
|
||||
};
|
||||
|
@ -131,6 +156,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
|
||||
|
||||
|
@ -360,12 +390,8 @@ static inline char mon_bin_get_setup(unsigned char *setupb,
|
|||
const struct urb *urb, char ev_type)
|
||||
{
|
||||
|
||||
if (!usb_endpoint_xfer_control(&urb->ep->desc) || ev_type != 'S')
|
||||
return '-';
|
||||
|
||||
if (urb->setup_packet == NULL)
|
||||
return 'Z';
|
||||
|
||||
memcpy(setupb, urb->setup_packet, SETUP_LEN);
|
||||
return 0;
|
||||
}
|
||||
|
@ -387,6 +413,26 @@ static char mon_bin_get_data(const struct mon_reader_bin *rp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
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, int status)
|
||||
{
|
||||
|
@ -396,6 +442,7 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
|
|||
unsigned int urb_length;
|
||||
unsigned int offset;
|
||||
unsigned int length;
|
||||
unsigned int ndesc, lendesc;
|
||||
unsigned char dir;
|
||||
struct mon_bin_hdr *ep;
|
||||
char data_tag = 0;
|
||||
|
@ -407,6 +454,19 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
|
|||
/*
|
||||
* Find the maximum allowable length, then allocate space.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
ndesc = 0;
|
||||
}
|
||||
lendesc = ndesc*sizeof(struct mon_bin_isodesc);
|
||||
|
||||
urb_length = (ev_type == 'S') ?
|
||||
urb->transfer_buffer_length : urb->actual_length;
|
||||
length = urb_length;
|
||||
|
@ -429,10 +489,12 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
|
|||
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);
|
||||
|
@ -456,9 +518,31 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
|
|||
ep->ts_usec = ts.tv_usec;
|
||||
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' */
|
||||
|
@ -592,7 +676,8 @@ static int mon_bin_open(struct inode *inode, struct file *file)
|
|||
* 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;
|
||||
|
@ -609,7 +694,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;
|
||||
}
|
||||
|
@ -657,6 +742,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;
|
||||
|
@ -674,8 +760,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);
|
||||
|
@ -687,13 +773,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)) {
|
||||
if (rp->b_read >= hdrbytes) {
|
||||
step_len = ep->len_cap;
|
||||
step_len -= rp->b_read - sizeof(struct mon_bin_hdr);
|
||||
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)) {
|
||||
|
@ -709,7 +795,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);
|
||||
|
@ -908,6 +994,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;
|
||||
|
||||
|
@ -917,8 +1004,9 @@ 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;
|
||||
|
||||
|
@ -984,16 +1072,18 @@ static long mon_bin_compat_ioctl(struct file *file,
|
|||
|
||||
switch (cmd) {
|
||||
|
||||
case MON_IOCX_GET32: {
|
||||
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), compat_ptr(getb.data32),
|
||||
getb.alloc32);
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue