On Fri, Jun 07, 2019 at 12:03:02PM +0530, Prabhat Chand Pandey wrote:
> This patch introduces the dbc_function structure as a first step in
> making DbC modular and capable in exposing different user interfaces using
> different "functions", which may implement the callbacks exposed here
> according to the driver's need.
> 
> Only one "function" can be registered at a time.
> The generic way to enable a DbC function would be, using sys-fs:

"sysfs"

> 
> Locate the directory for PCI enumerated XHCI host controller in the
> sysfs path.
> e.g.: cd /sys/bus/pci/devices/0000:00:14.0
> $ echo "enable" > dbc

You are adding a new sysfs file to the pci device?  why?

> This is done AFTER the function is selected at build time. Only 1 function
> can be selected at a time.

Why?

And "dbc" is debug port, right?  Or is it something else?

> Signed-off-by: Rajaram Regupathy <rajaram.regupa...@intel.com>
> Signed-off-by: Abhilash K V <abhilash....@intel.com>
> Signed-off-by: Prabhat Chand Pandey <prabhat.chand.pan...@intel.com>
> Acked-by: Mathias Nyman <mathias.ny...@linux.intel.com>
> ---
>  drivers/usb/host/xhci-dbgcap.c | 159 ++++++++++++++++++++++-----------
>  drivers/usb/host/xhci-dbgcap.h |  32 ++++++-
>  2 files changed, 138 insertions(+), 53 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
> index 52e32644a4b2..96adc53b0fdb 100644
> --- a/drivers/usb/host/xhci-dbgcap.c
> +++ b/drivers/usb/host/xhci-dbgcap.c
> @@ -9,11 +9,14 @@
>  #include <linux/dma-mapping.h>
>  #include <linux/slab.h>
>  #include <linux/nls.h>
> +#include <linux/module.h>
>  
>  #include "xhci.h"
>  #include "xhci-trace.h"
>  #include "xhci-dbgcap.h"
>  
> +static struct dbc_function *dbc_registered_func;
> +
>  static inline void *
>  dbc_dma_alloc_coherent(struct xhci_hcd *xhci, size_t size,
>                      dma_addr_t *dma_handle, gfp_t flags)
> @@ -35,41 +38,42 @@ dbc_dma_free_coherent(struct xhci_hcd *xhci, size_t size,
>                                 size, cpu_addr, dma_handle);
>  }
>  
> -static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings)
> +static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings,
> +                                     struct dbc_function *func)
>  {
>       struct usb_string_descriptor    *s_desc;
>       u32                             string_length;
>  
>       /* Serial string: */
>       s_desc = (struct usb_string_descriptor *)strings->serial;
> -     utf8s_to_utf16s(DBC_STRING_SERIAL, strlen(DBC_STRING_SERIAL),
> +     utf8s_to_utf16s(func->string.serial, strlen(func->string.serial),
>                       UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
>                       DBC_MAX_STRING_LENGTH);
>  
> -     s_desc->bLength         = (strlen(DBC_STRING_SERIAL) + 1) * 2;
> +     s_desc->bLength         = (strlen(func->string.serial) + 1) * 2;
>       s_desc->bDescriptorType = USB_DT_STRING;
>       string_length           = s_desc->bLength;
>       string_length           <<= 8;
>  
>       /* Product string: */
>       s_desc = (struct usb_string_descriptor *)strings->product;
> -     utf8s_to_utf16s(DBC_STRING_PRODUCT, strlen(DBC_STRING_PRODUCT),
> +     utf8s_to_utf16s(func->string.product, strlen(func->string.product),
>                       UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
>                       DBC_MAX_STRING_LENGTH);
>  
> -     s_desc->bLength         = (strlen(DBC_STRING_PRODUCT) + 1) * 2;
> +     s_desc->bLength         = (strlen(func->string.product) + 1) * 2;
>       s_desc->bDescriptorType = USB_DT_STRING;
>       string_length           += s_desc->bLength;
>       string_length           <<= 8;
>  
>       /* Manufacture string: */
>       s_desc = (struct usb_string_descriptor *)strings->manufacturer;
> -     utf8s_to_utf16s(DBC_STRING_MANUFACTURER,
> -                     strlen(DBC_STRING_MANUFACTURER),
> +     utf8s_to_utf16s(func->string.manufacturer,
> +                     strlen(func->string.manufacturer),
>                       UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData,
>                       DBC_MAX_STRING_LENGTH);
>  
> -     s_desc->bLength         = (strlen(DBC_STRING_MANUFACTURER) + 1) * 2;
> +     s_desc->bLength         = (strlen(func->string.manufacturer) + 1) * 2;
>       s_desc->bDescriptorType = USB_DT_STRING;
>       string_length           += s_desc->bLength;
>       string_length           <<= 8;
> @@ -84,7 +88,9 @@ static u32 xhci_dbc_populate_strings(struct dbc_str_descs 
> *strings)
>       return string_length;
>  }
>  
> -static void xhci_dbc_init_contexts(struct xhci_hcd *xhci, u32 string_length)
> +static void xhci_dbc_init_contexts(struct xhci_hcd *xhci,
> +                                     struct dbc_function *func,
> +                                     u32 string_length)
>  {
>       struct xhci_dbc         *dbc;
>       struct dbc_info_context *info;
> @@ -124,10 +130,10 @@ static void xhci_dbc_init_contexts(struct xhci_hcd 
> *xhci, u32 string_length)
>       /* Set DbC context and info registers: */
>       xhci_write_64(xhci, dbc->ctx->dma, &dbc->regs->dccp);
>  
> -     dev_info = cpu_to_le32((DBC_VENDOR_ID << 16) | DBC_PROTOCOL);
> +     dev_info = cpu_to_le32((func->vid << 16) | func->protocol);
>       writel(dev_info, &dbc->regs->devinfo1);
>  
> -     dev_info = cpu_to_le32((DBC_DEVICE_REV << 16) | DBC_PRODUCT_ID);
> +     dev_info = cpu_to_le32((func->device_rev << 16) | func->pid);
>       writel(dev_info, &dbc->regs->devinfo2);
>  }
>  
> @@ -181,11 +187,13 @@ static void xhci_dbc_flush_endpoint_requests(struct 
> dbc_ep *dep)
>               xhci_dbc_flush_single_request(req);
>  }
>  
> -static void xhci_dbc_flush_requests(struct xhci_dbc *dbc)
> +
> +void xhci_dbc_flush_requests(struct xhci_dbc *dbc)
>  {
>       xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_OUT]);
>       xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_IN]);
>  }
> +EXPORT_SYMBOL_GPL(xhci_dbc_flush_requests);
>  
>  struct dbc_request *
>  dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags)
> @@ -205,6 +213,7 @@ dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags)
>  
>       return req;
>  }
> +EXPORT_SYMBOL_GPL(dbc_alloc_request);
>  
>  void
>  dbc_free_request(struct dbc_ep *dep, struct dbc_request *req)
> @@ -213,6 +222,7 @@ dbc_free_request(struct dbc_ep *dep, struct dbc_request 
> *req)
>  
>       kfree(req);
>  }
> +EXPORT_SYMBOL_GPL(dbc_free_request);
>  
>  static void
>  xhci_dbc_queue_trb(struct xhci_ring *ring, u32 field1,
> @@ -344,6 +354,7 @@ int dbc_ep_queue(struct dbc_ep *dep, struct dbc_request 
> *req,
>  
>       return ret;
>  }
> +EXPORT_SYMBOL_GPL(dbc_ep_queue);
>  
>  static inline void xhci_dbc_do_eps_init(struct xhci_hcd *xhci, bool 
> direction)
>  {
> @@ -371,7 +382,9 @@ static void xhci_dbc_eps_exit(struct xhci_hcd *xhci)
>       memset(dbc->eps, 0, sizeof(struct dbc_ep) * ARRAY_SIZE(dbc->eps));
>  }
>  
> -static int xhci_dbc_mem_init(struct xhci_hcd *xhci, gfp_t flags)
> +static int xhci_dbc_mem_init(struct xhci_hcd *xhci,
> +                             struct dbc_function *func,
> +                             gfp_t flags)
>  {
>       int                     ret;
>       dma_addr_t              deq;
> @@ -418,8 +431,8 @@ static int xhci_dbc_mem_init(struct xhci_hcd *xhci, gfp_t 
> flags)
>       xhci_write_64(xhci, deq, &dbc->regs->erdp);
>  
>       /* Setup strings and contexts: */
> -     string_length = xhci_dbc_populate_strings(dbc->string);
> -     xhci_dbc_init_contexts(xhci, string_length);
> +     string_length = xhci_dbc_populate_strings(dbc->string, func);
> +     xhci_dbc_init_contexts(xhci, func, string_length);
>  
>       xhci_dbc_eps_init(xhci);
>       dbc->state = DS_INITIALIZED;
> @@ -478,20 +491,9 @@ static int xhci_do_dbc_start(struct xhci_hcd *xhci)
>       u32                     ctrl;
>       struct xhci_dbc         *dbc = xhci->dbc;
>  
> -     if (dbc->state != DS_DISABLED)
> +     if (dbc->state != DS_INITIALIZED)
>               return -EINVAL;
>  
> -     writel(0, &dbc->regs->control);
> -     ret = xhci_handshake(&dbc->regs->control,
> -                          DBC_CTRL_DBC_ENABLE,
> -                          0, 1000);
> -     if (ret)
> -             return ret;
> -
> -     ret = xhci_dbc_mem_init(xhci, GFP_ATOMIC);
> -     if (ret)
> -             return ret;
> -
>       ctrl = readl(&dbc->regs->control);
>       writel(ctrl | DBC_CTRL_DBC_ENABLE | DBC_CTRL_PORT_ENABLE,
>              &dbc->regs->control);
> @@ -552,9 +554,13 @@ static void xhci_dbc_stop(struct xhci_hcd *xhci)
>  
>       cancel_delayed_work_sync(&dbc->event_work);
>  
> -     if (port->registered)
> -             xhci_dbc_tty_unregister_device(xhci);
> -
> +     if (port->registered &&
> +         dbc_registered_func &&
> +         dbc_registered_func->post_disconnect) {
> +             ret = dbc_registered_func->post_disconnect(dbc);
> +             if (ret)
> +                     xhci_err(xhci, "dbc post_disconnect error %d\n", ret);
> +     }
>       spin_lock_irqsave(&dbc->lock, flags);
>       ret = xhci_do_dbc_stop(xhci);
>       spin_unlock_irqrestore(&dbc->lock, flags);
> @@ -798,16 +804,12 @@ static void xhci_dbc_handle_events(struct work_struct 
> *work)
>  
>       switch (evtr) {
>       case EVT_GSER:
> -             ret = xhci_dbc_tty_register_device(xhci);
> -             if (ret) {
> -                     xhci_err(xhci, "failed to alloc tty device\n");
> -                     break;
> -             }
> -
> -             xhci_info(xhci, "DbC now attached to /dev/ttyDBC0\n");
> +             if (dbc_registered_func->post_config)
> +                     dbc_registered_func->post_config(dbc);
>               break;
>       case EVT_DISC:
> -             xhci_dbc_tty_unregister_device(xhci);
> +             if (dbc_registered_func->post_disconnect)
> +                     ret = dbc_registered_func->post_disconnect(dbc);
>               break;
>       case EVT_DONE:
>               break;
> @@ -912,46 +914,76 @@ static ssize_t dbc_store(struct device *dev,
>                        struct device_attribute *attr,
>                        const char *buf, size_t count)
>  {
> +     struct xhci_dbc         *dbc;
>       struct xhci_hcd         *xhci;
> +     int                     ret = 0;
>  
>       xhci = hcd_to_xhci(dev_get_drvdata(dev));
> +     dbc = xhci->dbc;
>  
> -     if (!strncmp(buf, "enable", 6))
> +     if (!strncmp(buf, "enable", 6) && dbc->state == DS_DISABLED) {
> +             if (!dbc_registered_func)
> +                     return -EINVAL;
> +             if (!try_module_get(dbc_registered_func->owner))
> +                     return -ENODEV;
> +             ret = xhci_dbc_mem_init(dbc->xhci, dbc_registered_func,
> +                                             GFP_ATOMIC);
> +             if (ret)
> +                     goto err;
> +             if (dbc_registered_func->run)
> +                     ret = dbc_registered_func->run(dbc);
> +             if (ret) {
> +                     xhci_dbc_mem_cleanup(xhci);
> +                     dbc->state = DS_DISABLED;
> +                     goto err;
> +             }
>               xhci_dbc_start(xhci);
> -     else if (!strncmp(buf, "disable", 7))
> +     } else if (!strncmp(buf, "disable", 7) && dbc->state != DS_DISABLED) {
> +             if (!dbc_registered_func)
> +                     return -EINVAL;
>               xhci_dbc_stop(xhci);
> -     else
> +             if (dbc_registered_func->stop)
> +                     dbc_registered_func->stop(dbc);
> +             module_put(dbc_registered_func->owner);
> +     } else
>               return -EINVAL;
>  
>       return count;
> +err:
> +     module_put(dbc_registered_func->owner);
> +     return ret;
>  }
>  
>  static DEVICE_ATTR_RW(dbc);
>  
> +static struct attribute *dbc_dev_attributes[] = {
> +     &dev_attr_dbc.attr,
> +     NULL
> +};
> +
> +static const struct attribute_group dbc_dev_attrib_grp = {
> +     .attrs = dbc_dev_attributes,
> +};
> +
> +
>  int xhci_dbc_init(struct xhci_hcd *xhci)
>  {
>       int                     ret;
>       struct device           *dev = xhci_to_hcd(xhci)->self.controller;
>  
>       ret = xhci_do_dbc_init(xhci);
> -     if (ret)
> -             goto init_err3;
> -
> -     ret = xhci_dbc_tty_register_driver(xhci);
>       if (ret)
>               goto init_err2;
>  
> -     ret = device_create_file(dev, &dev_attr_dbc);
> +     ret = sysfs_create_group(&dev->kobj, &dbc_dev_attrib_grp);
>       if (ret)
>               goto init_err1;
>  
>       return 0;
>  
>  init_err1:
> -     xhci_dbc_tty_unregister_driver();
> -init_err2:
>       xhci_do_dbc_exit(xhci);
> -init_err3:
> +init_err2:
>       return ret;
>  }
>  
> @@ -963,11 +995,38 @@ void xhci_dbc_exit(struct xhci_hcd *xhci)
>               return;
>  
>       device_remove_file(dev, &dev_attr_dbc);
> -     xhci_dbc_tty_unregister_driver();
>       xhci_dbc_stop(xhci);
>       xhci_do_dbc_exit(xhci);
>  }
>  
> +static inline int is_invalid(int val)
> +{
> +     return (val <  0 || val > 0xffff);
> +}
> +
> +int xhci_dbc_register_function(struct dbc_function *func)
> +{
> +     if (dbc_registered_func)
> +             return -EBUSY;
> +
> +     if (is_invalid(func->protocol) ||
> +             is_invalid(func->vid) ||
> +             is_invalid(func->pid) ||
> +             is_invalid(func->device_rev))
> +             return -EINVAL;
> +     if (!func->run || !func->stop)
> +             return -EINVAL;
> +     dbc_registered_func = func;
> +     return 0;
> +}
> +EXPORT_SYMBOL_GPL(xhci_dbc_register_function);
> +
> +void xhci_dbc_unregister_function(void)
> +{
> +     dbc_registered_func = NULL;
> +}
> +EXPORT_SYMBOL_GPL(xhci_dbc_unregister_function);
> +
>  #ifdef CONFIG_PM
>  int xhci_dbc_suspend(struct xhci_hcd *xhci)
>  {
> diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
> index ce0c6072bd48..b4d5622a9030 100644
> --- a/drivers/usb/host/xhci-dbgcap.h
> +++ b/drivers/usb/host/xhci-dbgcap.h
> @@ -11,6 +11,7 @@
>  
>  #include <linux/tty.h>
>  #include <linux/kfifo.h>
> +#include <linux/kernel.h>
>  
>  struct dbc_regs {
>       __le32  capability;
> @@ -48,9 +49,9 @@ struct dbc_info_context {
>  
>  #define DBC_MAX_PACKET                       1024
>  #define DBC_MAX_STRING_LENGTH                64
> -#define DBC_STRING_MANUFACTURER              "Linux Foundation"
> -#define DBC_STRING_PRODUCT           "Linux USB Debug Target"
> -#define DBC_STRING_SERIAL            "0001"
> +#define DBC_STR_MANUFACTURER         "Linux Foundation"
> +#define DBC_STR_PRODUCT              "Linux USB Debug Target"
> +#define DBC_STR_SERIAL                       "0001"
>  #define      DBC_CONTEXT_SIZE                64
>  
>  /*
> @@ -75,6 +76,7 @@ struct dbc_str_descs {
>  #define DBC_PRODUCT_ID                       0x0010  /* device 0010 */
>  #define DBC_DEVICE_REV                       0x0010  /* 0.10 */
>  
> +
>  enum dbc_state {
>       DS_DISABLED = 0,
>       DS_INITIALIZED,
> @@ -108,6 +110,25 @@ struct dbc_ep {
>       unsigned                        direction:1;
>  };
>  
> +struct dbc_function {
> +     char                            func_name[32];
> +     /* string descriptors */
> +     struct dbc_str_descs            string;
> +     /* other device or interface descriptors */
> +     u16                             protocol;
> +     u16                             vid;
> +     u16                             pid;
> +     u16                             device_rev;

le16 for all of these?

> +     /* callbacks */
> +     int (*run)(struct xhci_dbc *dbc);
> +     int (*post_config)(struct xhci_dbc *dbc);
> +     int (*post_disconnect)(struct xhci_dbc *dbc);
> +     int (*stop)(struct xhci_dbc *dbc);
> +
> +     /* module owner */
> +     struct module                   *owner;
> +};

Why would a function have a module owner?

What exactly is a "function"?  is it a driver?  If so, why not make it a
real driver, have a real bus, and bind drivers to the devices on the
bus?

thanks,

greg k-h

Reply via email to