Commit 6f0306d1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'usb-5.9-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB fixes from Greg KH:
 "Let's try this again...  Here are some USB fixes for 5.9-rc3.

  This differs from the previous pull request for this release in that
  the usb gadget patch now does not break some systems, and actually
  does what it was intended to do. Many thanks to Marek Szyprowski for
  quickly noticing and testing the patch from Andy Shevchenko to resolve
  this issue.

  Additionally, some more new USB quirks have been added to get some new
  devices to work properly based on user reports.

  Other than that, the patches are all here, and they contain:

   - usb gadget driver fixes

   - xhci driver fixes

   - typec fixes

   - new quirks and ids

   - fixes for USB patches that went into 5.9-rc1.

  All of these have been tested in linux-next with no reported issues"

* tag 'usb-5.9-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (33 commits)
  usb: storage: Add unusual_uas entry for Sony PSZ drives
  USB: Ignore UAS for JMicron JMS567 ATA/ATAPI Bridge
  usb: host: ohci-exynos: Fix error handling in exynos_ohci_probe()
  USB: gadget: u_f: Unbreak offset calculation in VLAs
  USB: quirks: Ignore duplicate endpoint on Sound Devices MixPre-D
  usb: typec: tcpm: Fix Fix source hard reset response for TDA 2.3.1.1 and TDA 2.3.1.2 failures
  USB: PHY: JZ4770: Fix static checker warning.
  USB: gadget: f_ncm: add bounds checks to ncm_unwrap_ntb()
  USB: gadget: u_f: add overflow checks to VLA macros
  xhci: Always restore EP_SOFT_CLEAR_TOGGLE even if ep reset failed
  xhci: Do warm-reset when both CAS and XDEV_RESUME are set
  usb: host: xhci: fix ep context print mismatch in debugfs
  usb: uas: Add quirk for PNY Pro Elite
  tools: usb: move to tools buildsystem
  USB: Fix device driver race
  USB: Also match device drivers using the ->match vfunc
  usb: host: xhci-tegra: fix tegra_xusb_get_phy()
  usb: host: xhci-tegra: otg usb2/usb3 port init
  usb: hcd: Fix use after free in usb_hcd_pci_remove()
  usb: typec: ucsi: Hold con->lock for the entire duration of ucsi_register_port()
  ...
