Commit b375e116 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: add scatter-gather support to usbmon

This patch (as1301) adds support to usbmon for scatter-gather URBs.
The text interface looks at only the first scatterlist element, since
it never copies more than 32 bytes of data anyway.  The binary
interface copies as much data as possible up to the first
non-addressable buffer.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
CC: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 40f8db8f
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/smp_lock.h> #include <linux/smp_lock.h>
#include <linux/scatterlist.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -221,7 +222,7 @@ static void mon_free_buff(struct mon_pgmap *map, int npages); ...@@ -221,7 +222,7 @@ static void mon_free_buff(struct mon_pgmap *map, int npages);
/* /*
* This is a "chunked memcpy". It does not manipulate any counters. * This is a "chunked memcpy". It does not manipulate any counters.
*/ */
static void 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 off, const unsigned char *from, unsigned int length)
{ {
unsigned int step_len; unsigned int step_len;
...@@ -246,6 +247,7 @@ static void mon_copy_to_buff(const struct mon_reader_bin *this, ...@@ -246,6 +247,7 @@ static void mon_copy_to_buff(const struct mon_reader_bin *this,
from += step_len; from += step_len;
length -= step_len; length -= step_len;
} }
return off;
} }
/* /*
...@@ -394,14 +396,44 @@ static inline char mon_bin_get_setup(unsigned char *setupb, ...@@ -394,14 +396,44 @@ static inline char mon_bin_get_setup(unsigned char *setupb,
return 0; return 0;
} }
static char mon_bin_get_data(const struct mon_reader_bin *rp, static unsigned int mon_bin_get_data(const struct mon_reader_bin *rp,
unsigned int offset, struct urb *urb, unsigned int length) 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_buffer == NULL) } else {
return 'Z'; /* If IOMMU coalescing occurred, we cannot trust sg_page */
mon_copy_to_buff(rp, offset, urb->transfer_buffer, length); if (urb->sg->nents != urb->num_sgs) {
return 0; *flag = 'D';
return length;
}
/* Copy up to the first non-addressable segment */
for_each_sg(urb->sg->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';
}
return length;
} }
static void mon_bin_get_isodesc(const struct mon_reader_bin *rp, static void mon_bin_get_isodesc(const struct mon_reader_bin *rp,
...@@ -536,8 +568,9 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, ...@@ -536,8 +568,9 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
} }
if (length != 0) { if (length != 0) {
ep->flag_data = mon_bin_get_data(rp, offset, urb, length); length = mon_bin_get_data(rp, offset, urb, length,
if (ep->flag_data != 0) { /* Yes, it's 0x00, not '0' */ &ep->flag_data);
if (length > 0) {
delta = (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1); delta = (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1);
ep->len_cap -= length; ep->len_cap -= length;
delta -= (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1); delta -= (ep->len_cap + PKT_ALIGN-1) & ~(PKT_ALIGN-1);
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/time.h> #include <linux/time.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/scatterlist.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "usb_mon.h" #include "usb_mon.h"
...@@ -137,6 +138,8 @@ static inline char mon_text_get_setup(struct mon_event_text *ep, ...@@ -137,6 +138,8 @@ 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, 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 len, char ev_type, struct mon_bus *mbus)
{ {
void *src;
if (len <= 0) if (len <= 0)
return 'L'; return 'L';
if (len >= DATA_MAX) if (len >= DATA_MAX)
...@@ -150,10 +153,24 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb, ...@@ -150,10 +153,24 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
return '>'; return '>';
} }
if (urb->transfer_buffer == NULL) if (urb->num_sgs == 0) {
return 'Z'; /* '0' would be not as pretty. */ src = urb->transfer_buffer;
if (src == NULL)
return 'Z'; /* '0' would be not as pretty. */
} else {
struct scatterlist *sg = urb->sg->sg;
/* If IOMMU coalescing occurred, we cannot trust sg_page */
if (urb->sg->nents != urb->num_sgs ||
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; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment