diff options
Diffstat (limited to 'drivers/usb/host/xhci-dbgcap.c')
| -rw-r--r-- | drivers/usb/host/xhci-dbgcap.c | 259 |
1 files changed, 192 insertions, 67 deletions
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 9da4f3b452cb..9fd497e0dc7f 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -29,6 +29,12 @@ #include "xhci-trace.h" #include "xhci-dbgcap.h" +static const struct dbc_str dbc_str_default = { + .manufacturer = "Linux Foundation", + .product = "Linux USB Debug Target", + .serial = "0001", +}; + static void dbc_free_ctx(struct device *dev, struct xhci_container_ctx *ctx) { if (!ctx) @@ -52,55 +58,6 @@ static void dbc_ring_free(struct device *dev, struct xhci_ring *ring) kfree(ring); } -static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings) -{ - 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), - UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData, - DBC_MAX_STRING_LENGTH); - - s_desc->bLength = (strlen(DBC_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), - UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData, - DBC_MAX_STRING_LENGTH); - - s_desc->bLength = (strlen(DBC_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), - UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData, - DBC_MAX_STRING_LENGTH); - - s_desc->bLength = (strlen(DBC_STRING_MANUFACTURER) + 1) * 2; - s_desc->bDescriptorType = USB_DT_STRING; - string_length += s_desc->bLength; - string_length <<= 8; - - /* String0: */ - strings->string0[0] = 4; - strings->string0[1] = USB_DT_STRING; - strings->string0[2] = 0x09; - strings->string0[3] = 0x04; - string_length += 4; - - return string_length; -} - static void xhci_dbc_init_ep_contexts(struct xhci_dbc *dbc) { struct xhci_ep_ctx *ep_ctx; @@ -124,7 +81,65 @@ static void xhci_dbc_init_ep_contexts(struct xhci_dbc *dbc) ep_ctx->deq = cpu_to_le64(deq | dbc->ring_in->cycle_state); } -static void xhci_dbc_init_contexts(struct xhci_dbc *dbc, u32 string_length) +static u8 get_str_desc_len(const char *desc) +{ + return ((struct usb_string_descriptor *)desc)->bLength; +} + +static u32 dbc_prepare_info_context_str_len(struct dbc_str_descs *descs) +{ + u32 len; + + len = get_str_desc_len(descs->serial); + len <<= 8; + len += get_str_desc_len(descs->product); + len <<= 8; + len += get_str_desc_len(descs->manufacturer); + len <<= 8; + len += get_str_desc_len(descs->string0); + + return len; +} + +static int xhci_dbc_populate_str_desc(char *desc, const char *src) +{ + struct usb_string_descriptor *s_desc; + int len; + + s_desc = (struct usb_string_descriptor *)desc; + + /* len holds number of 2 byte UTF-16 characters */ + len = utf8s_to_utf16s(src, strlen(src), UTF16_LITTLE_ENDIAN, + (wchar_t *)s_desc->wData, USB_MAX_STRING_LEN * 2); + if (len < 0) + return len; + + s_desc->bLength = len * 2 + 2; + s_desc->bDescriptorType = USB_DT_STRING; + + return s_desc->bLength; +} + +static void xhci_dbc_populate_str_descs(struct dbc_str_descs *str_descs, + struct dbc_str *str) +{ + /* Serial string: */ + xhci_dbc_populate_str_desc(str_descs->serial, str->serial); + + /* Product string: */ + xhci_dbc_populate_str_desc(str_descs->product, str->product); + + /* Manufacturer string: */ + xhci_dbc_populate_str_desc(str_descs->manufacturer, str->manufacturer); + + /* String0: */ + str_descs->string0[0] = 4; + str_descs->string0[1] = USB_DT_STRING; + str_descs->string0[2] = 0x09; + str_descs->string0[3] = 0x04; +} + +static void xhci_dbc_init_contexts(struct xhci_dbc *dbc) { struct dbc_info_context *info; u32 dev_info; @@ -135,12 +150,12 @@ static void xhci_dbc_init_contexts(struct xhci_dbc *dbc, u32 string_length) /* Populate info Context: */ info = (struct dbc_info_context *)dbc->ctx->bytes; - dma = dbc->string_dma; + dma = dbc->str_descs_dma; info->string0 = cpu_to_le64(dma); - info->manufacturer = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH); - info->product = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 2); - info->serial = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 3); - info->length = cpu_to_le32(string_length); + info->manufacturer = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN); + info->product = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN * 2); + info->serial = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN * 3); + info->length = cpu_to_le32(dbc_prepare_info_context_str_len(dbc->str_descs)); /* Populate bulk in and out endpoint contexts: */ xhci_dbc_init_ep_contexts(dbc); @@ -525,7 +540,6 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags) { int ret; dma_addr_t deq; - u32 string_length; struct device *dev = dbc->dev; /* Allocate various rings for events and transfers: */ @@ -552,11 +566,11 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags) goto ctx_fail; /* Allocate the string table: */ - dbc->string_size = sizeof(*dbc->string); - dbc->string = dma_alloc_coherent(dev, dbc->string_size, - &dbc->string_dma, flags); - if (!dbc->string) - goto string_fail; + dbc->str_descs_size = sizeof(*dbc->str_descs); + dbc->str_descs = dma_alloc_coherent(dev, dbc->str_descs_size, + &dbc->str_descs_dma, flags); + if (!dbc->str_descs) + goto str_descs_fail; /* Setup ERST register: */ writel(dbc->erst.num_entries, &dbc->regs->ersts); @@ -566,16 +580,16 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags) dbc->ring_evt->dequeue); lo_hi_writeq(deq, &dbc->regs->erdp); - /* Setup strings and contexts: */ - string_length = xhci_dbc_populate_strings(dbc->string); - xhci_dbc_init_contexts(dbc, string_length); + /* Setup string descriptors and contexts: */ + xhci_dbc_populate_str_descs(dbc->str_descs, &dbc->str); + xhci_dbc_init_contexts(dbc); xhci_dbc_eps_init(dbc); dbc->state = DS_INITIALIZED; return 0; -string_fail: +str_descs_fail: dbc_free_ctx(dev, dbc->ctx); dbc->ctx = NULL; ctx_fail: @@ -600,8 +614,8 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc) xhci_dbc_eps_exit(dbc); - dma_free_coherent(dbc->dev, dbc->string_size, dbc->string, dbc->string_dma); - dbc->string = NULL; + dma_free_coherent(dbc->dev, dbc->str_descs_size, dbc->str_descs, dbc->str_descs_dma); + dbc->str_descs = NULL; dbc_free_ctx(dbc->dev, dbc->ctx); dbc->ctx = NULL; @@ -1196,6 +1210,108 @@ static ssize_t dbc_bcdDevice_store(struct device *dev, return size; } +static ssize_t dbc_manufacturer_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev)); + struct xhci_dbc *dbc = xhci->dbc; + + return sysfs_emit(buf, "%s\n", dbc->str.manufacturer); +} + +static ssize_t dbc_manufacturer_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev)); + struct xhci_dbc *dbc = xhci->dbc; + size_t len; + + if (dbc->state != DS_DISABLED) + return -EBUSY; + + len = strcspn(buf, "\n"); + if (!len) + return -EINVAL; + + if (len > USB_MAX_STRING_LEN) + return -E2BIG; + + memcpy(dbc->str.manufacturer, buf, len); + dbc->str.manufacturer[len] = '\0'; + + return size; +} + +static ssize_t dbc_product_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev)); + struct xhci_dbc *dbc = xhci->dbc; + + return sysfs_emit(buf, "%s\n", dbc->str.product); +} + +static ssize_t dbc_product_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev)); + struct xhci_dbc *dbc = xhci->dbc; + size_t len; + + if (dbc->state != DS_DISABLED) + return -EBUSY; + + len = strcspn(buf, "\n"); + if (!len) + return -EINVAL; + + if (len > USB_MAX_STRING_LEN) + return -E2BIG; + + memcpy(dbc->str.product, buf, len); + dbc->str.product[len] = '\0'; + + return size; +} + +static ssize_t dbc_serial_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev)); + struct xhci_dbc *dbc = xhci->dbc; + + return sysfs_emit(buf, "%s\n", dbc->str.serial); +} + +static ssize_t dbc_serial_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev)); + struct xhci_dbc *dbc = xhci->dbc; + size_t len; + + if (dbc->state != DS_DISABLED) + return -EBUSY; + + len = strcspn(buf, "\n"); + if (!len) + return -EINVAL; + + if (len > USB_MAX_STRING_LEN) + return -E2BIG; + + memcpy(dbc->str.serial, buf, len); + dbc->str.serial[len] = '\0'; + + return size; +} + static ssize_t dbc_bInterfaceProtocol_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1283,6 +1399,9 @@ static DEVICE_ATTR_RW(dbc); static DEVICE_ATTR_RW(dbc_idVendor); static DEVICE_ATTR_RW(dbc_idProduct); static DEVICE_ATTR_RW(dbc_bcdDevice); +static DEVICE_ATTR_RW(dbc_serial); +static DEVICE_ATTR_RW(dbc_product); +static DEVICE_ATTR_RW(dbc_manufacturer); static DEVICE_ATTR_RW(dbc_bInterfaceProtocol); static DEVICE_ATTR_RW(dbc_poll_interval_ms); @@ -1291,6 +1410,9 @@ static struct attribute *dbc_dev_attrs[] = { &dev_attr_dbc_idVendor.attr, &dev_attr_dbc_idProduct.attr, &dev_attr_dbc_bcdDevice.attr, + &dev_attr_dbc_serial.attr, + &dev_attr_dbc_product.attr, + &dev_attr_dbc_manufacturer.attr, &dev_attr_dbc_bInterfaceProtocol.attr, &dev_attr_dbc_poll_interval_ms.attr, NULL @@ -1316,6 +1438,9 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver * dbc->bInterfaceProtocol = DBC_PROTOCOL; dbc->poll_interval = DBC_POLL_INTERVAL_DEFAULT; + /* initialize serial, product and manufacturer with default values */ + dbc->str = dbc_str_default; + if (readl(&dbc->regs->control) & DBC_CTRL_DBC_ENABLE) goto err; |