parents 42df60fc 20934c0d
......@@ -378,21 +378,19 @@ static void acm_ctrl_irq(struct urb *urb)
if (current_size < expected_size) {
/* notification is transmitted fragmented, reassemble */
if (acm->nb_size < expected_size) {
if (acm->nb_size) {
kfree(acm->notification_buffer);
acm->nb_size = 0;
}
u8 *new_buffer;
alloc_size = roundup_pow_of_two(expected_size);
/*
* kmalloc ensures a valid notification_buffer after a
* use of kfree in case the previous allocation was too
* small. Final freeing is done on disconnect.
*/
acm->notification_buffer =
kmalloc(alloc_size, GFP_ATOMIC);
if (!acm->notification_buffer)
/* Final freeing is done on disconnect. */
new_buffer = krealloc(acm->notification_buffer,
alloc_size, GFP_ATOMIC);
if (!new_buffer) {
acm->nb_index = 0;
goto exit;
}
acm->notification_buffer = new_buffer;
acm->nb_size = alloc_size;
dr = (struct usb_cdc_notification *)acm->notification_buffer;
}
copy_size = min(current_size,
......
......@@ -905,6 +905,35 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
static bool is_dev_usb_generic_driver(struct device *dev)
{
struct usb_device_driver *udd = dev->driver ?
to_usb_device_driver(dev->driver) : NULL;
return udd == &usb_generic_driver;
}
static int __usb_bus_reprobe_drivers(struct device *dev, void *data)
{
struct usb_device_driver *new_udriver = data;
struct usb_device *udev;
int ret;
if (!is_dev_usb_generic_driver(dev))
return 0;
udev = to_usb_device(dev);
if (usb_device_match_id(udev, new_udriver->id_table) == NULL &&
(!new_udriver->match || new_udriver->match(udev) != 0))
return 0;
ret = device_reprobe(dev);
if (ret && ret != -EPROBE_DEFER)
dev_err(dev, "Failed to reprobe device (error %d)\n", ret);
return 0;
}
/**
* usb_register_device_driver - register a USB device (not interface) driver
* @new_udriver: USB operations for the device driver
......@@ -934,13 +963,20 @@ int usb_register_device_driver(struct usb_device_driver *new_udriver,
retval = driver_register(&new_udriver->drvwrap.driver);
if (!retval)
if (!retval) {
pr_info("%s: registered new device driver %s\n",
usbcore_name, new_udriver->name);
else
/*
* Check whether any device could be better served with
* this new driver
*/
bus_for_each_dev(&usb_bus_type, NULL, new_udriver,
__usb_bus_reprobe_drivers);
} else {
printk(KERN_ERR "%s: error %d registering device "
" driver %s\n",
usbcore_name, retval, new_udriver->name);
}
return retval;
}
......
......@@ -205,8 +205,9 @@ static int __check_usb_generic(struct device_driver *drv, void *data)
udrv = to_usb_device_driver(drv);
if (udrv == &usb_generic_driver)
return 0;
return usb_device_match_id(udev, udrv->id_table) != NULL;
if (usb_device_match_id(udev, udrv->id_table) != NULL)
return 1;
return (udrv->match && udrv->match(udev));
}
static bool usb_generic_driver_match(struct usb_device *udev)
......
......@@ -315,11 +315,14 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_probe);
void usb_hcd_pci_remove(struct pci_dev *dev)
{
struct usb_hcd *hcd;
int hcd_driver_flags;
hcd = pci_get_drvdata(dev);
if (!hcd)
return;
hcd_driver_flags = hcd->driver->flags;
if (pci_dev_run_wake(dev))
pm_runtime_get_noresume(&dev->dev);
......@@ -347,7 +350,7 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
up_read(&companions_rwsem);
}
usb_put_hcd(hcd);
if ((hcd->driver->flags & HCD_MASK) < HCD_USB3)
if ((hcd_driver_flags & HCD_MASK) < HCD_USB3)
pci_free_irq_vectors(dev);
pci_disable_device(dev);
}
......
......@@ -370,6 +370,10 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0926, 0x0202), .driver_info =
USB_QUIRK_ENDPOINT_IGNORE },
/* Sound Devices MixPre-D */
{ USB_DEVICE(0x0926, 0x0208), .driver_info =
USB_QUIRK_ENDPOINT_IGNORE },
/* Keytouch QWERTY Panel keyboard */
{ USB_DEVICE(0x0926, 0x3333), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
......@@ -465,6 +469,8 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x2386, 0x3119), .driver_info = USB_QUIRK_NO_LPM },
{ USB_DEVICE(0x2386, 0x350e), .driver_info = USB_QUIRK_NO_LPM },
/* DJI CineSSD */
{ USB_DEVICE(0x2ca3, 0x0031), .driver_info = USB_QUIRK_NO_LPM },
......@@ -509,6 +515,7 @@ static const struct usb_device_id usb_amd_resume_quirk_list[] = {
*/
static const struct usb_device_id usb_endpoint_ignore[] = {
{ USB_DEVICE_INTERFACE_NUMBER(0x0926, 0x0202, 1), .driver_info = 0x85 },
{ USB_DEVICE_INTERFACE_NUMBER(0x0926, 0x0208, 1), .driver_info = 0x85 },
{ }
};
......
......@@ -1054,27 +1054,25 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
* dwc3_prepare_one_trb - setup one TRB from one request
* @dep: endpoint for which this request is prepared
* @req: dwc3_request pointer
* @trb_length: buffer size of the TRB
* @chain: should this TRB be chained to the next?
* @node: only for isochronous endpoints. First TRB needs different type.
*/
static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
struct dwc3_request *req, unsigned chain, unsigned node)
struct dwc3_request *req, unsigned int trb_length,
unsigned chain, unsigned node)
{
struct dwc3_trb *trb;
unsigned int length;
dma_addr_t dma;
unsigned stream_id = req->request.stream_id;
unsigned short_not_ok = req->request.short_not_ok;
unsigned no_interrupt = req->request.no_interrupt;
unsigned is_last = req->request.is_last;
if (req->request.num_sgs > 0) {
length = sg_dma_len(req->start_sg);
if (req->request.num_sgs > 0)
dma = sg_dma_address(req->start_sg);
} else {
length = req->request.length;
else
dma = req->request.dma;
}
trb = &dep->trb_pool[dep->trb_enqueue];
......@@ -1086,7 +1084,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
__dwc3_prepare_one_trb(dep, trb, dma, trb_length, chain, node,
stream_id, short_not_ok, no_interrupt, is_last);
}
......@@ -1096,16 +1094,27 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
struct scatterlist *sg = req->start_sg;
struct scatterlist *s;
int i;
unsigned int length = req->request.length;
unsigned int remaining = req->request.num_mapped_sgs
- req->num_queued_sgs;
/*
* If we resume preparing the request, then get the remaining length of
* the request and resume where we left off.
*/
for_each_sg(req->request.sg, s, req->num_queued_sgs, i)
length -= sg_dma_len(s);
for_each_sg(sg, s, remaining, i) {
unsigned int length = req->request.length;
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
unsigned int rem = length % maxp;
unsigned int trb_length;
unsigned chain = true;
trb_length = min_t(unsigned int, length, sg_dma_len(s));
length -= trb_length;
/*
* IOMMU driver is coalescing the list of sgs which shares a
* page boundary into one and giving it to USB driver. With
......@@ -1113,7 +1122,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
* sgs passed. So mark the chain bit to false if it isthe last
* mapped sg.
*/
if (i == remaining - 1)
if ((i == remaining - 1) || !length)
chain = false;
if (rem && usb_endpoint_dir_out(dep->endpoint.desc) && !chain) {
......@@ -1123,7 +1132,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
req->needs_extra_trb = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, i);
dwc3_prepare_one_trb(dep, req, trb_length, true, i);
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
......@@ -1134,8 +1143,39 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
req->request.short_not_ok,
req->request.no_interrupt,
req->request.is_last);
} else if (req->request.zero && req->request.length &&
!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
!rem && !chain) {
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
req->needs_extra_trb = true;
/* Prepare normal TRB */
dwc3_prepare_one_trb(dep, req, trb_length, true, i);
/* Prepare one extra TRB to handle ZLP */
trb = &dep->trb_pool[dep->trb_enqueue];
req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
!req->direction, 1,
req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt,
req->request.is_last);
/* Prepare one more TRB to handle MPS alignment */
if (!req->direction) {
trb = &dep->trb_pool[dep->trb_enqueue];
req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp,
false, 1, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt,
req->request.is_last);
}
} else {
dwc3_prepare_one_trb(dep, req, chain, i);
dwc3_prepare_one_trb(dep, req, trb_length, chain, i);
}
/*
......@@ -1150,6 +1190,16 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
req->num_queued_sgs++;
/*
* The number of pending SG entries may not correspond to the
* number of mapped SG entries. If all the data are queued, then
* don't include unused SG entries.
*/
if (length == 0) {
req->num_pending_sgs -= req->request.num_mapped_sgs - req->num_queued_sgs;
break;
}
if (!dwc3_calc_trbs_left(dep))
break;
}
......@@ -1169,7 +1219,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
req->needs_extra_trb = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, 0);
dwc3_prepare_one_trb(dep, req, length, true, 0);
/* Now prepare one extra TRB to align transfer size */
trb = &dep->trb_pool[dep->trb_enqueue];
......@@ -1180,6 +1230,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
req->request.no_interrupt,
req->request.is_last);
} else if (req->request.zero && req->request.length &&
!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
(IS_ALIGNED(req->request.length, maxp))) {
struct dwc3 *dwc = dep->dwc;
struct dwc3_trb *trb;
......@@ -1187,18 +1238,29 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
req->needs_extra_trb = true;
/* prepare normal TRB */
dwc3_prepare_one_trb(dep, req, true, 0);
dwc3_prepare_one_trb(dep, req, length, true, 0);
/* Now prepare one extra TRB to handle ZLP */
/* Prepare one extra TRB to handle ZLP */
trb = &dep->trb_pool[dep->trb_enqueue];
req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
false, 1, req->request.stream_id,
!req->direction, 1, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt,
req->request.is_last);
/* Prepare one more TRB to handle MPS alignment for OUT */
if (!req->direction) {
trb = &dep->trb_pool[dep->trb_enqueue];
req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp,
false, 1, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt,
req->request.is_last);
}
} else {
dwc3_prepare_one_trb(dep, req, false, 0);
dwc3_prepare_one_trb(dep, req, length, false, 0);
}
}
......@@ -2671,8 +2733,17 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
status);
if (req->needs_extra_trb) {
unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event,
status);
/* Reclaim MPS padding TRB for ZLP */
if (!req->direction && req->request.zero && req->request.length &&
!usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
(IS_ALIGNED(req->request.length, maxp)))
ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, status);
req->needs_extra_trb = false;
}
......
......@@ -1181,12 +1181,15 @@ static int ncm_unwrap_ntb(struct gether *port,
int ndp_index;
unsigned dg_len, dg_len2;
unsigned ndp_len;
unsigned block_len;
struct sk_buff *skb2;
int ret = -EINVAL;
unsigned max_size = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
unsigned ntb_max = le32_to_cpu(ntb_parameters.dwNtbOutMaxSize);
unsigned frame_max = le16_to_cpu(ecm_desc.wMaxSegmentSize);
const struct ndp_parser_opts *opts = ncm->parser_opts;
unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
int dgram_counter;
bool ndp_after_header;
/* dwSignature */
if (get_unaligned_le32(tmp) != opts->nth_sign) {
......@@ -1205,25 +1208,37 @@ static int ncm_unwrap_ntb(struct gether *port,
}
tmp++; /* skip wSequence */
block_len = get_ncm(&tmp, opts->block_length);
/* (d)wBlockLength */
if (get_ncm(&tmp, opts->block_length) > max_size) {
if (block_len > ntb_max) {
INFO(port->func.config->cdev, "OUT size exceeded\n");
goto err;
}
ndp_index = get_ncm(&tmp, opts->ndp_index);
ndp_after_header = false;
/* Run through all the NDP's in the NTB */
do {
/* NCM 3.2 */
if (((ndp_index % 4) != 0) &&
(ndp_index < opts->nth_size)) {
/*
* NCM 3.2
* dwNdpIndex
*/
if (((ndp_index % 4) != 0) ||
(ndp_index < opts->nth_size) ||
(ndp_index > (block_len -
opts->ndp_size))) {
INFO(port->func.config->cdev, "Bad index: %#X\n",
ndp_index);
goto err;
}
if (ndp_index == opts->nth_size)
ndp_after_header = true;
/* walk through NDP */
/*
* walk through NDP
* dwSignature
*/
tmp = (void *)(skb->data + ndp_index);
if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
......@@ -1234,14 +1249,15 @@ static int ncm_unwrap_ntb(struct gether *port,
ndp_len = get_unaligned_le16(tmp++);
/*
* NCM 3.3.1
* wLength
* entry is 2 items
* item size is 16/32 bits, opts->dgram_item_len * 2 bytes
* minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
* Each entry is a dgram index and a dgram length.
*/
if ((ndp_len < opts->ndp_size
+ 2 * 2 * (opts->dgram_item_len * 2))
|| (ndp_len % opts->ndplen_align != 0)) {
+ 2 * 2 * (opts->dgram_item_len * 2)) ||
(ndp_len % opts->ndplen_align != 0)) {
INFO(port->func.config->cdev, "Bad NDP length: %#X\n",
ndp_len);
goto err;
......@@ -1258,8 +1274,21 @@ static int ncm_unwrap_ntb(struct gether *port,
do {
index = index2;
/* wDatagramIndex[0] */
if ((index < opts->nth_size) ||
(index > block_len - opts->dpe_size)) {
INFO(port->func.config->cdev,
"Bad index: %#X\n", index);
goto err;
}
dg_len = dg_len2;
if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */
/*
* wDatagramLength[0]
* ethernet hdr + crc or larger than max frame size
*/
if ((dg_len < 14 + crc_len) ||
(dg_len > frame_max)) {
INFO(port->func.config->cdev,
"Bad dgram length: %#X\n", dg_len);
goto err;
......@@ -1283,6 +1312,37 @@ static int ncm_unwrap_ntb(struct gether *port,
index2 = get_ncm(&tmp, opts->dgram_item_len);
dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
if (index2 == 0 || dg_len2 == 0)
break;
/* wDatagramIndex[1] */
if (ndp_after_header) {
if (index2 < opts->nth_size + opts->ndp_size) {
INFO(port->func.config->cdev,
"Bad index: %#X\n", index2);
goto err;
}
} else {
if (index2 < opts->nth_size + opts->dpe_size) {
INFO(port->func.config->cdev,
"Bad index: %#X\n", index2);
goto err;
}
}
if (index2 > block_len - opts->dpe_size) {
INFO(port->func.config->cdev,
"Bad index: %#X\n", index2);
goto err;
}
/* wDatagramLength[1] */
if ((dg_len2 < 14 + crc_len) ||
(dg_len2 > frame_max)) {
INFO(port->func.config->cdev,
"Bad dgram length: %#X\n", dg_len);
goto err;
}
/*
* Copy the data into a new skb.
* This ensures the truesize is correct
......@@ -1299,9 +1359,6 @@ static int ncm_unwrap_ntb(struct gether *port,
ndp_len -= 2 * (opts->dgram_item_len * 2);
dgram_counter++;
if (index2 == 0 || dg_len2 == 0)
break;
} while (ndp_len > 2 * (opts->dgram_item_len * 2));
} while (ndp_index);
......
......@@ -753,12 +753,13 @@ static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream)
goto err_sts;
return 0;
err_sts:
usb_ep_free_request(fu->ep_status, stream->req_status);
stream->req_status = NULL;
err_out:
usb_ep_free_request(fu->ep_out, stream->req_out);
stream->req_out = NULL;
err_out:
usb_ep_free_request(fu->ep_in, stream->req_in);
stream->req_in = NULL;
out:
return -ENOMEM;
}
......
......@@ -14,6 +14,7 @@
#define __U_F_H__
#include <linux/usb/gadget.h>
#include <linux/overflow.h>
/* Variable Length Array Macros **********************************************/
#define vla_group(groupname) size_t groupname##__next = 0
......@@ -21,21 +22,36 @@
#define vla_item(groupname, type, name, n) \
size_t groupname##_##name##__offset = ({ \
size_t align_mask = __alignof__(type) - 1; \
size_t offset = (groupname##__next + align_mask) & ~align_mask;\
size_t size = (n) * sizeof(type); \
groupname##__next = offset + size; \
size_t offset = 0; \
if (groupname##__next != SIZE_MAX) { \
size_t align_mask = __alignof__(type) - 1; \
size_t size = array_size(n, sizeof(type)); \
offset = (groupname##__next + align_mask) & \
~align_mask; \
if (check_add_overflow(offset, size, \
&groupname##__next)) { \
groupname##__next = SIZE_MAX; \
offset = 0; \
} \
} \
offset; \
})
#define vla_item_with_sz(groupname, type, name, n) \
size_t groupname##_##name##__sz = (n) * sizeof(type); \
size_t groupname##_##name##__offset = ({ \
size_t align_mask = __alignof__(type) - 1; \
size_t offset = (groupname##__next + align_mask) & ~align_mask;\
size_t size = groupname##_##name##__sz; \
groupname##__next = offset + size; \
offset; \
size_t groupname##_##name##__sz = array_size(n, sizeof(type)); \
size_t groupname##_##name##__offset = ({ \
size_t offset = 0; \
if (groupname##__next != SIZE_MAX) { \
size_t align_mask = __alignof__(type) - 1; \
offset = (groupname##__next + align_mask) & \
~align_mask; \
if (check_add_overflow(offset, groupname##_##name##__sz,\
&groupname##__next)) { \
groupname##__next = SIZE_MAX; \
offset = 0; \
} \
} \
offset; \
})
#define vla_ptr(ptr, groupname, name) \
......
......@@ -171,9 +171,8 @@ static int exynos_ohci_probe(struct platform_device *pdev)
hcd->rsrc_len = resource_size(res);
irq = platform_get_irq(pdev, 0);
if (!irq) {
dev_err(&pdev->dev, "Failed to get IRQ\n");
err = -ENODEV;
if (irq < 0) {
err = irq;
goto fail_io;
}
......
......@@ -274,7 +274,7 @@ static int xhci_slot_context_show(struct seq_file *s, void *unused)
static int xhci_endpoint_context_show(struct seq_file *s, void *unused)
{
int dci;
int ep_index;
dma_addr_t dma;
struct xhci_hcd *xhci;
struct xhci_ep_ctx *ep_ctx;
......@@ -283,9 +283,9 @@ static int xhci_endpoint_context_show(struct seq_file *s, void *unused)
xhci = hcd_to_xhci(bus_to_hcd(dev->udev->bus));
for (dci = 1; dci < 32; dci++) {
ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, dci);
dma = dev->out_ctx->dma + dci * CTX_SIZE(xhci->hcc_params);
for (ep_index = 0; ep_index < 31; ep_index++) {
ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
dma = dev->out_ctx->dma + (ep_index + 1) * CTX_SIZE(xhci->hcc_params);
seq_printf(s, "%pad: %s\n", &dma,
xhci_decode_ep_context(le32_to_cpu(ep_ctx->ep_info),
le32_to_cpu(ep_ctx->ep_info2),
......
......@@ -740,15 +740,6 @@ static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci,
{
u32 pls = status_reg & PORT_PLS_MASK;
/* resume state is a xHCI internal state.
* Do not report it to usb core, instead, pretend to be U3,
* thus usb core knows it's not ready for transfer
*/
if (pls == XDEV_RESUME) {
*status |= USB_SS_PORT_LS_U3;
return;
}
/* When the CAS bit is set then warm reset
* should be performed on port
*/
......@@ -770,6 +761,16 @@ static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci,
*/
pls |= USB_PORT_STAT_CONNECTION;
} else {
/*
* Resume state is an xHCI internal state. Do not report it to
* usb core, instead, pretend to be U3, thus usb core knows
* it's not ready for transfer.
*/
if (pls == XDEV_RESUME) {
*status |= USB_SS_PORT_LS_U3;
return;
}
/*
* If CAS bit isn't set but the Port is already at
* Compliance Mode, fake a connection so the USB core
......
......@@ -50,20 +50,6 @@
#define RENESAS_RETRY 10000
#define RENESAS_DELAY 10
#define ROM_VALID_01 0x2013
#define ROM_VALID_02 0x2026
static int renesas_verify_fw_version(struct pci_dev *pdev, u32 version)
{
switch (version) {
case ROM_VALID_01:
case ROM_VALID_02:
return 0;
}
dev_err(&pdev->dev, "FW has invalid version :%d\n", version);
return -EINVAL;
}
static int renesas_fw_download_image(struct pci_dev *dev,
const u32 *fw, size_t step, bool rom)
{
......@@ -202,10 +188,7 @@ static int renesas_check_rom_state(struct pci_dev *pdev)
version &= RENESAS_FW_VERSION_FIELD;
version = version >> RENESAS_FW_VERSION_OFFSET;
err = renesas_verify_fw_version(pdev, version);
if (err)
return err;
dev_dbg(&pdev->dev, "Found ROM version: %x\n", version);
/*
* Test if ROM is present and loaded, if so we can skip everything
......
......@@ -1136,7 +1136,7 @@ static struct phy *tegra_xusb_get_phy(struct tegra_xusb *tegra, char *name,
unsigned int i, phy_count = 0;
for (i = 0; i < tegra->soc->num_types; i++) {
if (!strncmp(tegra->soc->phy_types[i].name, "usb2",
if (!strncmp(tegra->soc->phy_types[i].name, name,
strlen(name)))
return tegra->phys[phy_count+port];
......@@ -1258,6 +1258,8 @@ static int tegra_xusb_init_usb_phy(struct tegra_xusb *tegra)
INIT_WORK(&tegra->id_work, tegra_xhci_id_work);
tegra->id_nb.notifier_call = tegra_xhci_id_notify;
tegra->otg_usb2_port = -EINVAL;
tegra->otg_usb3_port = -EINVAL;
for (i = 0; i < tegra->num_usb_phys; i++) {
struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", i);
......
......@@ -3236,10 +3236,11 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
wait_for_completion(cfg_cmd->completion);
ep->ep_state &= ~EP_SOFT_CLEAR_TOGGLE;
xhci_free_command(xhci, cfg_cmd);
cleanup:
xhci_free_command(xhci, stop_cmd);
if (ep->ep_state & EP_SOFT_CLEAR_TOGGLE)
ep->ep_state &= ~EP_SOFT_CLEAR_TOGGLE;
}
static int xhci_check_streams_endpoint(struct xhci_hcd *xhci,
......
......@@ -426,7 +426,7 @@ static int lvs_rh_probe(struct usb_interface *intf,
USB_DT_SS_HUB_SIZE, USB_CTRL_GET_TIMEOUT);
if (ret < (USB_DT_HUB_NONVAR_SIZE + 2)) {
dev_err(&hdev->dev, "wrong root hub descriptor read %d\n", ret);
return ret;
return ret < 0 ? ret : -EINVAL;
}
/* submit urb to poll interrupt endpoint */
......
......@@ -492,7 +492,7 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer,
prepare_to_wait(&dev->waitq, &wait, TASK_INTERRUPTIBLE);
dev_dbg(&dev->interface->dev, "%s - submit %c\n", __func__,
dev->cntl_buffer[0]);
retval = usb_submit_urb(dev->cntl_urb, GFP_KERNEL);
retval = usb_submit_urb(dev->cntl_urb, GFP_ATOMIC);
if (retval >= 0)
timeout = schedule_timeout(YUREX_WRITE_TIMEOUT);
finish_wait(&dev->waitq, &wait);
......
......@@ -176,6 +176,7 @@ static int ingenic_usb_phy_init(struct usb_phy *phy)
/* Wait for PHY to reset */
usleep_range(30, 300);
reg = readl(priv->base + REG_USBPCR_OFFSET);
writel(reg & ~USBPCR_POR, priv->base + REG_USBPCR_OFFSET);
usleep_range(300, 1000);
......
......@@ -2328,7 +2328,7 @@ UNUSUAL_DEV( 0x357d, 0x7788, 0x0114, 0x0114,
"JMicron",
"USB to ATA/ATAPI Bridge",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_BROKEN_FUA ),
US_FL_BROKEN_FUA | US_FL_IGNORE_UAS ),
/* Reported by Andrey Rahmatullin <wrar@altlinux.org> */
UNUSUAL_DEV( 0x4102, 0x1020, 0x0100, 0x0100,
......
......@@ -28,6 +28,13 @@
* and don't forget to CC: the USB development list <linux-usb@vger.kernel.org>
*/
/* Reported-by: Till Dörges <doerges@pre-sense.de> */
UNUSUAL_DEV(0x054c, 0x087d, 0x0000, 0x9999,
"Sony",
"PSZ-HA*",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_REPORT_OPCODES),
/* Reported-by: Julian Groß <julian.g@posteo.de> */
UNUSUAL_DEV(0x059f, 0x105f, 0x0000, 0x9999,
"LaCie",
......@@ -80,6 +87,13 @@ UNUSUAL_DEV(0x152d, 0x0578, 0x0000, 0x9999,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_BROKEN_FUA),
/* Reported-by: Thinh Nguyen <thinhn@synopsys.com> */
UNUSUAL_DEV(0x154b, 0xf00d, 0x0000, 0x9999,
"PNY",
"Pro Elite SSD",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_ATA_1X),
/* Reported-by: Hans de Goede <hdegoede@redhat.com> */
UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999,
"VIA",
......
......@@ -3372,13 +3372,31 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, SNK_HARD_RESET_SINK_OFF, 0);
break;
case SRC_HARD_RESET_VBUS_OFF:
tcpm_set_vconn(port, true);
/*
* 7.1.5 Response to Hard Resets
* Hard Reset Signaling indicates a communication failure has occurred and the
* Source Shall stop driving VCONN, Shall remove Rp from the VCONN pin and Shall
* drive VBUS to vSafe0V as shown in Figure 7-9.
*/
tcpm_set_vconn(port, false);
tcpm_set_vbus(port, false);
tcpm_set_roles(port, port->self_powered, TYPEC_SOURCE,
tcpm_data_role_for_source(port));
tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER);
/*
* If tcpc fails to notify vbus off, TCPM will wait for PD_T_SAFE_0V +
* PD_T_SRC_RECOVER before turning vbus back on.
* From Table 7-12 Sequence Description for a Source Initiated Hard Reset:
* 4. Policy Engine waits tPSHardReset after sending Hard Reset Signaling and then
* tells the Device Policy Manager to instruct the power supply to perform a
* Hard Reset. The transition to vSafe0V Shall occur within tSafe0V (t2).
* 5. After tSrcRecover the Source applies power to VBUS in an attempt to
* re-establish communication with the Sink and resume USB Default Operation.
* The transition to vSafe5V Shall occur within tSrcTurnOn(t4).
*/
tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SAFE_0V + PD_T_SRC_RECOVER);
break;
case SRC_HARD_RESET_VBUS_ON:
tcpm_set_vconn(port, true);
tcpm_set_vbus(port, true);
port->tcpc->set_pd_rx(port->tcpc, true);
tcpm_set_attached_state(port, true);
......@@ -3944,7 +3962,11 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port)
tcpm_set_state(port, SNK_HARD_RESET_WAIT_VBUS, 0);
break;
case SRC_HARD_RESET_VBUS_OFF:
tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, 0);
/*
* After establishing the vSafe0V voltage condition on VBUS, the Source Shall wait
* tSrcRecover before re-applying VCONN and restoring VBUS to vSafe5V.
*/
tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER);
break;
case HARD_RESET_SEND:
break;
......
......@@ -288,8 +288,6 @@ struct typec_altmode *ucsi_register_displayport(struct ucsi_connector *con,
struct typec_altmode *alt;
struct ucsi_dp *dp;
mutex_lock(&con->lock);
/* We can't rely on the firmware with the capabilities. */
desc->vdo |= DP_CAP_DP_SIGNALING | DP_CAP_RECEPTACLE;
......@@ -298,15 +296,12 @@ struct typec_altmode *ucsi_register_displayport(struct ucsi_connector *con,
desc->vdo |= all_assignments << 16;
alt = typec_port_register_altmode(con->port, desc);
if (IS_ERR(alt)) {
mutex_unlock(&con->lock);
if (IS_ERR(alt))
return alt;
}
dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL);
if (!dp) {
typec_unregister_altmode(alt);
mutex_unlock(&con->lock);
return ERR_PTR(-ENOMEM);
}
......@@ -319,7 +314,5 @@ struct typec_altmode *ucsi_register_displayport(struct ucsi_connector *con,
alt->ops = &ucsi_displayport_ops;
typec_altmode_set_drvdata(alt, dp);
mutex_unlock(&con->lock);
return alt;
}
......@@ -146,40 +146,33 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
return UCSI_CCI_LENGTH(cci);
}
static int ucsi_run_command(struct ucsi *ucsi, u64 command,
void *data, size_t size)
int ucsi_send_command(struct ucsi *ucsi, u64 command,
void *data, size_t size)
{
u8 length;
int ret;
mutex_lock(&ucsi->ppm_lock);
ret = ucsi_exec_command(ucsi, command);
if (ret < 0)
return ret;
goto out;
length = ret;
if (data) {
ret = ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, data, size);
if (ret)
return ret;
goto out;
}
ret = ucsi_acknowledge_command(ucsi);
if (ret)
return ret;
return length;
}
int ucsi_send_command(struct ucsi *ucsi, u64 command,
void *retval, size_t size)
{
int ret;
goto out;
mutex_lock(&ucsi->ppm_lock);
ret = ucsi_run_command(ucsi, command, retval, size);
ret = length;
out:
mutex_unlock(&ucsi->ppm_lock);
return ret;
}
EXPORT_SYMBOL_GPL(ucsi_send_command);
......@@ -205,7 +198,7 @@ void ucsi_altmode_update_active(struct ucsi_connector *con)
int i;
command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num);
ret = ucsi_run_command(con->ucsi, command, &cur, sizeof(cur));
ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur));
if (ret < 0) {
if (con->ucsi->version > 0x0100) {
dev_err(con->ucsi->dev,
......@@ -354,7 +347,7 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
command |= UCSI_GET_ALTMODE_RECIPIENT(recipient);
command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num);
command |= UCSI_GET_ALTMODE_OFFSET(i);
len = ucsi_run_command(con->ucsi, command, &alt, sizeof(alt));
len = ucsi_send_command(con->ucsi, command, &alt, sizeof(alt));
/*
* We are collecting all altmodes first and then registering.
* Some type-C device will return zero length data beyond last
......@@ -431,7 +424,7 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
command |= UCSI_GET_ALTMODE_RECIPIENT(recipient);
command |= UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num);
command |= UCSI_GET_ALTMODE_OFFSET(i);
len = ucsi_run_command(con->ucsi, command, alt, sizeof(alt));
len = ucsi_send_command(con->ucsi, command, alt, sizeof(alt));
if (len <= 0)
return len;
......@@ -502,7 +495,7 @@ static void ucsi_get_pdos(struct ucsi_connector *con, int is_partner)
command |= UCSI_GET_PDOS_PARTNER_PDO(is_partner);
command |= UCSI_GET_PDOS_NUM_PDOS(UCSI_MAX_PDOS - 1);
command |= UCSI_GET_PDOS_SRC_PDOS;
ret = ucsi_run_command(ucsi, command, con->src_pdos,
ret = ucsi_send_command(ucsi, command, con->src_pdos,
sizeof(con->src_pdos));
if (ret < 0) {
dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret);
......@@ -681,7 +674,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
*/
command = UCSI_GET_CAM_SUPPORTED;
command |= UCSI_CONNECTOR_NUMBER(con->num);
ucsi_run_command(con->ucsi, command, NULL, 0);
ucsi_send_command(con->ucsi, command, NULL, 0);
}
if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE)
......@@ -736,20 +729,24 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
u32 cci;
int ret;
mutex_lock(&ucsi->ppm_lock);
ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command,
sizeof(command));
if (ret < 0)
return ret;
goto out;
tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS);
do {
if (time_is_before_jiffies(tmo))
return -ETIMEDOUT;
if (time_is_before_jiffies(tmo)) {
ret = -ETIMEDOUT;
goto out;
}
ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
if (ret)
return ret;
goto out;
/* If the PPM is still doing something else, reset it again. */
if (cci & ~UCSI_CCI_RESET_COMPLETE) {
......@@ -757,13 +754,15 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
&command,
sizeof(command));
if (ret < 0)
return ret;
goto out;
}
msleep(20);
} while (!(cci & UCSI_CCI_RESET_COMPLETE));
return 0;
out:
mutex_unlock(&ucsi->ppm_lock);
return ret;
}
static int ucsi_role_cmd(struct ucsi_connector *con, u64 command)
......@@ -775,9 +774,7 @@ static int ucsi_role_cmd(struct ucsi_connector *con, u64 command)
u64 c;
/* PPM most likely stopped responding. Resetting everything. */
mutex_lock(&con->ucsi->ppm_lock);
ucsi_reset_ppm(con->ucsi);
mutex_unlock(&con->ucsi->ppm_lock);
c = UCSI_SET_NOTIFICATION_ENABLE | con->ucsi->ntfy;
ucsi_send_command(con->ucsi, c, NULL, 0);
......@@ -901,12 +898,15 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
con->num = index + 1;
con->ucsi = ucsi;
/* Delay other interactions with the con until registration is complete */
mutex_lock(&con->lock);
/* Get connector capability */
command = UCSI_GET_CONNECTOR_CAPABILITY;
command |= UCSI_CONNECTOR_NUMBER(con->num);
ret = ucsi_run_command(ucsi, command, &con->cap, sizeof(con->cap));
ret = ucsi_send_command(ucsi, command, &con->cap, sizeof(con->cap));
if (ret < 0)
return ret;
goto out;
if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DRP)
cap->data = TYPEC_PORT_DRD;
......@@ -938,27 +938,32 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
ret = ucsi_register_port_psy(con);
if (ret)
return ret;
goto out;
/* Register the connector */
con->port = typec_register_port(ucsi->dev, cap);
if (IS_ERR(con->port))
return PTR_ERR(con->port);
if (IS_ERR(con->port)) {
ret = PTR_ERR(con->port);
goto out;
}
/* Alternate modes */
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON);
if (ret)
if (ret) {
dev_err(ucsi->dev, "con%d: failed to register alt modes\n",
con->num);
goto out;
}
/* Get the status */
command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num);
ret = ucsi_run_command(ucsi, command, &con->status,
sizeof(con->status));
ret = ucsi_send_command(ucsi, command, &con->status, sizeof(con->status));
if (ret < 0) {
dev_err(ucsi->dev, "con%d: failed to get status\n", con->num);
return 0;
ret = 0;
goto out;
}
ret = 0; /* ucsi_send_command() returns length on success */
switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
case UCSI_CONSTAT_PARTNER_TYPE_UFP:
......@@ -983,17 +988,21 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
if (con->partner) {
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP);
if (ret)
if (ret) {
dev_err(ucsi->dev,
"con%d: failed to register alternate modes\n",
con->num);
else
ret = 0;
} else {
ucsi_altmode_update_active(con);
}
}
trace_ucsi_register_port(con->num, &con->status);
return 0;
out:
mutex_unlock(&con->lock);
return ret;
}
/**
......@@ -1009,8 +1018,6 @@ static int ucsi_init(struct ucsi *ucsi)
int ret;
int i;
mutex_lock(&ucsi->ppm_lock);
/* Reset the PPM */
ret = ucsi_reset_ppm(ucsi);
if (ret) {
......@@ -1021,13 +1028,13 @@ static int ucsi_init(struct ucsi *ucsi)
/* Enable basic notifications */
ucsi->ntfy = UCSI_ENABLE_NTFY_CMD_COMPLETE | UCSI_ENABLE_NTFY_ERROR;
command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy;
ret = ucsi_run_command(ucsi, command, NULL, 0);
ret = ucsi_send_command(ucsi, command, NULL, 0);
if (ret < 0)
goto err_reset;
/* Get PPM capabilities */
command = UCSI_GET_CAPABILITY;
ret = ucsi_run_command(ucsi, command, &ucsi->cap, sizeof(ucsi->cap));
ret = ucsi_send_command(ucsi, command, &ucsi->cap, sizeof(ucsi->cap));
if (ret < 0)
goto err_reset;
......@@ -1054,12 +1061,10 @@ static int ucsi_init(struct ucsi *ucsi)
/* Enable all notifications */
ucsi->ntfy = UCSI_ENABLE_NTFY_ALL;
command = UCSI_SET_NOTIFICATION_ENABLE | ucsi->ntfy;
ret = ucsi_run_command(ucsi, command, NULL, 0);
ret = ucsi_send_command(ucsi, command, NULL, 0);
if (ret < 0)
goto err_unregister;
mutex_unlock(&ucsi->ppm_lock);
return 0;
err_unregister:
......@@ -1074,8 +1079,6 @@ static int ucsi_init(struct ucsi *ucsi)
err_reset:
ucsi_reset_ppm(ucsi);
err:
mutex_unlock(&ucsi->ppm_lock);
return ret;
}
......
......@@ -461,6 +461,11 @@ static void stub_disconnect(struct usb_device *udev)
return;
}
static bool usbip_match(struct usb_device *udev)
{
return true;
}
#ifdef CONFIG_PM
/* These functions need usb_port_suspend and usb_port_resume,
......@@ -486,6 +491,7 @@ struct usb_device_driver stub_driver = {
.name = "usbip-host",
.probe = stub_probe,
.disconnect = stub_disconnect,
.match = usbip_match,
#ifdef CONFIG_PM
.suspend = stub_suspend,
.resume = stub_resume,
......
testusb-y += testusb.o
ffs-test-y += ffs-test.o
# SPDX-License-Identifier: GPL-2.0
# Makefile for USB tools
include ../scripts/Makefile.include
PTHREAD_LIBS = -lpthread
WARNINGS = -Wall -Wextra
CFLAGS = $(WARNINGS) -g -I../include
LDFLAGS = $(PTHREAD_LIBS)
bindir ?= /usr/bin
all: testusb ffs-test
%: %.c
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
endif
# Do not use make's built-in rules
# (this improves performance and avoids hard-to-debug behaviour);
MAKEFLAGS += -r
override CFLAGS += -O2 -Wall -Wextra -g -D_GNU_SOURCE -I$(OUTPUT)include -I$(srctree)/tools/include
override LDFLAGS += -lpthread
ALL_TARGETS := testusb ffs-test
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
all: $(ALL_PROGRAMS)
export srctree OUTPUT CC LD CFLAGS
include $(srctree)/tools/build/Makefile.include
TESTUSB_IN := $(OUTPUT)testusb-in.o
$(TESTUSB_IN): FORCE
$(Q)$(MAKE) $(build)=testusb
$(OUTPUT)testusb: $(TESTUSB_IN)
$(QUIET_LINK)$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
FFS_TEST_IN := $(OUTPUT)ffs-test-in.o
$(FFS_TEST_IN): FORCE
$(Q)$(MAKE) $(build)=ffs-test
$(OUTPUT)ffs-test: $(FFS_TEST_IN)
$(QUIET_LINK)$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
clean:
$(RM) testusb ffs-test
rm -f $(ALL_PROGRAMS)
find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete -o -name '\.*.o.cmd' -delete
install: $(ALL_PROGRAMS)
install -d -m 755 $(DESTDIR)$(bindir); \
for program in $(ALL_PROGRAMS); do \
install $$program $(DESTDIR)$(bindir); \
done
FORCE:
.PHONY: all install clean FORCE prepare
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment