xhci: dbc: Provide sysfs option to configure dbc descriptors
When DbC is enabled the first port on the xHC host acts as a usb device. xHC provides the descriptors automatically when the DbC device is enumerated. Most of the values are hardcoded, but some fields such as idProduct, idVendor, bcdDevice and bInterfaceProtocol can be modified. Add sysfs entries that allow userspace to change these. User can only change them before dbc is enabled, i.e. before writing "enable" to dbc sysfs file as we don't want these values to change while device is connected, or during enumeration. Add documentation for these entries in Documentation/ABI/testing/sysfs-bus-pci-drivers-xhci_hcd Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Link: https://lore.kernel.org/r/20230317154715.535523-9-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
4feb07d0ad
Коммит
edf1664f32
|
@ -23,3 +23,55 @@ Description:
|
|||
Reading this attribute gives the state of the DbC. It
|
||||
can be one of the following states: disabled, enabled,
|
||||
initialized, connected, configured and stalled.
|
||||
|
||||
What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_idVendor
|
||||
Date: March 2023
|
||||
Contact: Mathias Nyman <mathias.nyman@linux.intel.com>
|
||||
Description:
|
||||
This dbc_idVendor attribute lets us change the idVendor field
|
||||
presented in the USB device descriptor by this xhci debug
|
||||
device.
|
||||
Value can only be changed while debug capability (DbC) is in
|
||||
disabled state to prevent USB device descriptor change while
|
||||
connected to a USB host.
|
||||
The default value is 0x1d6b (Linux Foundation).
|
||||
It can be any 16-bit integer.
|
||||
|
||||
What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_idProduct
|
||||
Date: March 2023
|
||||
Contact: Mathias Nyman <mathias.nyman@linux.intel.com>
|
||||
Description:
|
||||
This dbc_idProduct attribute lets us change the idProduct field
|
||||
presented in the USB device descriptor by this xhci debug
|
||||
device.
|
||||
Value can only be changed while debug capability (DbC) is in
|
||||
disabled state to prevent USB device descriptor change while
|
||||
connected to a USB host.
|
||||
The default value is 0x0010. It can be any 16-bit integer.
|
||||
|
||||
What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_bcdDevice
|
||||
Date: March 2023
|
||||
Contact: Mathias Nyman <mathias.nyman@linux.intel.com>
|
||||
Description:
|
||||
This dbc_bcdDevice attribute lets us change the bcdDevice field
|
||||
presented in the USB device descriptor by this xhci debug
|
||||
device.
|
||||
Value can only be changed while debug capability (DbC) is in
|
||||
disabled state to prevent USB device descriptor change while
|
||||
connected to a USB host.
|
||||
The default value is 0x0010. (device rev 0.10)
|
||||
It can be any 16-bit integer.
|
||||
|
||||
What: /sys/bus/pci/drivers/xhci_hcd/.../dbc_bInterfaceProtocol
|
||||
Date: March 2023
|
||||
Contact: Mathias Nyman <mathias.nyman@linux.intel.com>
|
||||
Description:
|
||||
This attribute lets us change the bInterfaceProtocol field
|
||||
presented in the USB Interface descriptor by the xhci debug
|
||||
device.
|
||||
Value can only be changed while debug capability (DbC) is in
|
||||
disabled state to prevent USB descriptor change while
|
||||
connected to a USB host.
|
||||
The default value is 1 (GNU Remote Debug command).
|
||||
Other permissible value is 0 which is for vendor defined debug
|
||||
target.
|
||||
|
|
|
@ -124,10 +124,10 @@ static void xhci_dbc_init_contexts(struct xhci_dbc *dbc, u32 string_length)
|
|||
/* Set DbC context and info registers: */
|
||||
lo_hi_writeq(dbc->ctx->dma, &dbc->regs->dccp);
|
||||
|
||||
dev_info = cpu_to_le32((DBC_VENDOR_ID << 16) | DBC_PROTOCOL);
|
||||
dev_info = (dbc->idVendor << 16) | dbc->bInterfaceProtocol;
|
||||
writel(dev_info, &dbc->regs->devinfo1);
|
||||
|
||||
dev_info = cpu_to_le32((DBC_DEVICE_REV << 16) | DBC_PRODUCT_ID);
|
||||
dev_info = (dbc->bcdDevice << 16) | dbc->idProduct;
|
||||
writel(dev_info, &dbc->regs->devinfo2);
|
||||
}
|
||||
|
||||
|
@ -971,7 +971,186 @@ static ssize_t dbc_store(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static ssize_t dbc_idVendor_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct xhci_dbc *dbc;
|
||||
struct xhci_hcd *xhci;
|
||||
|
||||
xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
dbc = xhci->dbc;
|
||||
|
||||
return sprintf(buf, "%04x\n", dbc->idVendor);
|
||||
}
|
||||
|
||||
static ssize_t dbc_idVendor_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct xhci_dbc *dbc;
|
||||
struct xhci_hcd *xhci;
|
||||
void __iomem *ptr;
|
||||
u16 value;
|
||||
u32 dev_info;
|
||||
|
||||
if (kstrtou16(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
dbc = xhci->dbc;
|
||||
if (dbc->state != DS_DISABLED)
|
||||
return -EBUSY;
|
||||
|
||||
dbc->idVendor = value;
|
||||
ptr = &dbc->regs->devinfo1;
|
||||
dev_info = readl(ptr);
|
||||
dev_info = (dev_info & ~(0xffffu << 16)) | (value << 16);
|
||||
writel(dev_info, ptr);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t dbc_idProduct_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct xhci_dbc *dbc;
|
||||
struct xhci_hcd *xhci;
|
||||
|
||||
xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
dbc = xhci->dbc;
|
||||
|
||||
return sprintf(buf, "%04x\n", dbc->idProduct);
|
||||
}
|
||||
|
||||
static ssize_t dbc_idProduct_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct xhci_dbc *dbc;
|
||||
struct xhci_hcd *xhci;
|
||||
void __iomem *ptr;
|
||||
u32 dev_info;
|
||||
u16 value;
|
||||
|
||||
if (kstrtou16(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
dbc = xhci->dbc;
|
||||
if (dbc->state != DS_DISABLED)
|
||||
return -EBUSY;
|
||||
|
||||
dbc->idProduct = value;
|
||||
ptr = &dbc->regs->devinfo2;
|
||||
dev_info = readl(ptr);
|
||||
dev_info = (dev_info & ~(0xffffu)) | value;
|
||||
writel(dev_info, ptr);
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t dbc_bcdDevice_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct xhci_dbc *dbc;
|
||||
struct xhci_hcd *xhci;
|
||||
|
||||
xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
dbc = xhci->dbc;
|
||||
|
||||
return sprintf(buf, "%04x\n", dbc->bcdDevice);
|
||||
}
|
||||
|
||||
static ssize_t dbc_bcdDevice_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct xhci_dbc *dbc;
|
||||
struct xhci_hcd *xhci;
|
||||
void __iomem *ptr;
|
||||
u32 dev_info;
|
||||
u16 value;
|
||||
|
||||
if (kstrtou16(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
dbc = xhci->dbc;
|
||||
if (dbc->state != DS_DISABLED)
|
||||
return -EBUSY;
|
||||
|
||||
dbc->bcdDevice = value;
|
||||
ptr = &dbc->regs->devinfo2;
|
||||
dev_info = readl(ptr);
|
||||
dev_info = (dev_info & ~(0xffffu << 16)) | (value << 16);
|
||||
writel(dev_info, ptr);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t dbc_bInterfaceProtocol_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct xhci_dbc *dbc;
|
||||
struct xhci_hcd *xhci;
|
||||
|
||||
xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
dbc = xhci->dbc;
|
||||
|
||||
return sprintf(buf, "%02x\n", dbc->bInterfaceProtocol);
|
||||
}
|
||||
|
||||
static ssize_t dbc_bInterfaceProtocol_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
struct xhci_dbc *dbc;
|
||||
struct xhci_hcd *xhci;
|
||||
void __iomem *ptr;
|
||||
u32 dev_info;
|
||||
u8 value;
|
||||
int ret;
|
||||
|
||||
/* bInterfaceProtocol is 8 bit, but xhci only supports values 0 and 1 */
|
||||
ret = kstrtou8(buf, 0, &value);
|
||||
if (ret || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
xhci = hcd_to_xhci(dev_get_drvdata(dev));
|
||||
dbc = xhci->dbc;
|
||||
if (dbc->state != DS_DISABLED)
|
||||
return -EBUSY;
|
||||
|
||||
dbc->bInterfaceProtocol = value;
|
||||
ptr = &dbc->regs->devinfo1;
|
||||
dev_info = readl(ptr);
|
||||
dev_info = (dev_info & ~(0xffu)) | value;
|
||||
writel(dev_info, ptr);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
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_bInterfaceProtocol);
|
||||
|
||||
static struct attribute *dbc_dev_attributes[] = {
|
||||
&dev_attr_dbc.attr,
|
||||
&dev_attr_dbc_idVendor.attr,
|
||||
&dev_attr_dbc_idProduct.attr,
|
||||
&dev_attr_dbc_bcdDevice.attr,
|
||||
&dev_attr_dbc_bInterfaceProtocol.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group dbc_dev_attrib_grp = {
|
||||
.attrs = dbc_dev_attributes,
|
||||
};
|
||||
|
||||
struct xhci_dbc *
|
||||
xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *driver)
|
||||
|
@ -986,6 +1165,10 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *
|
|||
dbc->regs = base;
|
||||
dbc->dev = dev;
|
||||
dbc->driver = driver;
|
||||
dbc->idProduct = DBC_PRODUCT_ID;
|
||||
dbc->idVendor = DBC_VENDOR_ID;
|
||||
dbc->bcdDevice = DBC_DEVICE_REV;
|
||||
dbc->bInterfaceProtocol = DBC_PROTOCOL;
|
||||
|
||||
if (readl(&dbc->regs->control) & DBC_CTRL_DBC_ENABLE)
|
||||
goto err;
|
||||
|
@ -993,7 +1176,7 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver *
|
|||
INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events);
|
||||
spin_lock_init(&dbc->lock);
|
||||
|
||||
ret = device_create_file(dev, &dev_attr_dbc);
|
||||
ret = sysfs_create_group(&dev->kobj, &dbc_dev_attrib_grp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
@ -1012,7 +1195,7 @@ void xhci_dbc_remove(struct xhci_dbc *dbc)
|
|||
xhci_dbc_stop(dbc);
|
||||
|
||||
/* remove sysfs files */
|
||||
device_remove_file(dbc->dev, &dev_attr_dbc);
|
||||
sysfs_remove_group(&dbc->dev->kobj, &dbc_dev_attrib_grp);
|
||||
|
||||
kfree(dbc);
|
||||
}
|
||||
|
|
|
@ -132,6 +132,10 @@ struct xhci_dbc {
|
|||
struct dbc_str_descs *string;
|
||||
dma_addr_t string_dma;
|
||||
size_t string_size;
|
||||
u16 idVendor;
|
||||
u16 idProduct;
|
||||
u16 bcdDevice;
|
||||
u8 bInterfaceProtocol;
|
||||
|
||||
enum dbc_state state;
|
||||
struct delayed_work event_work;
|
||||
|
|
Загрузка…
Ссылка в новой задаче