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:
Pete Zaitcev
2009-02-19 22:54:45 -07:00
committed by Greg Kroah-Hartman
parent 1ded7ea47b
commit 471c604daf
2 changed files with 136 additions and 33 deletions

View File

@ -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 @@ 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;
@ -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;
}