Add a new debufs file parameter that controls the number of segments used when allocating a bulk endpoint ring. Matrix Vision has a libusb program for their USB 2.0 cameras that submits a number of very large transfers. The xHCI driver ends up rejecting some transfers because it doesn't have enough room on the one bulk ring segment. By experimentation, their program works if 20 bulk segments are allocated. Point Grey also has a USB 3.0 webcam that needs more than one bulk segment. We really need to fix this by adding dynamic ring segment allocation, but that code is a bit complex and will take a while to write. To ensure Matrix Vision's libusb program works when their device is connected under an xHCI host, add a debufs file to allow users to increase the number of bulk endpoint ring segments. This is a temporary fix, and should be removed once the xHCI driver has dynamic segment allocation. The userspace program will need to change the number of bulk segments allocated before the user plugs in the webcam. Each xHCI host controller will have one directory in the debugfs usb directory (usually mounted at /sys/kernel/debug/usb/), and the name of that directory will be based on the PCI bus name. The userspace program will need to echo the number of bulk segments to the file, like so: root@xanatos:/sys/kernel/debug/usb# echo 2 > xhci/0000\:05\:00.0/num_bulk_segments You can also cat the file to see the current number of bulk ring segments that the driver is using: root@xanatos:/sys/kernel/debug/usb# cat xhci/0000\:05\:00.0/num_bulk_segments 2 Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx> Cc: Kruno Mrak <kruno.mrak@xxxxxxxxxxxxxxxx> Cc: Tim Vlaar <Tvlaar@xxxxxxxxxx> --- Hi Tim and Kruno, Can you please test out this patch? I wasn't quite sure what the upper limit on bulk ring segments should be. I don't want to allow userspace (even as root) to be able to eat up all of the DMA'able memory, so I arbitrarily picked the limit of 100 bulk segments (which will amount to about 100KB allocated per bulk endpoint). If that limit needs to be something higher let me know. Sarah Sharp drivers/usb/host/xhci-dbg.c | 95 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci-mem.c | 4 ++ drivers/usb/host/xhci.c | 13 +++++- drivers/usb/host/xhci.h | 7 +++ 4 files changed, 118 insertions(+), 1 deletions(-) diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index e9b0f04..737d86b 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -21,6 +21,7 @@ */ #include "xhci.h" +#include <linux/debugfs.h> #define XHCI_INIT_VALUE 0x0 @@ -574,3 +575,97 @@ void xhci_dbg_ctx(struct xhci_hcd *xhci, xhci_dbg_slot_ctx(xhci, ctx); xhci_dbg_ep_ctx(xhci, ctx, last_ep); } + +/********************* Debugfs methods **********************/ +static struct dentry *xhci_debug_root; + +static int xhci_bulk_segments_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int xhci_bulk_segments_close(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t xhci_bulk_segments_read(struct file *file, char __user *user_buf, + size_t len, loff_t *offset) +{ + struct xhci_hcd *xhci; + char buf[50]; + int ret; + + xhci = file->private_data; + ret = sprintf(buf, "%u\n", xhci->num_bulk_segs); + if (ret <= 0) + return ret; + + return simple_read_from_buffer(user_buf, len, offset, buf, ret); +} + +static ssize_t xhci_bulk_segments_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct xhci_hcd *xhci; + char buf[50]; + size_t len; + long long num_bulk_segs = 0; + + xhci = file->private_data; + len = simple_write_to_buffer(buf, 50, ppos, user_buf, count); + buf[len] = '\0'; + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = '\0'; + + num_bulk_segs = simple_strtoll(buf, NULL, 10); + if (num_bulk_segs <= 0 || num_bulk_segs > XHCI_BULK_SEGS_LIMIT) + return -EINVAL; + + xhci->num_bulk_segs = num_bulk_segs; + return len; +} + +static const struct file_operations xhci_bulk_segments_fops = { + .owner = THIS_MODULE, + .open = xhci_bulk_segments_open, + .read = xhci_bulk_segments_read, + .write = xhci_bulk_segments_write, + .release = xhci_bulk_segments_close, + .llseek = noop_llseek, +}; + +int xhci_create_debug_dir(void) { + xhci_debug_root = debugfs_create_dir("xhci", usb_debug_root); + if (!xhci_debug_root) + return -ENOENT; + return 0; +} + +void xhci_remove_debug_dir(void) { + debugfs_remove(xhci_debug_root); +} + +inline void xhci_remove_debug_files(struct xhci_hcd *xhci) +{ + debugfs_remove_recursive(xhci->debug_dir); +} + +inline int xhci_create_debug_files(struct xhci_hcd *xhci) +{ + struct usb_bus *bus = &xhci_to_hcd(xhci)->self; + + xhci->debug_dir = debugfs_create_dir(bus->bus_name, xhci_debug_root); + if (!xhci->debug_dir) + return -ENOMEM; + + if (!debugfs_create_file("num_bulk_segments", S_IRUGO|S_IWUSR, + xhci->debug_dir, xhci, + &xhci_bulk_segments_fops)) { + xhci_remove_debug_files(xhci); + return -ENOMEM; + } + + return 0; +} diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 94a8b28..e7ac4fb 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1316,6 +1316,10 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, if (usb_endpoint_xfer_isoc(&ep->desc)) virt_dev->eps[ep_index].new_ring = xhci_ring_alloc(xhci, 8, true, mem_flags); + else if (usb_endpoint_xfer_bulk(&ep->desc)) + virt_dev->eps[ep_index].new_ring = + xhci_ring_alloc(xhci, xhci->num_bulk_segs, + true, mem_flags); else virt_dev->eps[ep_index].new_ring = xhci_ring_alloc(xhci, 1, true, mem_flags); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 54cb762..a6783de 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -322,6 +322,8 @@ int xhci_init(struct usb_hcd *hcd) } else { xhci_dbg(xhci, "xHCI doesn't need link TRB QUIRK\n"); } + /* Can be changed later via debugfs */ + xhci->num_bulk_segs = 1; retval = xhci_mem_init(xhci, GFP_KERNEL); xhci_dbg(xhci, "Finished xhci_init\n"); @@ -425,6 +427,9 @@ int xhci_run(struct usb_hcd *hcd) if (!usb_hcd_is_primary_hcd(hcd)) return xhci_run_finished(xhci); + if (xhci_create_debug_files(xhci)) + return -ENOMEM; + xhci_dbg(xhci, "xhci_run\n"); /* unregister the legacy interrupt */ if (hcd->irq) @@ -573,6 +578,7 @@ void xhci_stop(struct usb_hcd *hcd) xhci_mem_cleanup(xhci); xhci_dbg(xhci, "xhci_stop completed - status = %x\n", xhci_readl(xhci, &xhci->op_regs->status)); + xhci_remove_debug_files(xhci); } /* @@ -3647,9 +3653,13 @@ MODULE_LICENSE("GPL"); static int __init xhci_hcd_init(void) { -#ifdef CONFIG_PCI int retval = 0; + if (xhci_create_debug_dir()) + return -ENOENT; + +#ifdef CONFIG_PCI + retval = xhci_register_pci(); if (retval < 0) { @@ -3684,5 +3694,6 @@ static void __exit xhci_hcd_cleanup(void) #ifdef CONFIG_PCI xhci_unregister_pci(); #endif + xhci_remove_debug_dir(); } module_exit(xhci_hcd_cleanup); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 35140ac..bc7c09c 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1449,6 +1449,8 @@ struct xhci_hcd { #define XHCI_SW_BW_CHECKING (1 << 8) unsigned int num_active_eps; unsigned int limit_active_eps; +#define XHCI_BULK_SEGS_LIMIT 100 + unsigned int num_bulk_segs; /* There are two roothubs to keep track of bus suspend info for */ struct xhci_bus_state bus_state[2]; /* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */ @@ -1459,6 +1461,7 @@ struct xhci_hcd { /* Array of pointers to USB 2.0 PORTSC registers */ __le32 __iomem **usb2_ports; unsigned int num_usb2_ports; + struct dentry *debug_dir; }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ @@ -1551,6 +1554,10 @@ char *xhci_get_slot_state(struct xhci_hcd *xhci, void xhci_dbg_ep_rings(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, struct xhci_virt_ep *ep); +inline int xhci_create_debug_dir(void); +void xhci_remove_debug_dir(void); +inline int xhci_create_debug_files(struct xhci_hcd *xhci); +inline void xhci_remove_debug_files(struct xhci_hcd *xhci); /* xHCI memory management */ void xhci_mem_cleanup(struct xhci_hcd *xhci); -- 1.7.4.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html