On Wed, 2015-10-28 at 16:00 +0800, Lu Baolu wrote:
> The xHCI host exports xHCI-specific extended capabilities utilizing
> a method similar to PCI extended capabilities. In many cases, users
> want to know whether a specific extended capability is supported by
> a host. Unfortunately, currently there's no existing mechanisms in
> the kernel to do this.
> 
> This patch exposes extended capabilities supported by the xHCI host
> via debugfs.
> 
> Signed-off-by: Lu Baolu <baolu...@linux.intel.com>
> ---
>  drivers/usb/host/xhci-dbg.c      | 212 
> +++++++++++++++++++++++++++++++++++++++
>  drivers/usb/host/xhci-ext-caps.h |   9 +-
>  drivers/usb/host/xhci.c          |  27 ++++-
>  drivers/usb/host/xhci.h          |  10 ++
>  4 files changed, 256 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
> index 74c42f7..d3dcfed 100644
> --- a/drivers/usb/host/xhci-dbg.c
> +++ b/drivers/usb/host/xhci-dbg.c
> @@ -20,6 +20,11 @@
>   * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
>   */
>  
> +#include <linux/vmalloc.h>
> +#include <linux/slab.h>
> +#include <linux/debugfs.h>
> +#include <linux/usb.h>
> +
>  #include "xhci.h"
>  
>  #define XHCI_INIT_VALUE 0x0
> @@ -612,3 +617,210 @@ void xhci_dbg_trace(struct xhci_hcd *xhci, void 
> (*trace)(struct va_format *),
>       va_end(args);
>  }
>  EXPORT_SYMBOL_GPL(xhci_dbg_trace);
> +
> +#ifdef CONFIG_DEBUG_FS
> +struct debug_buffer {
> +     ssize_t (*fill_func)(struct debug_buffer *);
> +     struct usb_bus *bus;
> +     struct mutex mutex;
> +     size_t count;
> +     char *output_buf;
> +     size_t alloc_size;
> +};
> +
> +static const char *get_extcap_desc(u32 cap_id)
> +{
> +     switch (cap_id) {
> +     case XHCI_EXT_CAPS_LEGACY:
> +             return "USB Legacy Support";
> +     case XHCI_EXT_CAPS_PROTOCOL:
> +             return "Supported Protocol";
> +     case XHCI_EXT_CAPS_PM:
> +             return "Extended Power Management";
> +     case XHCI_EXT_CAPS_VIRT:
> +             return "I/O Virtualization (xHCI-IOV)";
> +     case XHCI_EXT_CAPS_ROUTE:
> +             return "Message Interrupt";
> +     case XHCI_EXT_CAPS_LOCALMEM:
> +             return "Local Memory";
> +     case XHCI_EXT_CAPS_DEBUG:
> +             return "USB Debug Capability";
> +     default:
> +             if (XHCI_EXT_CAPS_VENDOR(XHCI_EXT_CAPS_ID(cap_id)))
> +                     return "Vendor Defined";
> +             else
> +                     return "Unknown";
> +     }
> +}
> +
> +static ssize_t fill_extcap_buffer(struct debug_buffer *buf)
> +{
> +     __le32 __iomem  *addr;
> +     struct usb_hcd  *hcd;
> +     struct xhci_hcd *xhci;
> +     u32             offset, cap_id;
> +     char            *next;
> +     int             size, temp;
> +     unsigned long   flags;
> +     int             time_to_leave;
> +
> +     hcd = bus_to_hcd(buf->bus);
> +     xhci = hcd_to_xhci(hcd);
> +     next = buf->output_buf;
> +     size = buf->alloc_size;
> +
> +     spin_lock_irqsave(&xhci->lock, flags);
> +
> +     addr = &xhci->cap_regs->hcc_params;
> +     offset = XHCI_HCC_EXT_CAPS(readl(addr));
> +     if (!HCD_HW_ACCESSIBLE(hcd) || !offset) {
> +             size = scnprintf(next, size,
> +                     "bus %s, device %s\n%s\nNo extended capabilities\n",
> +                     hcd->self.controller->bus->name,
> +                     dev_name(hcd->self.controller),
> +                     hcd->product_desc);
> +             goto done;
> +     }
> +
> +     temp = scnprintf(next, size, "@addr(virt)\t\tCAP_ID\tDescription\n");
> +     size -= temp;
> +     next += temp;
> +
> +     addr = &xhci->cap_regs->hc_capbase + offset;
> +     time_to_leave = XHCI_EXT_MAX_CAPID;
> +     while (time_to_leave--) {
> +             cap_id = readl(addr);
> +             temp = scnprintf(next, size, "@%p\t%02x\t%s\n",
> +                     addr, XHCI_EXT_CAPS_ID(cap_id),
> +                     get_extcap_desc(XHCI_EXT_CAPS_ID(cap_id)));
> +             size -= temp;
> +             next += temp;
> +
> +             offset = XHCI_EXT_CAPS_NEXT(cap_id);
> +             if (!offset)
> +                     break;
> +             addr += offset;
> +     }
> +
> +done:
> +     spin_unlock_irqrestore(&xhci->lock, flags);
> +
> +     return buf->alloc_size - size;
> +}
> +
> +static struct debug_buffer *buffer_init(struct usb_bus *bus,
> +                             ssize_t (*fill_func)(struct debug_buffer *))
> +{
> +     struct debug_buffer *buf;
> +
> +     buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL);
> +     if (!buf)
> +             return NULL;
> +
> +     buf->bus = bus;
> +     buf->fill_func = fill_func;
> +     mutex_init(&buf->mutex);
> +
> +     return buf;
> +}
> +
> +static int fill_buffer(struct debug_buffer *buf)
> +{
> +     int ret;
> +
> +     if (buf->output_buf)
> +             return -EINVAL;
> +
> +     buf->alloc_size = PAGE_SIZE;
> +     buf->output_buf = vmalloc(buf->alloc_size);

That really makes no sense. If you allocate exactly
PAGE_SIZE, you should allocate a page.

        Regards
                Oliver


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to