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:

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

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

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;
+
+       /* 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;
+};
 #define DBC_QUEUE_SIZE                 16
 #define DBC_WRITE_BUF_SIZE             8192
 
@@ -151,6 +172,8 @@ struct xhci_dbc {
        struct dbc_ep                   eps[2];
 
        struct dbc_port                 port;
+       /* priv pointer for a function */
+       void                            *func_priv;
 };
 
 #define dbc_bulkout_ctx(d)             \
@@ -200,8 +223,11 @@ void xhci_dbc_tty_unregister_driver(void);
 int xhci_dbc_tty_register_device(struct xhci_hcd *xhci);
 void xhci_dbc_tty_unregister_device(struct xhci_hcd *xhci);
 struct dbc_request *dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags);
+void xhci_dbc_flush_reqests(struct xhci_dbc *dbc);
 void dbc_free_request(struct dbc_ep *dep, struct dbc_request *req);
 int dbc_ep_queue(struct dbc_ep *dep, struct dbc_request *req, gfp_t gfp_flags);
+int xhci_dbc_register_function(struct dbc_function *func);
+void xhci_dbc_unregister_function(void);
 #ifdef CONFIG_PM
 int xhci_dbc_suspend(struct xhci_hcd *xhci);
 int xhci_dbc_resume(struct xhci_hcd *xhci);
-- 
2.21.0

Reply via email to