diff -urp usbutils-0.72/configure.in usbutils-0.72-p3/configure.in --- usbutils-0.72/configure.in 2006-02-18 16:14:41.000000000 -0800 +++ usbutils-0.72-p3/configure.in 2006-11-25 14:37:21.000000000 -0800 @@ -20,7 +20,7 @@ AC_PROG_LN_S AC_FUNC_ALLOCA AC_HEADER_DIRENT AC_HEADER_STDC -AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/ioctl.h sys/param.h unistd.h getopt.h errno.h ]) +AC_CHECK_HEADERS([ctype.h fcntl.h inttypes.h stdlib.h string.h sys/ioctl.h sys/param.h unistd.h getopt.h errno.h ]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST @@ -28,11 +28,12 @@ AC_TYPE_OFF_T AC_TYPE_SIZE_T # Checks for library functions. +# AC_CHECK_LIB makes usbmon to link to libusb, which is unnecesary, but... AC_FUNC_MALLOC AC_FUNC_VPRINTF AC_CHECK_FUNCS([getcwd memset getopt_long strchr strerror strstr strtoul uname]) AC_CHECK_LIB(usb, usb_get_string_simple, , - [AC_MSG_ERROR(get libusb 0.1.8 or newer)]) + [AC_MSG_ERROR(get libusb{-devel} 0.1.8 or newer)]) #AC_ARG_ENABLE(usbmodules, # [ --enable-usbmodules build usbmodules (for Linux 2.4)], diff -urp usbutils-0.72/Makefile.am usbutils-0.72-p3/Makefile.am --- usbutils-0.72/Makefile.am 2006-02-18 15:48:13.000000000 -0800 +++ usbutils-0.72-p3/Makefile.am 2006-11-25 14:12:15.000000000 -0800 @@ -4,16 +4,18 @@ AM_CPPFLAGS = -DUSBIDS_FILE='"@datadir@/ AM_CFLAGS = -Wall -W -Wunused -sbin_PROGRAMS = lsusb +sbin_PROGRAMS = lsusb usbmon lsusb_SOURCES = lsusb.c \ names.c names.h \ devtree.c devtree.h list.h \ usbmisc.c usbmisc.h +usbmon_SOURCES = usbmon.c + data_DATA = usb.ids -man_MANS = lsusb.8 +man_MANS = lsusb.8 usbmon.8 # usbmodules is needed only with 2.4 based hotplugging, and is OBSOLETE diff -urpN usbutils-0.72/usbmon.8 usbutils-0.72-p3/usbmon.8 --- usbutils-0.72/usbmon.8 1969-12-31 16:00:00.000000000 -0800 +++ usbutils-0.72-p3/usbmon.8 2006-11-25 13:56:55.000000000 -0800 @@ -0,0 +1,117 @@ +.TH usbmon 8 "25 January 2005" "usbutils-0.72" "Linux USB Utilities" +.IX usbmon +.SH NAME +usbmon \- monitor USB traffic +.SH SYNOPSIS +.B usbmon +[ +.B -i +.I usb_bus_num +] +.SH DESCRIPTION +.B usbmon +allows to capture USB traffic for analysis in the manner similar to +.BR tcpdump (8) + +To make use of this program, you need to have a Linux kernel +which supports the binary "usbmon" interface +(e.g., Linux kernel 2.6.20 or newer). + +.SH OPTIONS +.TP +.B \-i +Listen on \fIusb_bus_num\fP. If unspecified, +.I usbmon +attempts to listen on the pseudo-bus number zero, +which is supposed to capture all packets on all buses. +However, this functionality is not implemented by any Linux kernel to date. +So, in practical terms, user has to specify the bus number always. +The bus number may be determined by examining the output of lsusb(8). + +.SH OUTPUT FORMAT +.LP +Default output of +.I usbmon +contains one text line per an event. The event corresponds to I/O operations +on the boundary of Host Controller Driver (HCD). +This includes events of the following types: +Submission, +Callback, +Error. +Every line consists +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. +.LP +Here is the list of words, from left to right: +.PP +- URB Tag. This is used to identify URBs is normally a kernel mode address +of the URB structure in hexadecimal. +.PP +- Timestamp in microseconds, a decimal number. The timestamp's resolution +depends on available clock, and so it can be much worse than a microsecond +(if the implementation uses jiffies, for example). +.PP +- Event Type. This type refers to the format of the event, not URB type. +Available types are: S - submission, C - callback, E - submission error. +.PP +- "Pipe". The pipe concept is deprecated. This is a composite word, used to +be derived from information in pipes. It consists of three fields, separated +by colons: URB type and direction, Device address, Endpoint number. +Type and direction are encoded with two bytes in the following manner: +.PP + Ci Co Control input and output + Zi Zo Isochronous input and output + Ii Io Interrupt input and output + Bi Bo Bulk input and output +.PP +Device address and Endpoint number are 3-digit and 2-digit (respectively) +decimal numbers, with leading zeroes. +.PP +- URB Status. In most cases, this field contains a number, sometimes negative, +which represents a "status" field of the URB. This field makes no sense for +submissions, but is present anyway to help scripts with parsing. When an +error occurs, the field contains the error code. In case of a submission of +a Control packet, this field contains a Setup Tag instead of an error code. +It is easy to tell whether the Setup Tag is present because it is never a +number. Thus if scripts find a number in this field, they proceed to read +Data Length. If they find something else, like a letter, they read the setup +packet before reading the Data Length. +.PP +- Setup packet, if present, consists of 5 words: one of each for bmRequestType, +bRequest, wValue, wIndex, wLength, as specified by the USB Specification 2.0. +These words are safe to decode if Setup Tag was 's'. Otherwise, the setup +packet was present, but not captured, and the fields contain filler. +.PP +- Data Length. For submissions, this is the requested length. For callbacks, +this is the actual length. +.PP +- Data tag. The usbmon may not always capture data, even if length is nonzero. +The data words are present only if this tag is '='. +.PP +- Data words follow, in big endian hexadecimal format. Notice that they are +not machine words, but really just a byte stream split into words to make +it easier to read. Thus, the last word may contain from one to four bytes. +The length of collected data is limited and can be less than the data length +report in Data Length word. + +.SH FILES +.TP +.B /proc/devices +This file is read to determine the major of /dev/usbmonN if such node does +not exist in the system. +.TP +.B /dev/usbmonN +The +.I usbmon +attempts to open +.I /dev/usbmon{N}, +where N is the bus number. +If the node does not exist, +.I usbmon +creates it. + +.SH SEE ALSO +.BR lsusb (8) + +.SH AUTHOR +Pete Zaitcev, . diff -urpN usbutils-0.72/usbmon.c usbutils-0.72-p3/usbmon.c --- usbutils-0.72/usbmon.c 1969-12-31 16:00:00.000000000 -0800 +++ usbutils-0.72-p3/usbmon.c 2006-11-25 14:10:29.000000000 -0800 @@ -0,0 +1,349 @@ +/* + * usbmon: Front-end for usbmon + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG "usbmon" + +#ifdef __GNUC__ +#define __unused __attribute__((unused)) +#else +#define __unused /**/ +#endif + +struct usbmon_packet { + uint64_t id; /* URB ID - from submission to callback */ + unsigned char type; /* Same as in text API; extensible. */ + unsigned char xfer_type; /* ISO, Intr, Control, Bulk; 0x80 IN */ + unsigned char epnum; /* Endpoint number */ + unsigned char devnum; /* Device address */ + unsigned short busnum; /* Bus number */ + char flag_setup; + char flag_data; + int64_t ts_sec; /* gettimeofday */ + int32_t ts_usec; /* gettimeofday */ + int status; + unsigned int length; /* Length of data (submitted or actual) */ + unsigned int len_cap; /* Delivered length */ + unsigned char setup[8]; /* Only for Control S-type */ +}; + +#define PIPE_ISOCHRONOUS 0 +#define PIPE_INTERRUPT 1 +#define PIPE_CONTROL 2 +#define PIPE_BULK 3 + +#define MON_IOC_MAGIC 0x92 + +#define MON_IOCQ_URB_LEN _IO(MON_IOC_MAGIC, 1) + +#define MON_IOCX_URB _IOWR(MON_IOC_MAGIC, 2, struct usbmon_packet) + +#define MON_IOCG_STATS _IOR(MON_IOC_MAGIC, 3, struct usbmon_stats) + +#define MON_IOCT_RING_SIZE _IO(MON_IOC_MAGIC, 4) + +#define MON_IOCQ_RING_SIZE _IO(MON_IOC_MAGIC, 5) + +struct usbmon_get_arg { + struct usbmon_packet *hdr; /* Only 48 bytes, not 64. */ + void *data; + size_t alloc; /* Length of data (can be zero) */ +}; + +#define MON_IOCX_GET _IOW(MON_IOC_MAGIC, 6, struct usbmon_get_arg) + +/* + */ +struct params { + int ifnum; /* USB bus number */ + char *devname; /* /dev/usbmonN */ + int data_size; +}; + +void Usage(void); + +void print_urb(struct usbmon_packet *ep, unsigned char *data); +void parse_params(struct params *p, char **argv); +void make_device(const struct params *p); +int find_major(void); +struct params par; + +int main(int argc __unused, char **argv) +{ + int fd; + struct usbmon_packet hdrb; + struct usbmon_get_arg getb; + unsigned char *data_buff; + int rc; + + if (sizeof(struct usbmon_packet) != 48) { + extern void usbmon_packet_size_is_bolixed(void); + usbmon_packet_size_is_bolixed(); /* link-time error */ + } + + parse_params(&par, argv+1); + + /* + * Two reasons to do this: + * 1. Reduce weird error messages. + * 2. If we create device nodes, we want them owned by root. + */ + if (geteuid() != 0) { + fprintf(stderr, TAG ": Must run as root\n"); + exit(1); + } + + if ((fd = open(par.devname, O_RDWR)) == -1) { + if (errno == ENOENT) { + make_device(&par); + if ((fd = open(par.devname, O_RDWR)) == -1) { + fprintf(stderr, TAG ": Can't open %s: %s\n", + par.devname, strerror(errno)); + exit(1); + } + } else { + fprintf(stderr, TAG ": Can't open %s: %s\n", + par.devname, strerror(errno)); + exit(1); + } + } + + rc = ioctl(fd, MON_IOCQ_RING_SIZE, 0); + printf("Ring size: %d\n", rc); + + if ((data_buff = malloc(par.data_size)) == NULL) { + fprintf(stderr, TAG ": No core\n"); + exit(1); + } + + for (;;) { + getb.hdr = &hdrb; + getb.data = data_buff; + getb.alloc = par.data_size; + if ((rc = ioctl(fd, MON_IOCX_GET, &getb)) != 0) { + /* Maybe keep going later, for some errnos. */ + fprintf(stderr, TAG ": No core\n"); + exit(1); + } + + print_urb(&hdrb, data_buff); + } + + // return 0; +} + +void print_urb(struct usbmon_packet *ep, unsigned char *data) +{ + char printf_buf[160]; + int DATA_MAX = 32; /* XXX Needs new printf_buf if changed */ + int cnt, limit; + char *pbuf; + char udir, utype; + int data_len, i; + + cnt = 0; + pbuf = printf_buf; + limit = sizeof(printf_buf); + + udir = ((ep->xfer_type & 0x80) != 0) ? 'i' : 'o'; + switch (ep->xfer_type & 0x3) { + case PIPE_ISOCHRONOUS: utype = 'Z'; break; + case PIPE_INTERRUPT: utype = 'I'; break; + case PIPE_CONTROL: utype = 'C'; break; + default: /* PIPE_BULK */ utype = 'B'; + } + cnt += snprintf(pbuf + cnt, limit - cnt, + "%lx %u %c %c%c:%03u:%02u", + ep->id, + (unsigned int)(ep->ts_sec & 0xFFFF) * 1000000 + ep->ts_usec, + ep->type, + utype, udir, ep->devnum, ep->epnum); + + if (ep->flag_setup == 0) { /* Setup packet is present and captured */ + cnt += snprintf(pbuf + cnt, limit - cnt, + " s %02x %02x %04x %04x %04x", + ep->setup[0], + ep->setup[1], + (ep->setup[3] << 8) | ep->setup[2], + (ep->setup[5] << 8) | ep->setup[4], + (ep->setup[7] << 8) | ep->setup[6]); + } else if (ep->flag_setup != '-') { /* Unable to capture setup packet */ + cnt += snprintf(pbuf + cnt, limit - cnt, + " %c __ __ ____ ____ ____", ep->flag_setup); + } else { /* No setup for this kind of URB */ + cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->status); + } + cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->length); + + if (ep->length > 0) { + if (ep->flag_data == 0) { + cnt += snprintf(pbuf + cnt, limit - cnt, " ="); + if ((data_len = ep->len_cap) >= DATA_MAX) + data_len = DATA_MAX; + for (i = 0; i < data_len; i++) { + if (i % 4 == 0) { + cnt += snprintf(pbuf + cnt, limit - cnt, + " "); + } + cnt += snprintf(pbuf + cnt, limit - cnt, + "%02x", data[i]); + } + cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + } else { + cnt += snprintf(pbuf + cnt, limit - cnt, + " %c\n", ep->flag_data); + } + } else { + cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + } + + printf("%s", printf_buf); +} + +void parse_params(struct params *p, char **argv) +{ + char *arg; + long num; + + memset(p, 0, sizeof(struct params)); + p->data_size = 32; /* Same as text API. */ + + while ((arg = *argv++) != NULL) { + if (arg[0] == '-') { + if (arg[1] == 0) + Usage(); + if (arg[2] != 0) + Usage(); + switch (arg[1]) { + case 'i': + if ((arg = *argv++) == NULL) + Usage(); + if (strncmp(arg, "usb", 3) == 0) + arg += 3; + if (!isdigit(arg[0])) + Usage(); + errno = 0; + num = strtol(arg, NULL, 10); + if (errno != 0) + Usage(); + if (num < 0 || num >= 128) { + fprintf(stderr, TAG ": Bus number %ld" + " is out of bounds\n", num); + exit(2); + } + p->ifnum = num; + break; + default: + Usage(); + } + } else { + Usage(); + } + } + + if (p->ifnum == 0) { + fprintf(stderr, TAG ": Bus Zero is not supported yet\n"); + exit(2); + } + + if (p->devname == NULL) { + if ((p->devname = malloc(100)) == NULL) { + fprintf(stderr, TAG ": No core\n"); + exit(1); + } + snprintf(p->devname, 100, "/dev/usbmon%d", p->ifnum); + } +} + +void make_device(const struct params *p) +{ + int major; + dev_t dev; + + major = find_major(); + dev = makedev(major, p->ifnum); + if (mknod(p->devname, S_IFCHR|S_IRUSR|S_IWUSR, dev) != 0) { + fprintf(stderr, TAG ": Can't make device %s: %s\n", + p->devname, strerror(errno)); + exit(1); + } +} + +int find_major(void) +{ + long num; + FILE *df; + enum { LEN = 50 }; + char buff[LEN], c, *p; + char *major, *mname; + + if ((df = fopen("/proc/devices", "r")) == NULL) { + fprintf(stderr, TAG ": Can't open /proc/devices\n"); + exit(1); + } + num = -1; + while (fgets(buff, LEN, df) != NULL) { + p = buff; + major = NULL; + mname = NULL; + for (p = buff; (c = *p) != 0; p++) { + if (major == NULL) { + if (c != ' ') { + major = p; + } + } else if (mname == NULL) { + if (!isdigit(c) && c != ' ') { + mname = p; + } + } else { + if (c == '\n') { + *p = 0; + break; + } + } + } + if (major != NULL && mname != NULL) { + if (strcmp(mname, "usbmon") == 0) { + errno = 0; + num = strtol(major, NULL, 10); + if (errno != 0) { + fprintf(stderr, TAG ": Syntax error " + "in /proc/devices\n"); + exit(1); + } + break; + } + } + } + fclose(df); + + if (num == -1) { + fprintf(stderr, TAG ": Can't find usbmon in /proc/devices\n"); + exit(1); + } + + if (num <= 0 || num > INT_MAX) { + fprintf(stderr, TAG ": Weird major %ld in /proc/devices\n", + num); + exit(1); + } + + return (int) num; +} + +void Usage(void) +{ + fprintf(stderr, "Usage: usbmon [-i usbN]\n"); + exit(2); +}