diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -477,6 +477,159 @@ struct power_supply *power_supply_get_by_name(const char *name) } EXPORT_SYMBOL_GPL(power_supply_get_by_name); +static bool power_supply_is_system_battery(struct power_supply *psy) +{ + union power_supply_propval val; + + if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY) + return false; + + if (!power_supply_get_property_direct(psy, POWER_SUPPLY_PROP_SCOPE, + &val)) + if (val.intval == POWER_SUPPLY_SCOPE_DEVICE) + return false; + + return true; +} + +static int __power_supply_get_num_system_batteries(struct power_supply *epsy, + void *data) +{ + int *count = data; + + if (power_supply_is_system_battery(epsy)) + (*count)++; + + return 0; +} + +static int power_supply_get_num_system_batteries(struct device *dev) +{ + int ret, count = 0; + + ret = power_supply_for_each_psy(&count, + __power_supply_get_num_system_batteries); + + dev_dbg(dev, "%s: count: %d ret %d\n", __func__, count, ret); + + if (ret) + return ret; + + return count; +} + +struct psy_get_supplies_data { + int cnt; + int size; + struct power_supply **psys; +}; + +static int +__power_supply_populate_system_batteries_array(struct power_supply *epsy, + void *_data) +{ + struct psy_get_supplies_data *data = _data; + + if (power_supply_is_system_battery(epsy)) { + if (data->size <= data->cnt) + return -EOVERFLOW; + + get_device(&epsy->dev); + data->psys[data->cnt] = epsy; + atomic_inc(&epsy->use_cnt); + data->cnt++; + } + + return 0; +} + +static int +power_supply_populate_system_batteries_array(struct device *dev, int size, + struct power_supply **batteries) +{ + int ret; + + struct psy_get_supplies_data data = { + .cnt = 0, + .size = size, + .psys = batteries, + }; + + ret = power_supply_for_each_psy(&data, + __power_supply_populate_system_batteries_array); + + dev_dbg(dev, "%s Found %d batteries with array size %d ret %d\n", + __func__, data.cnt, data.size, ret); + + if (ret < 0 || !data.cnt) { + power_supply_put_system_batteries(batteries, data.cnt); + return ret; + } + + return data.cnt; +} + +/** + * power_supply_get_system_batteries() - Fetches references to battery type + * power supplies in the system. + * @dev: Pointer to device requesting the power supply refs. + * @psys: Pointer to an array of power supply refs. + * + * Helper function to get handles to battery type power supplies in the system. + * If acquiring a ref to a power supply fails, then the search for battery type + * power supplies will abort and the acquired power supply references will be + * released. + * + * Return: Indicates the number of battery type power supplies returned on + * success or a negative error code on failure. + * + * Call power_supply_put_system_batteries() after use to cleanup resources. + */ +int __must_check power_supply_get_system_batteries(struct device *dev, + struct power_supply ***psys) +{ + int ret; + + if (!psys) + return -EINVAL; + + ret = power_supply_get_num_system_batteries(dev); + if (ret <= 0) { + *psys = NULL; + return ret; + } + + *psys = kzalloc_objs(**psys, ret); + if (!*psys) + return -ENOMEM; + + ret = power_supply_populate_system_batteries_array(dev, ret, *psys); + if (ret <= 0) + *psys = NULL; + + return ret; +} +EXPORT_SYMBOL_GPL(power_supply_get_system_batteries); + +/** + * power_supply_put_system_batteries() - Cleanup resources allocated by + * power_supply_get_system_batteries() + * @psys: Array of power supply references to release and free. + * @count: Number of elements in the array. + */ +void power_supply_put_system_batteries(struct power_supply **psys, int count) +{ + int i; + + for (i = 0; i < count; i++) { + if (psys[i]) + power_supply_put(psys[i]); + } + + kfree(psys); +} +EXPORT_SYMBOL_GPL(power_supply_put_system_batteries); + /** * power_supply_put() - Drop reference obtained with power_supply_get_by_name * @psy: Reference to put diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -147,12 +147,12 @@ static int cdns3_plat_probe(struct platform_device *pdev) cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy"); if (IS_ERR(cdns->usb2_phy)) return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy), - "Failed to get cdn3,usb2-phy\n"); + "Failed to get cdns3,usb2-phy\n"); cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy"); if (IS_ERR(cdns->usb3_phy)) return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy), - "Failed to get cdn3,usb3-phy\n"); + "Failed to get cdns3,usb3-phy\n"); ret = phy_init(cdns->usb2_phy); if (ret) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -151,7 +151,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, usb_endpoint_xfer_int(&ep->desc)) && desc->bmAttributes != 0) { dev_notice(ddev, "%s endpoint with bmAttributes = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n", - usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk", + usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Interrupt", desc->bmAttributes, cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bmAttributes = 0; diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -37,6 +37,7 @@ */ #include +#include #include #include #include @@ -408,7 +409,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, return 0; /* allocate 2^1 pages = 8K (on i386); * should be more than enough for one device */ - pages_start = (char *)__get_free_pages(GFP_NOIO, 1); + pages_start = kmalloc(PAGE_SIZE << 1, GFP_NOIO); if (!pages_start) return -ENOMEM; @@ -479,7 +480,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, if (length > *nbytes) length = *nbytes; if (copy_to_user(*buffer, pages_start + *skip_bytes, length)) { - free_pages((unsigned long)pages_start, 1); + kfree(pages_start); return -EFAULT; } *nbytes -= length; @@ -490,7 +491,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, } else *skip_bytes -= length; - free_pages((unsigned long)pages_start, 1); + kfree(pages_start); /* Now look at all of this device's children. */ usb_hub_for_each_child(usbdev, chix, childdev) { diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -2329,6 +2329,13 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl) if (!connected(ps)) return -ENODEV; + if (ps->dev->state != USB_STATE_CONFIGURED) + return -EHOSTUNREACH; + + intf = usb_ifnum_to_if(ps->dev, ctl->ifno); + if (!intf) + return -EINVAL; + /* alloc buffer */ size = _IOC_SIZE(ctl->ioctl_code); if (size > 0) { @@ -2345,11 +2352,7 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl) } } - if (ps->dev->state != USB_STATE_CONFIGURED) - retval = -EHOSTUNREACH; - else if (!(intf = usb_ifnum_to_if(ps->dev, ctl->ifno))) - retval = -EINVAL; - else switch (ctl->ioctl_code) { + switch (ctl->ioctl_code) { /* disconnect kernel driver from interface */ case USBDEVFS_DISCONNECT: diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -623,11 +623,11 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type, mutex_lock(&hub->status_mutex); ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len); if (ret < len) { - if (ret != -ENODEV) - dev_err(hub->intfdev, - "%s failed (err = %d)\n", __func__, ret); if (ret >= 0) ret = -EIO; + if (ret != -ENODEV) + dev_dbg(hub->intfdev, + "get_port_status failed: err = %d\n", ret); } else { *status = le16_to_cpu(hub->status->port.wPortStatus); *change = le16_to_cpu(hub->status->port.wPortChange); @@ -995,11 +995,11 @@ static int hub_hub_status(struct usb_hub *hub, mutex_lock(&hub->status_mutex); ret = get_hub_status(hub->hdev, &hub->status->hub); if (ret < (int)sizeof(hub->status->hub)) { - if (ret != -ENODEV) - dev_err(hub->intfdev, - "%s failed (err = %d)\n", __func__, ret); if (ret >= 0) ret = -EIO; + if (ret != -ENODEV) + dev_dbg(hub->intfdev, + "get_hub_status failed: err = %d\n", ret); } else { *status = le16_to_cpu(hub->status->hub.wHubStatus); *change = le16_to_cpu(hub->status->hub.wHubChange); diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -334,9 +334,8 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) /** * dwc2_wakeup_from_lpm_l1 - Exit the device from LPM L1 state - * * @hsotg: Programming view of DWC_otg controller - * + * @remotewakeup: Whether this is a remote wakeup */ void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg, bool remotewakeup) { diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -2188,22 +2188,89 @@ static void dwc3_vbus_draw_work(struct work_struct *work) ret, dwc->current_limit); } -static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc) +static int dwc3_psy_notifier(struct notifier_block *nb, + unsigned long event, void *data) { - struct power_supply *usb_psy; - const char *usb_psy_name; + struct dwc3 *dwc = container_of(nb, struct dwc3, psy_nb); + struct power_supply *psy = data; + unsigned long flags; + + if (dwc->usb_psy) + return NOTIFY_DONE; + + if (strcmp(psy->desc->name, dwc->usb_psy_name) != 0) + return NOTIFY_DONE; + + /* Explicitly get the reference for this psy */ + psy = power_supply_get_by_name(dwc->usb_psy_name); + if (!psy) + return NOTIFY_DONE; + + spin_lock_irqsave(&dwc->lock, flags); + /* + * The USB power_supply may already be set. This can happen if notifier + * callbacks for the USB power_supply race, or if a previous notifier + * callback has already successfully fetched and associated the instance. + * In such cases, release the newly acquired reference and ignore + * subsequent notifications until the notifier is unregistered. + */ + if (dwc->usb_psy) { + spin_unlock_irqrestore(&dwc->lock, flags); + power_supply_put(psy); + return NOTIFY_DONE; + } + + dwc->usb_psy = psy; + if (dwc->current_limit != DWC3_CURRENT_UNSPECIFIED) + schedule_work(&dwc->vbus_draw_work); + spin_unlock_irqrestore(&dwc->lock, flags); + + return NOTIFY_OK; +} + +static void dwc3_get_usb_power_supply(struct dwc3 *dwc) +{ + struct power_supply *psy; + unsigned long flags; int ret; - ret = device_property_read_string(dwc->dev, "usb-psy-name", &usb_psy_name); + ret = device_property_read_string(dwc->dev, "usb-psy-name", &dwc->usb_psy_name); if (ret < 0) - return NULL; - - usb_psy = power_supply_get_by_name(usb_psy_name); - if (!usb_psy) - return ERR_PTR(-EPROBE_DEFER); + return; INIT_WORK(&dwc->vbus_draw_work, dwc3_vbus_draw_work); - return usb_psy; + + dwc->current_limit = DWC3_CURRENT_UNSPECIFIED; + dwc->psy_nb.notifier_call = dwc3_psy_notifier; + ret = power_supply_reg_notifier(&dwc->psy_nb); + if (ret) { + dev_err(dwc->dev, "Failed to register power supply notifier: %d\n", ret); + dwc->psy_nb.notifier_call = NULL; + return; + } + + psy = power_supply_get_by_name(dwc->usb_psy_name); + if (!psy) + return; + + /* Unregister the notifier now that we have the power supply */ + power_supply_unreg_notifier(&dwc->psy_nb); + dwc->psy_nb.notifier_call = NULL; + + spin_lock_irqsave(&dwc->lock, flags); + /* + * It is possible that the notifier callback ran before we reached here + * and successfully fetched the power supply. In that case we need to + * release the above reference. + */ + if (dwc->usb_psy) { + spin_unlock_irqrestore(&dwc->lock, flags); + power_supply_put(psy); + return; + } + + dwc->usb_psy = psy; + spin_unlock_irqrestore(&dwc->lock, flags); } int dwc3_core_probe(const struct dwc3_probe_data *data) @@ -2251,9 +2318,9 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) dwc3_get_software_properties(dwc, &data->properties); - dwc->usb_psy = dwc3_get_usb_power_supply(dwc); - if (IS_ERR(dwc->usb_psy)) - return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n"); + spin_lock_init(&dwc->lock); + + dwc3_get_usb_power_supply(dwc); if (!data->ignore_clocks_and_resets) { dwc->reset = devm_reset_control_array_get_optional_shared(dev); @@ -2305,7 +2372,6 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) dwc->num_usb3_ports = 1; } - spin_lock_init(&dwc->lock); mutex_init(&dwc->mutex); pm_runtime_get_noresume(dev); @@ -2373,6 +2439,8 @@ int dwc3_core_probe(const struct dwc3_probe_data *data) err_assert_reset: reset_control_assert(dwc->reset); err_put_psy: + if (dwc->psy_nb.notifier_call) + power_supply_unreg_notifier(&dwc->psy_nb); if (dwc->usb_psy) power_supply_put(dwc->usb_psy); @@ -2429,6 +2497,9 @@ void dwc3_core_remove(struct dwc3 *dwc) dwc3_free_event_buffers(dwc); + if (dwc->psy_nb.notifier_call) + power_supply_unreg_notifier(&dwc->psy_nb); + if (dwc->usb_psy) { cancel_work_sync(&dwc->vbus_draw_work); power_supply_put(dwc->usb_psy); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -722,7 +722,6 @@ struct dwc3_event_buffer { * @cancelled_list: list of cancelled requests for this endpoint * @pending_list: list of pending requests for this endpoint * @started_list: list of started requests on this endpoint - * @regs: pointer to first endpoint register * @trb_pool: array of transaction buffers * @trb_pool_dma: dma address of @trb_pool * @trb_enqueue: enqueue 'pointer' into TRB array @@ -1059,6 +1058,8 @@ struct dwc3_glue_ops { * @role_switch_default_mode: default operation mode of controller while * usb role is USB_ROLE_NONE. * @usb_psy: pointer to power supply interface. + * @usb_psy_name: name of the USB power supply + * @psy_nb: power supply notifier block * @vbus_draw_work: Work to set the vbus drawing limit * @current_limit: How much current to draw from vbus, in milliAmperes. * @usb2_phy: pointer to USB2 PHY @@ -1251,9 +1252,13 @@ struct dwc3 { enum usb_dr_mode role_switch_default_mode; struct power_supply *usb_psy; + const char *usb_psy_name; + struct notifier_block psy_nb; struct work_struct vbus_draw_work; unsigned int current_limit; +#define DWC3_CURRENT_UNSPECIFIED UINT_MAX + u32 fladj; u32 ref_clk_per; u32 irq_gadget; diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c --- a/drivers/usb/dwc3/dwc3-am62.c +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -205,7 +205,9 @@ static int dwc3_ti_init(struct dwc3_am62 *am62) dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg); - clk_prepare_enable(am62->usb2_refclk); + ret = clk_prepare_enable(am62->usb2_refclk); + if (ret) + return ret; /* Set mode valid bit to indicate role is valid */ reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL); @@ -361,14 +363,19 @@ static int dwc3_ti_resume_common(struct device *dev) { struct dwc3_am62 *am62 = dev_get_drvdata(dev); u32 reg; + int ret; reg = dwc3_ti_readl(am62, USBSS_DEBUG_CFG); if (reg != USBSS_DEBUG_CFG_DISABLED) { /* lost power/context */ - dwc3_ti_init(am62); + ret = dwc3_ti_init(am62); + if (ret) + return ret; } else { dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_OFF); - clk_prepare_enable(am62->usb2_refclk); + ret = clk_prepare_enable(am62->usb2_refclk); + if (ret) + return ret; } if (device_may_wakeup(dev)) { diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -602,7 +602,7 @@ static void dwc3_qcom_run_stop_notifier(struct dwc3 *dwc, bool is_on) pm_runtime_mark_last_busy(qcom->dev); } -struct dwc3_glue_ops dwc3_qcom_glue_ops = { +static struct dwc3_glue_ops dwc3_qcom_glue_ops = { .pre_set_role = dwc3_qcom_set_role_notifier, .pre_run_stop = dwc3_qcom_run_stop_notifier, }; diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -98,18 +98,10 @@ static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data) dwc3_xlnx_mask_phy_rst(priv_data, false); - /* Assert and De-assert reset */ - ret = reset_control_assert(crst); - if (ret < 0) { - dev_err_probe(dev, ret, "failed to assert Reset\n"); - return ret; - } - - ret = reset_control_deassert(crst); - if (ret < 0) { - dev_err_probe(dev, ret, "failed to De-assert Reset\n"); - return ret; - } + /* assert and deassert reset */ + ret = reset_control_reset(crst); + if (ret) + return dev_err_probe(dev, ret, "failed to assert and deassert reset\n"); dwc3_xlnx_mask_phy_rst(priv_data, true); dwc3_xlnx_set_coherency(priv_data, XLNX_USB2_TRAFFIC_ROUTE_CONFIG); @@ -194,7 +186,7 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) } if (priv_data->usb3_phy) { - /* Set PIPE Power Present signal in FPD Power Present Register*/ + /* Set PIPE Power Present signal in FPD Power Present Register */ writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT); /* Set the PIPE Clock Select bit in FPD PIPE Clock register */ writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3129,15 +3129,26 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g, static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA) { struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; if (dwc->usb2_phy) return usb_phy_set_power(dwc->usb2_phy, mA); - if (!dwc->usb_psy) - return -EOPNOTSUPP; + spin_lock_irqsave(&dwc->lock, flags); + if (!dwc->usb_psy) { + if (!dwc->psy_nb.notifier_call) { + spin_unlock_irqrestore(&dwc->lock, flags); + return -EOPNOTSUPP; + } + dwc->current_limit = mA; + spin_unlock_irqrestore(&dwc->lock, flags); + dev_dbg(dwc->dev, "Stored VBUS draw: %u mA (power supply not ready)\n", mA); + return 0; + } dwc->current_limit = mA; schedule_work(&dwc->vbus_draw_work); + spin_unlock_irqrestore(&dwc->lock, flags); return 0; } diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "../host/xhci-port.h" #include "../host/xhci-ext-caps.h" @@ -46,9 +47,9 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc) return; } - op_regs_base = HC_LENGTH(readl(xhci_regs)); + op_regs_base = FIELD_GET(HC_LENGTH, readl(xhci_regs)); reg = readl(xhci_regs + XHCI_HCSPARAMS1); - port_num = HCS_MAX_PORTS(reg); + port_num = FIELD_GET(HCS_MAX_PORTS, reg); for (i = 1; i <= port_num; i++) { offset = op_regs_base + XHCI_PORTSC_BASE + 0x10 * (i - 1); diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -125,11 +125,11 @@ static int usb_string_copy(const char *s, char **s_copy) if (copy) { str = copy; } else { - str = kmalloc(USB_MAX_STRING_WITH_NULL_LEN, GFP_KERNEL); + str = kzalloc(USB_MAX_STRING_WITH_NULL_LEN, GFP_KERNEL); if (!str) return -ENOMEM; } - strcpy(str, s); + memcpy(str, s, ret + 1); if (str[ret - 1] == '\n') str[ret - 1] = '\0'; *s_copy = str; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -159,7 +159,9 @@ struct ffs_epfile { struct mutex mutex; struct ffs_data *ffs; - struct ffs_ep *ep; /* P: ffs->eps_lock */ + struct ffs_ep *ep; /* P: ffs->eps_lock */ + struct ffs_epfile *epfile_in; /* P: ffs->eps_lock */ + struct ffs_epfile *epfile_out; /* P: ffs->eps_lock */ /* * Buffer for holding data from partial reads which may happen since @@ -219,12 +221,13 @@ struct ffs_epfile { struct ffs_buffer *read_buffer; #define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN)) - char name[5]; + char name[8]; unsigned char in; /* P: ffs->eps_lock */ unsigned char isoc; /* P: ffs->eps_lock */ - unsigned char _pad; + u8 zlp_enabled; /* P: ffs->eps_lock */ + bool is_rw_proxy; /* Protects dmabufs */ struct mutex dmabufs_mutex; @@ -991,9 +994,8 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile, return ret; } -static struct ffs_ep *ffs_epfile_wait_ep(struct file *file) +static struct ffs_ep *ffs_epfile_wait_ep(struct ffs_epfile *epfile, struct file *file) { - struct ffs_epfile *epfile = file->private_data; struct ffs_ep *ep; int ret; @@ -1020,17 +1022,22 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) char *data = NULL; ssize_t ret, data_len = -EINVAL; int halt; + bool is_rw_proxy = epfile->is_rw_proxy; /* Are we still active? */ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) return -ENODEV; - ep = ffs_epfile_wait_ep(file); + /* Proxy to base endpoint if rw_proxy */ + if (is_rw_proxy) + epfile = io_data->read ? epfile->epfile_out : epfile->epfile_in; + + ep = ffs_epfile_wait_ep(epfile, file); if (IS_ERR(ep)) return PTR_ERR(ep); /* Do we halt? */ - halt = (!io_data->read == !epfile->in); + halt = is_rw_proxy ? 0 : (!io_data->read == !epfile->in); if (halt && epfile->isoc) return -EINVAL; @@ -1127,6 +1134,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) req->buf = data; req->num_sgs = 0; } + + req->zero = !io_data->read ? epfile->zlp_enabled : 0; req->length = data_len; io_data->buf = data; @@ -1178,6 +1187,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) req->buf = data; req->num_sgs = 0; } + + req->zero = !io_data->read ? epfile->zlp_enabled : 0; req->length = data_len; io_data->buf = data; @@ -1666,7 +1677,7 @@ static int ffs_dmabuf_transfer(struct file *file, priv = attach->importer_priv; - ep = ffs_epfile_wait_ep(file); + ep = ffs_epfile_wait_ep(epfile, file); if (IS_ERR(ep)) { ret = PTR_ERR(ep); goto err_attachment_put; @@ -1732,6 +1743,7 @@ static int ffs_dmabuf_transfer(struct file *file, /* Now that the dma_fence is in place, queue the transfer. */ + usb_req->zero = epfile->zlp_enabled; usb_req->length = req->length; usb_req->buf = NULL; usb_req->sg = priv->sgt->sgl; @@ -1779,10 +1791,14 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, struct ffs_epfile *epfile = file->private_data; struct ffs_ep *ep; int ret; + __u32 enable_zlp = 0; if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) return -ENODEV; + if (epfile->is_rw_proxy) + return -ENOTTY; + switch (code) { case FUNCTIONFS_DMABUF_ATTACH: { @@ -1811,12 +1827,29 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, return ffs_dmabuf_transfer(file, &req); } + /* + * We handle this IOCTL before ffs_epfile_wait_ep() to allow userspace + * to configure ZLP behavior immediately without blocking indefinitely + * while waiting for the USB host to connect and enable the endpoint. + */ + case FUNCTIONFS_ENDPOINT_ENABLE_ZLP: + if (!epfile->in) + return -EINVAL; + + if (copy_from_user(&enable_zlp, (void __user *)value, sizeof(enable_zlp))) + return -EFAULT; + + spin_lock_irq(&epfile->ffs->eps_lock); + epfile->zlp_enabled = !!enable_zlp; + spin_unlock_irq(&epfile->ffs->eps_lock); + + return 0; default: break; } /* Wait for endpoint to be enabled */ - ep = ffs_epfile_wait_ep(file); + ep = ffs_epfile_wait_ep(epfile, file); if (IS_ERR(ep)) return PTR_ERR(ep); @@ -2214,7 +2247,7 @@ static void ffs_data_closed(struct ffs_data *ffs) if (epfiles) ffs_epfiles_destroy(ffs->sb, epfiles, - ffs->eps_count); + ffs->epfiles_count); if (ffs->setup_state == FFS_SETUP_PENDING) __ffs_ep0_stall(ffs); @@ -2273,7 +2306,7 @@ static void ffs_data_clear(struct ffs_data *ffs) * copy of epfile will save us from use-after-free. */ if (epfiles) { - ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count); + ffs_epfiles_destroy(ffs->sb, epfiles, ffs->epfiles_count); ffs->epfiles = NULL; } @@ -2371,11 +2404,16 @@ static void functionfs_unbind(struct ffs_data *ffs) static int ffs_epfiles_create(struct ffs_data *ffs) { struct ffs_epfile *epfile, *epfiles; - unsigned i, count; + unsigned int i, count, epfiles_count; int err; count = ffs->eps_count; - epfiles = kzalloc_objs(*epfiles, count); + epfiles_count = count; + if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) + epfiles_count += count / 2; + ffs->epfiles_count = epfiles_count; + + epfiles = kzalloc_objs(*epfiles, epfiles_count); if (!epfiles) return -ENOMEM; @@ -2398,6 +2436,32 @@ static int ffs_epfiles_create(struct ffs_data *ffs) } } + if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) { + struct ffs_epfile *comp = epfiles + count; + + for (i = 0; i < count; i += 2, ++comp) { + struct ffs_epfile *ep1 = &epfiles[i]; + struct ffs_epfile *ep2 = &epfiles[i + 1]; + bool ep1_in = ffs->eps_addrmap[i + 1] & USB_ENDPOINT_DIR_MASK; + + comp->ffs = ffs; + comp->is_rw_proxy = true; + comp->epfile_in = ep1_in ? ep1 : ep2; + comp->epfile_out = ep1_in ? ep2 : ep1; + mutex_init(&comp->mutex); + mutex_init(&comp->dmabufs_mutex); + INIT_LIST_HEAD(&comp->dmabufs); + snprintf(comp->name, sizeof(comp->name), "%s_rw", + epfiles[i].name); + err = ffs_sb_create_file(ffs->sb, comp->name, + comp, &ffs_epfile_operations); + if (err) { + ffs_epfiles_destroy(ffs->sb, epfiles, count + (i / 2)); + return err; + } + } + } + ffs->epfiles = epfiles; return 0; } @@ -2975,7 +3039,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, FUNCTIONFS_VIRTUAL_ADDR | FUNCTIONFS_EVENTFD | FUNCTIONFS_ALL_CTRL_RECIP | - FUNCTIONFS_CONFIG0_SETUP)) { + FUNCTIONFS_CONFIG0_SETUP | + FUNCTIONFS_RW_PROXY_EPS)) { ret = -ENOSYS; goto error; } @@ -3063,6 +3128,21 @@ static int __ffs_data_got_descs(struct ffs_data *ffs, goto error; } + if (ffs->user_flags & FUNCTIONFS_RW_PROXY_EPS) { + if (ffs->eps_count % 2) { + ret = -EINVAL; + goto error; + } + + for (i = 1; i < ffs->eps_count; i += 2) { + if ((ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) == + (ffs->eps_addrmap[i + 1] & USB_ENDPOINT_DIR_MASK)) { + ret = -EINVAL; + goto error; + } + } + } + ffs->raw_descs_data = _data; ffs->raw_descs = raw_descs; ffs->raw_descs_length = data - raw_descs; @@ -3358,7 +3438,7 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, struct usb_request *req; struct usb_ep *ep; u8 bEndpointAddress; - u16 wMaxPacketSize; + __le16 wMaxPacketSize; /* * We back up bEndpointAddress because autoconfig overwrites diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -203,7 +203,6 @@ /*------------------------------------------------------------------------*/ #define FSG_DRIVER_DESC "Mass Storage Function" -#define FSG_DRIVER_VERSION "2009/09/11" static const char fsg_string_interface[] = "Mass Storage"; @@ -3516,8 +3515,6 @@ static struct usb_function_instance *fsg_alloc_inst(void) if (rc) goto release_common; - pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); - memset(&config, 0, sizeof(config)); config.removable = true; rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0", diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -641,15 +641,15 @@ int u_audio_start_capture(struct g_audio *audio_dev) ret = config_ep_by_speed(gadget, &audio_dev->func, ep_fback); if (ret < 0) { dev_err(dev, "config_ep_by_speed in_ep_fback failed (%d)\n", ret); - return ret; // TODO: Clean up out_ep + goto err_out_ep; } - prm->fb_ep_enabled = true; ret = usb_ep_enable(ep_fback); if (ret < 0) { dev_err(dev, "usb_ep_enable failed for in_ep_fback (%d)\n", ret); - return ret; // TODO: Clean up out_ep + goto err_out_ep; } + prm->fb_ep_enabled = true; req_len = ep_fback->maxpacket; req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC); @@ -680,6 +680,12 @@ int u_audio_start_capture(struct g_audio *audio_dev) dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return 0; + +err_out_ep: + set_active(prm, false); + free_ep(prm, ep); + + return ret; } EXPORT_SYMBOL_GPL(u_audio_start_capture); diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h --- a/drivers/usb/gadget/function/u_fs.h +++ b/drivers/usb/gadget/function/u_fs.h @@ -252,8 +252,14 @@ struct ffs_data { unsigned short strings_count; unsigned short interfaces_count; + + /* + * eps_count tracks the number of underlying hardware endpoints. + * epfiles_count tracks the total number of VFS endpoint files. + * When companion endpoints are active, epfiles_count > eps_count. + */ unsigned short eps_count; - unsigned short _pad1; + unsigned short epfiles_count; /* filled by __ffs_data_got_strings() */ /* ids in stringtabs are set in functionfs_bind() */ diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -471,11 +471,17 @@ static void ep_user_copy_worker(struct work_struct *work) struct kiocb *iocb = priv->iocb; size_t ret; - kthread_use_mm(mm); - ret = copy_to_iter(priv->buf, priv->actual, &priv->to); - kthread_unuse_mm(mm); - if (!ret) + if (mmget_not_zero(mm)) { + kthread_use_mm(mm); + ret = copy_to_iter(priv->buf, priv->actual, &priv->to); + kthread_unuse_mm(mm); + mmput(mm); + if (!ret) + ret = -EFAULT; + } else { ret = -EFAULT; + } + mmdrop(mm); /* completing the iocb can drop the ctx and mm, don't touch mm after */ iocb->ki_complete(iocb, ret); @@ -501,6 +507,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) * complete the aio request immediately. */ if (priv->to_free == NULL || unlikely(req->actual == 0)) { + mmdrop(priv->mm); kfree(req->buf); kfree(priv->to_free); kfree(priv); @@ -541,6 +548,7 @@ static ssize_t ep_aio(struct kiocb *iocb, priv->epdata = epdata; priv->actual = 0; priv->mm = current->mm; /* mm teardown waits for iocbs in exit_aio() */ + mmgrab(priv->mm); /* each kiocb is coupled to one usb_request, but we can't * allocate or submit those if the host disconnected. @@ -570,6 +578,7 @@ static ssize_t ep_aio(struct kiocb *iocb, fail: spin_unlock_irq(&epdata->dev->lock); + mmdrop(priv->mm); kfree(priv->to_free); kfree(priv); put_ep(epdata); diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c --- a/drivers/usb/gadget/legacy/nokia.c +++ b/drivers/usb/gadget/legacy/nokia.c @@ -50,7 +50,7 @@ static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; */ #define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS -#endif /* CONFIG_USB_DEBUG */ +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -90,7 +90,7 @@ config USB_BCM63XX_UDC config USB_FSL_USB2 tristate "Freescale Highspeed USB DR Peripheral Controller" - depends on FSL_SOC + depends on FSL_SOC || COMPILE_TEST help Some of Freescale PowerPC and i.MX processors have a High Speed Dual-Role(DR) USB controller, which supports device mode. diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c --- a/drivers/usb/gadget/udc/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -1945,6 +1945,7 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value, u16 index, u16 length) { u16 usb_status = 0; + struct usb_request *usb_req; struct qe_req *req; struct qe_ep *ep; int status = 0; @@ -1983,8 +1984,11 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value, } } - req = container_of(qe_alloc_request(&ep->ep, GFP_KERNEL), - struct qe_req, req); + usb_req = qe_alloc_request(&ep->ep, GFP_KERNEL); + if (!usb_req) + goto stall; + + req = container_of(usb_req, struct qe_req, req); req->req.length = 2; req->req.buf = udc->statusbuf; *(u16 *)req->req.buf = cpu_to_le16(usb_status); diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -320,7 +320,6 @@ struct pch_vbus_gpio_data { * @lock: protects all state * @stall: stall requested * @prot_stall: protcol stall requested - * @registered: driver registered with system * @suspended: driver in suspended state * @connected: gadget driver associated * @vbus_session: required vbus_session state diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -15,6 +15,7 @@ #include #include #include +#include struct fsl_usb2_dev_data { char *dr_mode; /* controller mode */ @@ -183,7 +184,7 @@ static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev) const struct of_device_id *match; const unsigned char *prop; static unsigned int idx; - int i; + int i, err; if (!of_device_is_available(np)) return -ENODEV; @@ -246,6 +247,10 @@ static int fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev) } } + err = devm_regulator_get_enable_optional(&ofdev->dev, "vbus"); + if (err) + return dev_err_probe(&ofdev->dev, err, "failed to get vbus regulator\n"); + for (i = 0; i < ARRAY_SIZE(dev_data->drivers); i++) { if (!dev_data->drivers[i]) continue; diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -683,7 +683,7 @@ static int fill_buffer(struct debug_buffer *buf) int ret; if (!buf->page) - buf->page = (char *)get_zeroed_page(GFP_KERNEL); + buf->page = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!buf->page) { ret = -ENOMEM; @@ -729,11 +729,8 @@ static int debug_close(struct inode *inode, struct file *file) { struct debug_buffer *buf = file->private_data; - if (buf) { - if (buf->page) - free_page((unsigned long)buf->page); - kfree(buf); - } + kfree(buf->page); + kfree(buf); return 0; } diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h --- a/drivers/usb/host/xhci-caps.h +++ b/drivers/usb/host/xhci-caps.h @@ -5,22 +5,23 @@ */ #include +#include /* hc_capbase - bitmasks */ /* bits 7:0 - Capability Registers Length */ -#define HC_LENGTH(p) ((p) & 0xff) +#define HC_LENGTH GENMASK(7, 0) /* bits 15:8 - Rsvd */ /* bits 31:16 - Host Controller Interface Version Number */ -#define HC_VERSION(p) (((p) >> 16) & 0xffff) +#define HC_VERSION GENMASK(31, 16) /* HCSPARAMS1 - hcs_params1 - bitmasks */ /* bits 7:0 - Number of Device Slots */ -#define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff) -#define HCS_SLOTS_MASK 0xff -/* bits 18:8 - Number of Interrupters, max values is 1024 */ -#define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff) -/* bits 31:24, Max Ports - max value is 255 */ -#define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff) +#define HCS_SLOTS_MASK GENMASK(7, 0) +/* bits 18:8 - Number of Interrupters, max values is 1024 */ +#define HCS_MAX_INTRS GENMASK(18, 8) +/* bits 23:19 - Rsvd */ +/* bits 31:24 - Max Ports, max values is 255 */ +#define HCS_MAX_PORTS GENMASK(31, 24) /* HCSPARAMS2 - hcs_params2 - bitmasks */ /* @@ -33,24 +34,25 @@ * Note: 1 Frame = 8 Microframes * xHCI specification section 5.3.4. */ -#define HCS_IST_VALUE(p) ((p) & 0x7) +#define HCS_IST_VALUE GENMASK(2, 0) #define HCS_IST_UNIT BIT(3) /* bits 7:4 - Event Ring Segment Table Max, 2^(n) */ -#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) +#define HCS_ERST_MAX GENMASK(7, 4) /* bits 20:8 - Rsvd */ /* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */ -#define HCS_MAX_SP_HI(p) (((p) >> 21) & 0x1f) +#define HCS_MAX_SP_HI GENMASK(25, 21) /* bit 26 - Scratchpad restore, for save/restore HW state */ /* bits 31:27 - Max Scratchpad Buffers (Lo), 5 Least significant bits */ -#define HCS_MAX_SP_LO(p) (((p) >> 27) & 0x1f) -#define HCS_MAX_SCRATCHPAD(p) (HCS_MAX_SP_HI(p) << 5 | HCS_MAX_SP_LO(p)) +#define HCS_MAX_SP_LO GENMASK(31, 27) +#define HCS_MAX_SCRATCHPAD(p) (FIELD_GET(HCS_MAX_SP_HI, (p)) << 5 | \ + FIELD_GET(HCS_MAX_SP_LO, (p))) /* HCSPARAMS3 - hcs_params3 - bitmasks */ /* bits 7:0 - U1 Device Exit Latency, Max U1 to U0 latency for the roothub ports */ -#define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff) +#define HCS_U1_LATENCY GENMASK(7, 0) /* bits 15:8 - Rsvd */ /* bits 31:16 - U2 Device Exit Latency, Max U2 to U0 latency for the roothub ports */ -#define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff) +#define HCS_U2_LATENCY GENMASK(31, 16) /* HCCPARAMS1 - hcc_params - bitmasks */ /* bit 0 - 64-bit Addressing Capability */ @@ -77,19 +79,20 @@ /* bit 11 - Contiguous Frame ID Capability */ #define HCC_CFC BIT(11) /* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */ -#define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) +#define HCC_MAX_PSA GENMASK(15, 12) +#define GET_MAX_PSA_SIZE(p) (1 << (FIELD_GET(HCC_MAX_PSA, (p)) + 1)) /* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */ -#define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff) +#define HCC_EXT_CAPS GENMASK(31, 16) /* DBOFF - db_off - bitmasks */ /* bits 1:0 - Rsvd */ /* bits 31:2 - Doorbell Array Offset */ -#define DBOFF_MASK (0xfffffffc) +#define DBOFF_MASK GENMASK(31, 2) /* RTSOFF - run_regs_off - bitmasks */ /* bits 4:0 - Rsvd */ /* bits 31:5 - Runtime Register Space Offse */ -#define RTSOFF_MASK (~0x1f) +#define RTSOFF_MASK GENMASK(31, 5) /* HCCPARAMS2 - hcc_params2 - bitmasks */ /* bit 0 - U3 Entry Capability */ diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c --- a/drivers/usb/host/xhci-dbgtty.c +++ b/drivers/usb/host/xhci-dbgtty.c @@ -665,11 +665,11 @@ int dbc_tty_init(void) void dbc_tty_exit(void) { - if (dbc_tty_driver) { - tty_unregister_driver(dbc_tty_driver); - tty_driver_kref_put(dbc_tty_driver); - dbc_tty_driver = NULL; - } + if (IS_ERR_OR_NULL(dbc_tty_driver)) + return; + tty_unregister_driver(dbc_tty_driver); + tty_driver_kref_put(dbc_tty_driver); idr_destroy(&dbc_tty_minors); + dbc_tty_driver = NULL; } diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -9,6 +9,7 @@ #include #include +#include #include "xhci.h" #include "xhci-debugfs.h" @@ -791,7 +792,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) xhci->debugfs_root, "reg-cap"); xhci_debugfs_regset(xhci, - HC_LENGTH(readl(&xhci->cap_regs->hc_capbase)), + FIELD_GET(HC_LENGTH, readl(&xhci->cap_regs->hc_capbase)), xhci_op_regs, ARRAY_SIZE(xhci_op_regs), xhci->debugfs_root, "reg-op"); diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c --- a/drivers/usb/host/xhci-histb.c +++ b/drivers/usb/host/xhci-histb.c @@ -276,7 +276,7 @@ static int xhci_histb_probe(struct platform_device *pdev) if (ret) goto put_usb3_hcd; - if (HCC_MAX_PSA(xhci->hcc_params) >= 4) + if (GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4) xhci->shared_hcd->can_do_streams = 1; ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -115,8 +115,8 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf, if ((xhci->quirks & XHCI_LPM_SUPPORT)) { reg = readl(&xhci->cap_regs->hcs_params3); - ss_cap->bU1devExitLat = HCS_U1_LATENCY(reg); - ss_cap->bU2DevExitLat = cpu_to_le16(HCS_U2_LATENCY(reg)); + ss_cap->bU1devExitLat = FIELD_GET(HCS_U1_LATENCY, reg); + ss_cap->bU2DevExitLat = cpu_to_le16(FIELD_GET(HCS_U2_LATENCY, reg)); } if (wLength < le16_to_cpu(bos->wTotalLength)) @@ -1295,7 +1295,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } /* In spec software should not attempt to suspend * a port unless the port reports that it is in the - * enabled (PED = ‘1’,PLS < ‘3’) state. + * enabled (PED = '1',PLS < '3') state. */ portsc = xhci_portsc_readl(port); if ((portsc & PORT_PE) == 0 || (portsc & PORT_RESET) || diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "xhci.h" #include "xhci-trace.h" @@ -1001,7 +1002,6 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, for (i = 0; i < 31; i++) { dev->eps[i].ep_index = i; dev->eps[i].vdev = dev; - dev->eps[i].xhci = xhci; INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list); INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list); } @@ -1493,6 +1493,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, return -ENOMEM; virt_dev->eps[ep_index].skip = false; + virt_dev->eps[ep_index].next_uframe = -1; ep_ring = virt_dev->eps[ep_index].new_ring; xhci_ring_init(xhci, ep_ring); @@ -2083,7 +2084,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, addr, port_offset, port_count, major_revision); /* Port count includes the current port offset */ if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) - /* WTF? "Valid values are ‘1’ to MaxPorts" */ + /* WTF? "Valid values are '1' to MaxPorts" */ return; port_cap = &xhci->port_caps[xhci->num_port_caps++]; @@ -2300,7 +2301,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags) if (!segs) segs = ERST_DEFAULT_SEGS; - max_segs = BIT(HCS_ERST_MAX(xhci->hcs_params2)); + max_segs = FIELD_GET(HCS_ERST_MAX, xhci->hcs_params2) << 2; segs = min(segs, max_segs); ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -468,7 +468,7 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci) * MTK xHCI 0.96: PSA is 1 by default even if doesn't support stream, * and it's 3 when support it. */ - if (xhci->hci_version < 0x100 && HCC_MAX_PSA(xhci->hcc_params) == 4) + if (xhci->hci_version < 0x100 && GET_MAX_PSA_SIZE(xhci->hcc_params) == 4) xhci->quirks |= XHCI_BROKEN_STREAMS; } @@ -650,7 +650,7 @@ static int xhci_mtk_probe(struct platform_device *pdev) } usb3_hcd = xhci_get_usb3_hcd(xhci); - if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 && + if (usb3_hcd && GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4 && !(xhci->quirks & XHCI_BROKEN_STREAMS)) usb3_hcd->can_do_streams = 1; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "xhci.h" #include "xhci-trace.h" @@ -662,7 +663,8 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) } usb3_hcd = xhci_get_usb3_hcd(xhci); - if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) && HCC_MAX_PSA(xhci->hcc_params) >= 4) + if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) && + GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4) usb3_hcd->can_do_streams = 1; /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -340,7 +340,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s } usb3_hcd = xhci_get_usb3_hcd(xhci); - if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 && + if (usb3_hcd && GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4 && !(xhci->quirks & XHCI_BROKEN_STREAMS)) usb3_hcd->can_do_streams = 1; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -57,6 +57,8 @@ #include #include #include +#include + #include "xhci.h" #include "xhci-trace.h" @@ -561,8 +563,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, * pointer command pending because the device can choose to start any * stream once the endpoint is on the HW schedule. */ - if ((ep_state & EP_STOP_CMD_PENDING) || (ep_state & SET_DEQ_PENDING) || - (ep_state & EP_HALTED) || (ep_state & EP_CLEARING_TT)) + if (ep_state & (EP_STOP_CMD_PENDING | SET_DEQ_PENDING | EP_HALTED | + EP_CLEARING_TT | EP_DROP_PENDING)) return; trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id)); @@ -573,9 +575,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, } /* Ring the doorbell for any rings with pending URBs */ -static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, - unsigned int slot_id, - unsigned int ep_index) +void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci, unsigned int slot_id, + unsigned int ep_index) { unsigned int stream_id; struct xhci_virt_ep *ep; @@ -598,13 +599,6 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, } } -void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci, - unsigned int slot_id, - unsigned int ep_index) -{ - ring_doorbell_for_active_rings(xhci, slot_id, ep_index); -} - static struct xhci_virt_ep *xhci_get_virt_ep(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index) @@ -686,112 +680,100 @@ static u64 xhci_get_hw_deq(struct xhci_hcd *xhci, struct xhci_virt_device *vdev, return le64_to_cpu(ep_ctx->deq); } -static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, - unsigned int slot_id, unsigned int ep_index, - unsigned int stream_id, struct xhci_td *td) +/* + * Move the endpoint dequeue pointer to the next queued TD on ring->td_list or + * to enqueue if no TDs are queued (empty ring) + * All cancelled TDs on ring->td_list should be moved to ep->cancelled_td_list + * before calling this function + */ +static int xhci_move_deq_to_next_td(struct xhci_hcd *xhci, + struct xhci_virt_ep *ep, + unsigned int stream_id) { - struct xhci_virt_device *dev = xhci->devs[slot_id]; - struct xhci_virt_ep *ep = &dev->eps[ep_index]; - struct xhci_ring *ep_ring; struct xhci_command *cmd; - struct xhci_segment *new_seg; - union xhci_trb *new_deq; - int new_cycle; + struct xhci_ring *ring; + struct xhci_td *td; dma_addr_t addr; - u64 hw_dequeue; - bool hw_dequeue_found = false; - bool td_last_trb_found = false; + int new_cycle; u32 trb_sct = 0; - int ret; + int ret = 0; - ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id, - ep_index, stream_id); - if (!ep_ring) { + ring = xhci_virt_ep_to_ring(xhci, ep, stream_id); + if (!ring) { xhci_warn(xhci, "WARN can't find new dequeue, invalid stream ID %u\n", stream_id); return -ENODEV; } - hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id) & TR_DEQ_PTR_MASK; - new_seg = ep_ring->deq_seg; - new_deq = ep_ring->dequeue; - new_cycle = le32_to_cpu(td->end_trb->generic.field[3]) & TRB_CYCLE; - - /* - * Walk the ring until both the next TRB and hw_dequeue are found (don't - * move hw_dequeue back if it went forward due to a HW bug). Cycle state - * is loaded from a known good TRB, track later toggles to maintain it. - */ - do { - if (!hw_dequeue_found && xhci_trb_virt_to_dma(new_seg, new_deq) - == (dma_addr_t)hw_dequeue) { - hw_dequeue_found = true; - if (td_last_trb_found) - break; - } - if (new_deq == td->end_trb) - td_last_trb_found = true; - - if (td_last_trb_found && trb_is_link(new_deq) && - link_trb_toggles_cycle(new_deq)) - new_cycle ^= 0x1; - - next_trb(&new_seg, &new_deq); - - /* Search wrapped around, bail out */ - if (new_deq == ep->ring->dequeue) { - xhci_err(xhci, "Error: Failed finding new dequeue state\n"); - return -EINVAL; - } - - } while (!hw_dequeue_found || !td_last_trb_found); - - /* Don't update the ring cycle state for the producer (us). */ - addr = xhci_trb_virt_to_dma(new_seg, new_deq); - if (addr == 0) { - xhci_warn(xhci, "Can't find dma of new dequeue ptr\n"); - xhci_warn(xhci, "deq seg = %p, deq ptr = %p\n", new_seg, new_deq); - return -EINVAL; - } - if ((ep->ep_state & SET_DEQ_PENDING)) { - xhci_warn(xhci, "Set TR Deq already pending, don't submit for %pad\n", - &addr); + xhci_warn(xhci, "Set TR Deq already pending\n"); return -EBUSY; } /* This function gets called from contexts where it cannot sleep */ cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!cmd) { - xhci_warn(xhci, "Can't alloc Set TR Deq cmd %pad\n", &addr); + xhci_warn(xhci, "Can't alloc Set TR Deq cmd\n"); return -ENOMEM; } + /* + * Move dequeue to the beginning of next td, or to enqueue if ring is + * empty. Avoid moving dequeue to a link trb (empty ring) as it causes + * issues on some hosts. In that case advance the enqueue to next segment + * before moving dequeue to it + */ + + if (list_empty(&ring->td_list)) { + if (trb_is_link(ring->enqueue)) + inc_enq_past_link(xhci, ring, 0); + ep->queued_deq_seg = ring->enq_seg; + ep->queued_deq_ptr = ring->enqueue; + new_cycle = ring->cycle_state; + } else { + td = list_first_entry(&ring->td_list, struct xhci_td, td_list); + ep->queued_deq_seg = td->start_seg; + ep->queued_deq_ptr = td->start_trb; + new_cycle = le32_to_cpu(td->start_trb->generic.field[3]) & TRB_CYCLE; + } + + addr = xhci_trb_virt_to_dma(ep->queued_deq_seg, ep->queued_deq_ptr); + if (addr == 0) { + xhci_warn(xhci, "Can't find new dequeue dma of seg %p, ptr %p\n", + ep->queued_deq_seg, ep->queued_deq_ptr); + ret = -EINVAL; + goto err_out; + } + if (stream_id) trb_sct = SCT_FOR_TRB(SCT_PRI_TR); ret = queue_command(xhci, cmd, lower_32_bits(addr) | trb_sct | new_cycle, upper_32_bits(addr), - STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(slot_id) | - EP_INDEX_FOR_TRB(ep_index) | TRB_TYPE(TRB_SET_DEQ), false); - if (ret < 0) { - xhci_free_command(xhci, cmd); - return ret; - } - ep->queued_deq_seg = new_seg; - ep->queued_deq_ptr = new_deq; + STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(ep->vdev->slot_id) | + EP_INDEX_FOR_TRB(ep->ep_index) | TRB_TYPE(TRB_SET_DEQ), false); + if (ret < 0) + goto err_out; xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Set TR Deq ptr 0x%llx, cycle %u\n", addr, new_cycle); - /* Stop the TD queueing code from ringing the doorbell until - * this command completes. The HC won't set the dequeue pointer - * if the ring is running, and ringing the doorbell starts the - * ring running. + /* + * Stop the TD queueing code from ringing the doorbell until this + * command completes. The HC won't set the dequeue pointer if the ring + * is running, and ringing the doorbell starts the ring. */ ep->ep_state |= SET_DEQ_PENDING; xhci_ring_cmd_db(xhci); + return 0; + +err_out: + xhci_free_command(xhci, cmd); + ep->queued_deq_seg = NULL; + ep->queued_deq_ptr = NULL; + + return ret; } /* flip_cycle means flip the cycle bit of all but the first and last TRB. @@ -950,7 +932,7 @@ static void xhci_dequeue_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xh } /* Complete the cancelled URBs we unlinked from td_list. */ -static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep) +static void xhci_giveback_invalidated_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) { struct xhci_ring *ring; struct xhci_td *td, *tmp_td; @@ -958,17 +940,17 @@ static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep) list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { - ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb); + ring = xhci_urb_to_transfer_ring(xhci, td->urb); if (td->cancel_status == TD_CLEARED) { - xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n", + xhci_dbg(xhci, "%s: Giveback cancelled URB %p TD\n", __func__, td->urb); - xhci_td_cleanup(ep->xhci, td, ring, td->status); + xhci_td_cleanup(xhci, td, ring, td->status); } else { - xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", + xhci_dbg(xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", __func__, td->urb, td->cancel_status); } - if (ep->xhci->xhc_state & XHCI_STATE_DYING) + if (xhci->xhc_state & XHCI_STATE_DYING) return; } } @@ -1002,15 +984,19 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci, struct xhci_td *td, enum xhci_ep_reset_type reset_type) { + struct xhci_port *rhub_port = ep->vdev->rhub_port; unsigned int slot_id = ep->vdev->slot_id; int err; /* - * Avoid resetting endpoint if link is inactive. Can cause host hang. - * Device will be reset soon to recover the link so don't do anything + * Avoid resetting endpoint if link is inactive or device disonnected. + * Can cause host hang. + * Device will be reset to recover an inactive link, so don't do anything */ - if (ep->vdev->flags & VDEV_PORT_ERROR) + if (rhub_port->link_inactive || !rhub_port->connected) { + ep->ep_state |= EP_DROP_PENDING; return -ENODEV; + } /* add td to cancelled list and let reset ep handler take care of it */ if (reset_type == EP_HARD_RESET) { @@ -1047,15 +1033,13 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci, * only call this when ring is not in a running state */ -static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) +static int xhci_invalidate_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) { - struct xhci_hcd *xhci; struct xhci_td *td = NULL; struct xhci_td *tmp_td = NULL; struct xhci_td *cached_td = NULL; struct xhci_ring *ring; u64 hw_deq; - unsigned int slot_id = ep->vdev->slot_id; int err; /* @@ -1065,8 +1049,6 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) if (ep->ep_state & SET_DEQ_PENDING) return 0; - xhci = ep->xhci; - list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Removing canceled TD starting at 0x%llx (dma) in stream %u URB %p", @@ -1080,6 +1062,13 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) td->urb, td->urb->stream_id); continue; } + + /* device disconnected or link error, ep will be dropped */ + if (ep->ep_state & EP_DROP_PENDING) { + td->cancel_status = TD_CLEARED; + continue; + } + /* * If a ring stopped on the TD we need to cancel then we have to * move the xHC endpoint ring dequeue pointer past this TD. @@ -1131,9 +1120,8 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) if (!cached_td) return 0; - err = xhci_move_dequeue_past_td(xhci, slot_id, ep->ep_index, - cached_td->urb->stream_id, - cached_td); + err = xhci_move_deq_to_next_td(xhci, ep, cached_td->urb->stream_id); + if (err) { /* Failed to move past cached td, just set cached TDs to no-op */ list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { @@ -1161,23 +1149,23 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) * * Call under xhci->lock on a stopped endpoint. */ -void xhci_process_cancelled_tds(struct xhci_virt_ep *ep) +void xhci_process_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) { - xhci_invalidate_cancelled_tds(ep); - xhci_giveback_invalidated_tds(ep); + xhci_invalidate_cancelled_tds(xhci, ep); + xhci_giveback_invalidated_tds(xhci, ep); } /* * Returns the TD the endpoint ring halted on. * Only call for non-running rings without streams. */ -static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep) +static struct xhci_td *find_halted_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) { struct xhci_td *td; u64 hw_deq; if (!list_empty(&ep->ring->td_list)) { /* Not streams compatible */ - hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0); + hw_deq = xhci_get_hw_deq(xhci, ep->vdev, ep->ep_index, 0); hw_deq &= TR_DEQ_PTR_MASK; td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list); if (trb_in_td(td, hw_deq)) @@ -1252,7 +1240,7 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, reset_type = EP_SOFT_RESET; } else { reset_type = EP_HARD_RESET; - td = find_halted_td(ep); + td = find_halted_td(xhci, ep); if (td) td->status = -EPROTO; } @@ -1310,13 +1298,17 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, } } + /* link is inactive or disconnected, ep is not running and shouldn't be restarted */ + if (ep->vdev->rhub_port->link_inactive || !ep->vdev->rhub_port->connected) + ep->ep_state |= EP_DROP_PENDING; + /* will queue a set TR deq if stopped on a cancelled, uncleared TD */ - xhci_invalidate_cancelled_tds(ep); + xhci_invalidate_cancelled_tds(xhci, ep); ep->ep_state &= ~EP_STOP_CMD_PENDING; /* Otherwise ring the doorbell(s) to restart queued transfers */ - xhci_giveback_invalidated_tds(ep); - ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + xhci_giveback_invalidated_tds(xhci, ep); + xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring) @@ -1537,14 +1529,14 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, /* HW cached TDs cleared from cache, give them back */ list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { - ep_ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb); + ep_ring = xhci_urb_to_transfer_ring(xhci, td->urb); if (td->cancel_status == TD_CLEARING_CACHE) { td->cancel_status = TD_CLEARED; - xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n", + xhci_dbg(xhci, "%s: Giveback cancelled URB %p TD\n", __func__, td->urb); - xhci_td_cleanup(ep->xhci, td, ep_ring, td->status); + xhci_td_cleanup(xhci, td, ep_ring, td->status); } else { - xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", + xhci_dbg(xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", __func__, td->urb, td->cancel_status); } } @@ -1555,17 +1547,17 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, /* Check for deferred or newly cancelled TDs */ if (!list_empty(&ep->cancelled_td_list)) { - xhci_dbg(ep->xhci, "%s: Pending TDs to clear, continuing with invalidation\n", + xhci_dbg(xhci, "%s: Pending TDs to clear, continuing with invalidation\n", __func__); - xhci_invalidate_cancelled_tds(ep); + xhci_invalidate_cancelled_tds(xhci, ep); /* Try to restart the endpoint if all is done */ - ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index); /* Start giving back any TDs invalidated above */ - xhci_giveback_invalidated_tds(ep); + xhci_giveback_invalidated_tds(xhci, ep); } else { /* Restart any rings with pending URBs */ - xhci_dbg(ep->xhci, "%s: All TDs cleared, ring doorbell\n", __func__); - ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + xhci_dbg(xhci, "%s: All TDs cleared, ring doorbell\n", __func__); + xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } } @@ -1591,16 +1583,16 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, "Ignoring reset ep completion code of %u", cmd_comp_code); /* Cleanup cancelled TDs as ep is stopped. May queue a Set TR Deq cmd */ - xhci_invalidate_cancelled_tds(ep); + xhci_invalidate_cancelled_tds(xhci, ep); /* Clear our internal halted state */ ep->ep_state &= ~EP_HALTED; - xhci_giveback_invalidated_tds(ep); + xhci_giveback_invalidated_tds(xhci, ep); /* if this was a soft reset, then restart */ if ((le32_to_cpu(trb->generic.field[3])) & TRB_TSP) - ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + xhci_ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } static void xhci_handle_cmd_enable_slot(int slot_id, struct xhci_command *command, @@ -2008,13 +2000,15 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci) static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) { struct xhci_virt_device *vdev = NULL; - struct usb_hcd *hcd; - u32 port_id; - u32 portsc, cmd_reg; - unsigned int hcd_portnum; struct xhci_bus_state *bus_state; - bool bogus_port_status = false; struct xhci_port *port; + struct usb_hcd *hcd; + bool bogus_port_status = false; + unsigned int hcd_portnum; + u32 cmd_reg; + u32 port_id; + u32 portsc; + u32 pls; /* Port status change events always have a successful completion code */ if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS) @@ -2051,6 +2045,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) bus_state = &port->rhub->bus_state; hcd_portnum = port->hcd_portnum; portsc = xhci_portsc_readl(port); + pls = portsc & PORT_PLS_MASK; xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n", hcd->self.busnum, hcd_portnum + 1, port_id, portsc); @@ -2062,11 +2057,13 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) usb_hcd_resume_root_hub(hcd); } - if (vdev && (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) { - if (!(portsc & PORT_RESET)) - vdev->flags |= VDEV_PORT_ERROR; - } else if (vdev && portsc & PORT_RC) { - vdev->flags &= ~VDEV_PORT_ERROR; + /* + * Tag broken links to avoid retries while hub driver sorts it out. + * Link status is not relible while port is in reset. + */ + if (!(portsc & PORT_RESET)) { + port->link_inactive = (pls == XDEV_INACTIVE); + port->connected = !!(portsc & PORT_CONNECT); } if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) { @@ -3978,88 +3975,102 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci, /* Returns the Isochronous Scheduling Threshold in Microframes. 1 Frame is 8 Microframes. */ static int xhci_ist_microframes(struct xhci_hcd *xhci) { - int ist = HCS_IST_VALUE(xhci->hcs_params2); + int ist = FIELD_GET(HCS_IST_VALUE, xhci->hcs_params2); if (xhci->hcs_params2 & HCS_IST_UNIT) ist *= 8; return ist; } -/* - * Calculates Frame ID field of the isochronous TRB identifies the - * target frame that the Interval associated with this Isochronous - * Transfer Descriptor will start on. Refer to 4.11.2.5 in 1.1 spec. - * - * Returns actual frame id on success, negative value on error. - */ -static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci, - struct urb *urb, int index) + +static bool xhci_isoc_td_uses_frame_id(struct xhci_hcd *xhci, struct urb *urb, + struct xhci_virt_ep *ep, int i) { - int start_frame, ist, ret = 0; - int start_frame_id, end_frame_id, current_frame_id; + if (urb->transfer_flags & URB_ISO_ASAP) + return false; - if (urb->dev->speed == USB_SPEED_LOW || - urb->dev->speed == USB_SPEED_FULL) - start_frame = urb->start_frame + index * urb->interval; - else - start_frame = (urb->start_frame + index * urb->interval) >> 3; + if (xhci->hcc_params & HCC_CFC) + return true; + /* set frame id for first TD of first URB in stream */ + if (ep->next_uframe == -1 && i == 0) + return true; + + return false; +} + +/* + * Check if frame is in the valid frame window, including start and end. + * If start > end then assume window wrapped around at a limit the frame + * value won't exceed. + */ +static bool xhci_frame_in_range(u32 frame, u32 start, u32 end) +{ + /* frame window end wrapped around */ + if (start > end) + return frame >= start || frame <= end; + + return frame >= start && frame <= end; +} + +/* + * Set the urb->start_frame of the URB. + * + * Returns microframe index of first TD + */ +static int xhci_get_isoc_start_frame(struct xhci_hcd *xhci, struct urb *urb, + struct xhci_virt_ep *ep) +{ + u32 curr_frame, start_uframe; + u32 urb_start, urb_end; + u32 win_start, win_end; + bool frame_unit; + int uinterval; + u32 mfindex; + int ist; + + /* check if urb uses frame units instead of microframes */ + frame_unit = (urb->dev->speed == USB_SPEED_FULL || + urb->dev->speed == USB_SPEED_LOW); + + uinterval = urb->interval; + if (frame_unit) + uinterval *= 8; + + /* get current microframe index and isoc scheduling threshold */ + mfindex = readl(&xhci->run_regs->microframe_index); ist = xhci_ist_microframes(xhci); - /* Software shall not schedule an Isoch TD with a Frame ID value that - * is less than the Start Frame ID or greater than the End Frame ID, - * where: - * - * End Frame ID = (Current MFINDEX register value + 895 ms.) MOD 2048 - * Start Frame ID = (Current MFINDEX register value + IST + 1) MOD 2048 - * - * Both the End Frame ID and Start Frame ID values are calculated - * in microframes. When software determines the valid Frame ID value; - * The End Frame ID value should be rounded down to the nearest Frame - * boundary, and the Start Frame ID value should be rounded up to the - * nearest Frame boundary. - */ - current_frame_id = readl(&xhci->run_regs->microframe_index); - start_frame_id = roundup(current_frame_id + ist + 1, 8); - end_frame_id = rounddown(current_frame_id + 895 * 8, 8); + /* calculate valid frame window, in frame units, see xhci 4.11.2.5 */ + curr_frame = MFINDEX_TO_FRAME(mfindex); + win_start = (curr_frame + DIV_ROUND_UP_POW2(ist, 8) + 1) % MAX_FRAMES; + win_end = (curr_frame + 895) % MAX_FRAMES; - start_frame &= 0x7ff; - start_frame_id = (start_frame_id >> 3) & 0x7ff; - end_frame_id = (end_frame_id >> 3) & 0x7ff; - - if (start_frame_id < end_frame_id) { - if (start_frame > end_frame_id || - start_frame < start_frame_id) - ret = -EINVAL; - } else if (start_frame_id > end_frame_id) { - if ((start_frame > end_frame_id && - start_frame < start_frame_id)) - ret = -EINVAL; + /* Is this the first URB starting the whole isoc data flow? */ + if (ep->next_uframe < 0) { + /* align first URB to next interval boundary, or at last to full frame */ + start_uframe = mfindex + ist + XHCI_CFC_DELAY; + start_uframe = roundup(start_uframe, 8); + start_uframe = roundup(start_uframe, uinterval) % MAX_UFRAMES; } else { - ret = -EINVAL; - } + /* URB is mid stream and expected to handle the next frame */ + start_uframe = ep->next_uframe; + urb_start = start_uframe / 8; + urb_end = (start_uframe + urb->number_of_packets * uinterval) / 8; + urb_end %= MAX_FRAMES; - if (index == 0) { - if (ret == -EINVAL || start_frame == start_frame_id) { - start_frame = start_frame_id + 1; - if (urb->dev->speed == USB_SPEED_LOW || - urb->dev->speed == USB_SPEED_FULL) - urb->start_frame = start_frame; - else - urb->start_frame = start_frame << 3; - ret = 0; - } - } + if (!xhci_frame_in_range(urb_start, win_start, win_end)) + xhci_dbg(xhci, "Ill-timed isoc URB %p for start frame %d, range %d-%d\n", + urb, urb_start, win_start, win_end); - if (ret) { - xhci_warn(xhci, "Frame ID %d (reg %d, index %d) beyond range (%d, %d)\n", - start_frame, current_frame_id, index, - start_frame_id, end_frame_id); - xhci_warn(xhci, "Ignore frame ID field, use SIA bit instead\n"); - return ret; + if (!xhci_frame_in_range(urb_end, win_start, win_end)) + xhci_dbg(xhci, "Ill-timed isoc URB %p for end frame %d, range %d-%d\n", + urb, urb_start, win_start, win_end); } + /* set urb->start_frame */ + urb->start_frame = frame_unit ? start_uframe / 8 : start_uframe; - return start_frame; + return start_uframe; } /* Check if we should generate event interrupt for a TD in an isoc URB */ @@ -4099,7 +4110,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, int i, j; bool more_trbs_coming; struct xhci_virt_ep *xep; - int frame_id; + int uinterval = urb->interval; + int start_uframe; xep = &xhci->devs[slot_id]->eps[ep_index]; ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; @@ -4115,6 +4127,12 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, start_cycle = ep_ring->cycle_state; urb_priv = urb->hcpriv; + + if (urb->dev->speed == USB_SPEED_FULL || urb->dev->speed == USB_SPEED_LOW) + uinterval = urb->interval * 8; + + start_uframe = xhci_get_isoc_start_frame(xhci, urb, xep); + /* Queue the TRBs for each TD, even if they are zero-length */ for (i = 0; i < num_tds; i++) { unsigned int total_pkt_count, max_pkt; @@ -4146,14 +4164,16 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, goto cleanup; } td = &urb_priv->td[i]; - /* use SIA as default, if frame id is used overwrite it */ - sia_frame_id = TRB_SIA; - if (!(urb->transfer_flags & URB_ISO_ASAP) && - (xhci->hcc_params & HCC_CFC)) { - frame_id = xhci_get_isoc_frame_id(xhci, urb, i); - if (frame_id >= 0) - sia_frame_id = TRB_FRAME_ID(frame_id); + + + /* Choose SIA or frame ID based scheduling for this TD */ + if (xhci_isoc_td_uses_frame_id(xhci, urb, xep, i)) { + sia_frame_id = (start_uframe + i * uinterval) / 8; + sia_frame_id = TRB_FRAME_ID(sia_frame_id % MAX_FRAMES); + } else { + sia_frame_id = TRB_SIA; } + /* * Set isoc specific data for the first TRB in a TD. * Prevent HW from getting the TRBs by keeping the cycle state @@ -4232,9 +4252,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } } - /* store the next frame id */ - if (xhci->hcc_params & HCC_CFC) - xep->next_frame_id = urb->start_frame + num_tds * urb->interval; + xep->next_uframe = (start_uframe + num_tds * uinterval) % MAX_UFRAMES; if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) { if (xhci->quirks & XHCI_AMD_PLL_FIX) @@ -4281,11 +4299,9 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, struct xhci_virt_device *xdev; struct xhci_ring *ep_ring; struct xhci_ep_ctx *ep_ctx; - int start_frame; + struct xhci_virt_ep *xep; int num_tds, num_trbs, i; int ret; - struct xhci_virt_ep *xep; - int ist; xdev = xhci->devs[slot_id]; xep = &xhci->devs[slot_id]->eps[ep_index]; @@ -4311,38 +4327,12 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, */ check_interval(urb, ep_ctx); - /* Calculate the start frame and put it in urb->start_frame. */ - if ((xhci->hcc_params & HCC_CFC) && !list_empty(&ep_ring->td_list)) { - if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) { - urb->start_frame = xep->next_frame_id; - goto skip_start_over; - } - } - - start_frame = readl(&xhci->run_regs->microframe_index); - start_frame &= 0x3fff; /* - * Round up to the next frame and consider the time before trb really - * gets scheduled by hardare. + * Check if this starts the isoc data flow. Relies on hw setting ep ctx + * state after doorbell ring. Consider adding list_empty(td_list) check */ - ist = xhci_ist_microframes(xhci); - start_frame += ist + XHCI_CFC_DELAY; - start_frame = roundup(start_frame, 8); - - /* - * Round up to the next ESIT (Endpoint Service Interval Time) if ESIT - * is greate than 8 microframes. - */ - if (urb->dev->speed == USB_SPEED_LOW || - urb->dev->speed == USB_SPEED_FULL) { - start_frame = roundup(start_frame, urb->interval << 3); - urb->start_frame = start_frame >> 3; - } else { - start_frame = roundup(start_frame, urb->interval); - urb->start_frame = start_frame; - } - -skip_start_over: + if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING) + xep->next_uframe = -1; return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index); } diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -6,6 +6,7 @@ * Copyright (C) 2014 Google, Inc. */ +#include #include #include #include @@ -993,7 +994,7 @@ static int tegra_xusb_wait_for_falcon(struct tegra_xusb *tegra) u32 value; cap_regs = tegra->regs; - op_regs = tegra->regs + HC_LENGTH(readl(&cap_regs->hc_capbase)); + op_regs = tegra->regs + FIELD_GET(HC_LENGTH, readl(&cap_regs->hc_capbase)), ret = readl_poll_timeout(&op_regs->status, value, !(value & STS_CNR), 1000, 200000); @@ -1895,7 +1896,7 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto remove_usb2; } - if (HCC_MAX_PSA(xhci->hcc_params) >= 4) + if (GET_MAX_PSA_SIZE(xhci->hcc_params) >= 4) xhci->shared_hcd->can_do_streams = 1; err = usb_add_hcd(xhci->shared_hcd, tegra->xhci_irq, IRQF_SHARED); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "xhci.h" #include "xhci-trace.h" @@ -1664,7 +1665,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag goto free_priv; } - if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) { + if (xhci->devs[slot_id]->rhub_port->link_inactive) { xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n"); ret = -ENODEV; goto free_priv; @@ -1852,11 +1853,11 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) } /* In this case no commands are pending but the endpoint is stopped */ - if (ep->ep_state & EP_CLEARING_TT) { + if (ep->ep_state & (EP_CLEARING_TT | EP_DROP_PENDING)) { /* and cancelled TDs can be given back right away */ xhci_dbg(xhci, "Invalidating TDs instantly on slot %d ep %d in state 0x%x\n", urb->dev->slot_id, ep_index, ep->ep_state); - xhci_process_cancelled_tds(ep); + xhci_process_cancelled_tds(xhci, ep); } else { /* Otherwise, queue a new Stop Endpoint command */ command = xhci_alloc_command(xhci, false, GFP_ATOMIC); @@ -3507,7 +3508,7 @@ static void xhci_calculate_streams_entries(struct xhci_hcd *xhci, * level page entries), but that's an optional feature for xHCI host * controllers. xHCs must support at least 4 stream IDs. */ - max_streams = HCC_MAX_PSA(xhci->hcc_params); + max_streams = GET_MAX_PSA_SIZE(xhci->hcc_params); if (*num_stream_ctxs > max_streams) { xhci_dbg(xhci, "xHCI HW only supports %u stream ctx entries.\n", max_streams); @@ -3637,7 +3638,7 @@ static int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, /* MaxPSASize value 0 (2 streams) means streams are not supported */ if ((xhci->quirks & XHCI_BROKEN_STREAMS) || - HCC_MAX_PSA(xhci->hcc_params) < 4) { + GET_MAX_PSA_SIZE(xhci->hcc_params) < 4) { xhci_dbg(xhci, "xHCI controller does not support streams.\n"); return -ENOSYS; } @@ -4035,7 +4036,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, xhci_get_slot_state(xhci, virt_dev->out_ctx)); xhci_dbg(xhci, "Not freeing device rings.\n"); /* Don't treat this as an error. May change my mind later. */ - virt_dev->flags = 0; ret = 0; goto command_cleanup; case COMP_SUCCESS: @@ -4087,10 +4087,12 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, } /* If necessary, update the number of active TTs on this root port */ xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps); - virt_dev->flags = 0; ret = 0; command_cleanup: + for (i = 0; i < EP_CTX_PER_DEV; i++) + virt_dev->eps[i].ep_state &= ~EP_DROP_PENDING; + xhci_free_command(xhci, reset_device_cmd); return ret; } @@ -4607,7 +4609,7 @@ static int xhci_calculate_hird_besl(struct xhci_hcd *xhci, int besl_device = 0; u32 field; - u2del = HCS_U2_LATENCY(xhci->hcs_params3); + u2del = FIELD_GET(HCS_U2_LATENCY, xhci->hcs_params3); field = le32_to_cpu(udev->bos->ext_cap->bmAttributes); if (field & USB_BESL_SUPPORT) { @@ -5459,26 +5461,28 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci_warn(xhci, "Host controller not accessible, removed?\n"); return -ENODEV; } - xhci->op_regs = hcd->regs + HC_LENGTH(hc_capbase); + xhci->op_regs = hcd->regs + FIELD_GET(HC_LENGTH, hc_capbase); + xhci->run_regs = hcd->regs + (readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK); /* Cache read-only capability registers */ hcs_params1 = readl(&xhci->cap_regs->hcs_params1); xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2); xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3); - xhci->hci_version = HC_VERSION(hc_capbase); + xhci->hci_version = FIELD_GET(HC_VERSION, hc_capbase); xhci->hcc_params = readl(&xhci->cap_regs->hcc_params); if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); xhci->dma_mask_bits = 64; - xhci->max_slots = min(HCS_MAX_SLOTS(hcs_params1), MAX_HC_SLOTS); - xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS); + xhci->max_slots = min(FIELD_GET(HCS_SLOTS_MASK, hcs_params1), MAX_HC_SLOTS); + xhci->max_ports = min(FIELD_GET(HCS_MAX_PORTS, hcs_params1), MAX_HC_PORTS); + /* xhci-plat or xhci-pci might have set max_interrupters already */ if (!xhci->max_interrupters) - xhci->max_interrupters = min(HCS_MAX_INTRS(hcs_params1), MAX_HC_INTRS); - else if (xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1)) - xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1); + xhci->max_interrupters = min(FIELD_GET(HCS_MAX_INTRS, hcs_params1), MAX_HC_INTRS); + else if (xhci->max_interrupters > FIELD_GET(HCS_MAX_INTRS, hcs_params1)) + xhci->max_interrupters = FIELD_GET(HCS_MAX_INTRS, hcs_params1); xhci->quirks |= quirks; @@ -5519,7 +5523,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) * DMA_BIT_MASK(32)) in this xhci_gen_setup(). */ if (xhci->quirks & XHCI_NO_64BIT_SUPPORT) - xhci->hcc_params &= ~BIT(0); + xhci->hcc_params &= ~HCC_64BIT_ADDR; /* * Set dma_mask and coherent_dma_mask to 64-bits if xHC supports diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -290,6 +290,11 @@ struct xhci_run_regs { struct xhci_intr_reg ir_set[1024]; }; +/* Bits [13:3] of the microframe index equals the 1ms frame index */ +#define MFINDEX_TO_FRAME(p) (((p) >> 3) & 0x7ff) +#define MAX_FRAMES 2048 +#define MAX_UFRAMES (MAX_FRAMES * 8) + /** * struct doorbell_array * @@ -677,9 +682,9 @@ struct xhci_virt_ep { #define EP_SOFT_CLEAR_TOGGLE BIT(7) /* usb_hub_clear_tt_buffer is in progress */ #define EP_CLEARING_TT BIT(8) +#define EP_DROP_PENDING BIT(9) /* port disconnect or link error, don't restart */ /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; - struct xhci_hcd *xhci; /* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue * command. We'll need to update the ring's dequeue segment and dequeue * pointer after the command completes. @@ -699,7 +704,7 @@ struct xhci_virt_ep { struct list_head bw_endpoint_list; unsigned long stop_time; /* Isoch Frame ID checking storage */ - int next_frame_id; + int next_uframe; /* Use new Isoch TRB layout needed for extended TBC support */ bool use_extended_tbc; /* set if this endpoint is controlled via sideband access*/ @@ -753,14 +758,6 @@ struct xhci_virt_device { struct xhci_port *rhub_port; struct xhci_interval_bw_table *bw_table; struct xhci_tt_bw_info *tt_info; - /* - * flags for state tracking based on events and issued commands. - * Software can not rely on states from output contexts because of - * latency between events and xHC updating output context values. - * See xhci 1.1 section 4.8.3 for more details - */ - unsigned long flags; -#define VDEV_PORT_ERROR BIT(0) /* Port error, link inactive */ /* The current max exit latency for the enabled USB3 link states. */ u16 current_mel; @@ -1480,6 +1477,8 @@ struct xhci_port { int hcd_portnum; struct xhci_hub *rhub; struct xhci_port_cap *port_cap; + unsigned int link_inactive:1; + unsigned int connected:1; unsigned int lpm_incapable:1; unsigned long resume_timestamp; bool rexit_active; @@ -1961,7 +1960,7 @@ unsigned int count_trbs(u64 addr, u64 len); unsigned int xhci_num_trbs_free(struct xhci_ring *ring); int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int suspend, gfp_t gfp_flags); -void xhci_process_cancelled_tds(struct xhci_virt_ep *ep); +void xhci_process_cancelled_tds(struct xhci_hcd *xhci, struct xhci_virt_ep *ep); void xhci_update_erst_dequeue(struct xhci_hcd *xhci, struct xhci_interrupter *ir, bool clear_ehb); diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -89,8 +89,11 @@ static int vendor_command(struct cypress *dev, unsigned char request, address, data, iobuf, CYPRESS_MAX_REQSIZE, USB_CTRL_GET_TIMEOUT); /* we must not process garbage */ - if (retval < 2) + if (retval < 2) { + if (retval >= 0) + retval = -EIO; goto err_buf; + } /* store returned data (more READs to be added) */ switch (request) { @@ -175,8 +178,9 @@ static ssize_t read_port(struct device *dev, struct device_attribute *attr, dev_dbg(&cyp->udev->dev, "READ_PORT%d called\n", port_num); result = vendor_command(cyp, CYPRESS_READ_PORT, read_id, 0); - dev_dbg(&cyp->udev->dev, "Result of vendor_command: %d\n\n", result); + if (result < 0) + return result; return sprintf(buf, "%d", cyp->port[port_num]); } diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -223,6 +223,7 @@ static int get_1284_register(struct parport *pp, unsigned char reg, unsigned cha } printk(KERN_WARNING "get_1284_register timeout\n"); kill_all_async_requests_priv(priv); + kref_put(&rq->ref_count, destroy_async); return -EIO; } diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -46,6 +46,14 @@ #define WC1_IS_P_95 BIT(12) #define WC1_IS_EN_P0_95 BIT(6) +/* mt8196 */ +#define PERI_WK_CTRL0_8196 0x08 +#define WC0_IS_EN_P0_96 BIT(0) +#define WC0_IS_EN_P1_96 BIT(7) + +#define PERI_WK_CTRL1_8196 0x10 +#define WC1_IS_EN_P2_96 BIT(0) + /* mt2712 etc */ #define PERI_SSUSB_SPM_CTRL 0x0 #define SSC_IP_SLEEP_EN BIT(4) @@ -59,6 +67,9 @@ enum ssusb_uwk_vers { SSUSB_UWK_V1_3, /* mt8195 IP0 */ SSUSB_UWK_V1_5 = 105, /* mt8195 IP2 */ SSUSB_UWK_V1_6, /* mt8195 IP3 */ + SSUSB_UWK_V1_7, /* mt8196 IP0 */ + SSUSB_UWK_V1_8, /* mt8196 IP1 */ + SSUSB_UWK_V1_9, /* mt8196 IP2 */ }; /* @@ -100,6 +111,21 @@ static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable) msk = WC0_IS_EN_P3_95 | WC0_IS_C_95(0x7) | WC0_IS_P_95; val = enable ? (WC0_IS_EN_P3_95 | WC0_IS_C_95(0x1)) : 0; break; + case SSUSB_UWK_V1_7: + reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8196; + msk = WC0_IS_EN_P0_96; + val = enable ? msk : 0; + break; + case SSUSB_UWK_V1_8: + reg = ssusb->uwk_reg_base + PERI_WK_CTRL0_8196; + msk = WC0_IS_EN_P1_96; + val = enable ? msk : 0; + break; + case SSUSB_UWK_V1_9: + reg = ssusb->uwk_reg_base + PERI_WK_CTRL1_8196; + msk = WC1_IS_EN_P2_96; + val = enable ? msk : 0; + break; case SSUSB_UWK_V2: reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL; msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN; diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -544,7 +544,8 @@ static int mtu3_suspend_common(struct device *dev, pm_message_t msg) ssusb_phy_power_off(ssusb); clk_bulk_disable_unprepare(BULK_CLKS_CNT, ssusb->clks); - ssusb_wakeup_set(ssusb, true); + if (device_may_wakeup(dev) && (ssusb->is_host || PMSG_IS_AUTO(msg))) + ssusb_wakeup_set(ssusb, true); return 0; sleep_err: diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -116,7 +116,6 @@ config USB_MUSB_POLARFIRE_SOC tristate "Microchip PolarFire SoC platforms" depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST depends on NOP_USB_XCEIV - select USB_MUSB_DUAL_ROLE help Say Y here to enable support for USB on Microchip's PolarFire SoC. diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1774,7 +1774,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) status = -EPIPE; } else if (rx_csr & MUSB_RXCSR_H_ERROR) { - dev_err(musb->controller, "ep%d RX three-strikes error", epnum); + dev_err_ratelimited(musb->controller, + "ep%d RX three-strikes error\n", epnum); /* * The three-strikes error could only happen when the USB @@ -1788,6 +1789,17 @@ void musb_host_rx(struct musb *musb, u8 epnum) rx_csr &= ~MUSB_RXCSR_H_ERROR; musb_writew(epio, MUSB_RXCSR, rx_csr); + /* + * Unplugging a USB-Ethernet adapter while it is busy can make + * the controller keep re-asserting the three-strikes error for + * this endpoint before the disconnect is processed. That floods + * the log and can wedge the host port until reboot. Drop the + * stale pending RX interrupt on platforms that support it (e.g. + * AM335x/DSPS) to break the storm; the transfer is still + * aborted below via the fault path. + */ + musb_platform_clear_ep_rxintr(musb, epnum); + } else if (rx_csr & MUSB_RXCSR_DATAERROR) { if (USB_ENDPOINT_XFER_ISOC != qh->type) { diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -32,10 +32,6 @@ /* so we can be sure to send the full buffer in one urb */ #define DIGI_OUT_BUF_SIZE 8 -/* port input buffer length -- must be >= transfer buffer length - 3 */ -/* so we can be sure to hold at least one full buffer from one urb */ -#define DIGI_IN_BUF_SIZE 64 - /* retry timeout while sleeping */ #define DIGI_RETRY_TIMEOUT (HZ/10) @@ -176,10 +172,10 @@ /* Structures */ struct digi_serial { - spinlock_t ds_serial_lock; + struct mutex open_mutex; struct usb_serial_port *ds_oob_port; /* out-of-band port */ int ds_oob_port_num; /* index of out-of-band port */ - int ds_device_started; + int open_count; }; struct digi_port { @@ -194,7 +190,6 @@ struct digi_port { int dp_throttled; int dp_throttle_restart; wait_queue_head_t dp_flush_wait; - wait_queue_head_t dp_close_wait; /* wait queue for close */ wait_queue_head_t write_wait; struct usb_serial_port *dp_port; }; @@ -227,9 +222,7 @@ static unsigned int digi_chars_in_buffer(struct tty_struct *tty); static int digi_open(struct tty_struct *tty, struct usb_serial_port *port); static void digi_close(struct usb_serial_port *port); static void digi_dtr_rts(struct usb_serial_port *port, int on); -static int digi_startup_device(struct usb_serial *serial); static int digi_startup(struct usb_serial *serial); -static void digi_disconnect(struct usb_serial *serial); static void digi_release(struct usb_serial *serial); static int digi_port_probe(struct usb_serial_port *port); static void digi_port_remove(struct usb_serial_port *port); @@ -272,7 +265,7 @@ static struct usb_serial_driver digi_acceleport_2_device = { .dtr_rts = digi_dtr_rts, .write = digi_write, .write_room = digi_write_room, - .write_bulk_callback = digi_write_bulk_callback, + .write_bulk_callback = digi_write_bulk_callback, .read_bulk_callback = digi_read_bulk_callback, .chars_in_buffer = digi_chars_in_buffer, .throttle = digi_rx_throttle, @@ -282,7 +275,6 @@ static struct usb_serial_driver digi_acceleport_2_device = { .tiocmget = digi_tiocmget, .tiocmset = digi_tiocmset, .attach = digi_startup, - .disconnect = digi_disconnect, .release = digi_release, .port_probe = digi_port_probe, .port_remove = digi_port_remove, @@ -301,7 +293,7 @@ static struct usb_serial_driver digi_acceleport_4_device = { .close = digi_close, .write = digi_write, .write_room = digi_write_room, - .write_bulk_callback = digi_write_bulk_callback, + .write_bulk_callback = digi_write_bulk_callback, .read_bulk_callback = digi_read_bulk_callback, .chars_in_buffer = digi_chars_in_buffer, .throttle = digi_rx_throttle, @@ -311,7 +303,6 @@ static struct usb_serial_driver digi_acceleport_4_device = { .tiocmget = digi_tiocmget, .tiocmset = digi_tiocmset, .attach = digi_startup, - .disconnect = digi_disconnect, .release = digi_release, .port_probe = digi_port_probe, .port_remove = digi_port_remove, @@ -335,7 +326,6 @@ static struct usb_serial_driver * const serial_drivers[] = { * interruptible_sleep_on_timeout is deprecated and has been replaced * with the equivalent code. */ - static long cond_wait_interruptible_timeout_irqrestore( wait_queue_head_t *q, long timeout, spinlock_t *lock, unsigned long flags) @@ -351,6 +341,13 @@ __releases(lock) return timeout; } +static struct usb_serial_port *digi_get_oob_port(struct usb_serial *serial) +{ + struct digi_serial *serial_priv = usb_get_serial_data(serial); + + return serial_priv->ds_oob_port; +} + /* * Digi Write OOB Command * @@ -361,15 +358,14 @@ __releases(lock) * the interruptible flag is true, or a negative error * returned by usb_submit_urb. */ - static int digi_write_oob_command(struct usb_serial_port *port, unsigned char *buf, int count, int interruptible) { - int ret = 0; - int len; - struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port; + struct usb_serial_port *oob_port = digi_get_oob_port(port->serial); struct digi_port *oob_priv = usb_get_serial_port_data(oob_port); unsigned long flags; + int ret = 0; + int len; dev_dbg(&port->dev, "digi_write_oob_command: TOP: port=%d, count=%d\n", @@ -406,10 +402,8 @@ static int digi_write_oob_command(struct usb_serial_port *port, dev_err(&port->dev, "%s: usb_submit_urb failed, ret=%d\n", __func__, ret); return ret; - } - /* * Digi Write In Band Command * @@ -421,16 +415,15 @@ static int digi_write_oob_command(struct usb_serial_port *port, * timeout ticks. Returns 0 if successful, or a negative * error returned by digi_write. */ - static int digi_write_inb_command(struct usb_serial_port *port, unsigned char *buf, int count, unsigned long timeout) { - int ret = 0; - int len; struct digi_port *priv = usb_get_serial_port_data(port); unsigned char *data = port->write_urb->transfer_buffer; unsigned long expire; unsigned long flags; + int ret = 0; + int len; dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n", priv->dp_port_num, count); @@ -459,7 +452,7 @@ static int digi_write_inb_command(struct usb_serial_port *port, /* len must be a multiple of 4 and small enough to */ /* guarantee the write will send buffered data first, */ /* so commands are in order with data and not split */ - len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len); + len = min(count, port->bulk_out_size - 2 - priv->dp_out_buf_len); if (len > 4) len &= ~3; @@ -467,11 +460,10 @@ static int digi_write_inb_command(struct usb_serial_port *port, if (priv->dp_out_buf_len > 0) { data[0] = DIGI_CMD_SEND_DATA; data[1] = priv->dp_out_buf_len; - memcpy(data + 2, priv->dp_out_buf, - priv->dp_out_buf_len); + memcpy(data + 2, priv->dp_out_buf, priv->dp_out_buf_len); memcpy(data + 2 + priv->dp_out_buf_len, buf, len); - port->write_urb->transfer_buffer_length - = priv->dp_out_buf_len + 2 + len; + port->write_urb->transfer_buffer_length = + priv->dp_out_buf_len + 2 + len; } else { memcpy(data, buf, len); port->write_urb->transfer_buffer_length = len; @@ -484,7 +476,6 @@ static int digi_write_inb_command(struct usb_serial_port *port, count -= len; buf += len; } - } spin_unlock_irqrestore(&priv->dp_port_lock, flags); @@ -495,7 +486,6 @@ static int digi_write_inb_command(struct usb_serial_port *port, return ret; } - /* * Digi Set Modem Signals * @@ -505,17 +495,15 @@ static int digi_write_inb_command(struct usb_serial_port *port, * -EINTR if interrupted while sleeping, or a non-zero error * returned by usb_submit_urb. */ - static int digi_set_modem_signals(struct usb_serial_port *port, unsigned int modem_signals, int interruptible) { - - int ret; struct digi_port *port_priv = usb_get_serial_port_data(port); - struct usb_serial_port *oob_port = (struct usb_serial_port *) ((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port; + struct usb_serial_port *oob_port = digi_get_oob_port(port->serial); struct digi_port *oob_priv = usb_get_serial_port_data(oob_port); unsigned char *data = oob_port->write_urb->transfer_buffer; unsigned long flags; + int ret; dev_dbg(&port->dev, "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x\n", @@ -573,14 +561,13 @@ static int digi_set_modem_signals(struct usb_serial_port *port, * is only called from close, and only one process can be in close on a * port at a time, so its ok. */ - static int digi_transmit_idle(struct usb_serial_port *port, unsigned long timeout) { - int ret; - unsigned char buf[2]; struct digi_port *priv = usb_get_serial_port_data(port); + unsigned char buf[2]; unsigned long flags; + int ret; spin_lock_irqsave(&priv->dp_port_lock, flags); priv->dp_transmit_idle = 0; @@ -607,16 +594,15 @@ static int digi_transmit_idle(struct usb_serial_port *port, } priv->dp_transmit_idle = 0; spin_unlock_irqrestore(&priv->dp_port_lock, flags); + return 0; - } - static void digi_rx_throttle(struct tty_struct *tty) { - unsigned long flags; struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); + unsigned long flags; /* stop receiving characters by not resubmitting the read urb */ spin_lock_irqsave(&priv->dp_port_lock, flags); @@ -625,13 +611,12 @@ static void digi_rx_throttle(struct tty_struct *tty) spin_unlock_irqrestore(&priv->dp_port_lock, flags); } - static void digi_rx_unthrottle(struct tty_struct *tty) { - int ret = 0; - unsigned long flags; struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); + unsigned long flags; + int ret = 0; spin_lock_irqsave(&priv->dp_port_lock, flags); @@ -651,7 +636,6 @@ static void digi_rx_unthrottle(struct tty_struct *tty) __func__, ret, priv->dp_port_num); } - static void digi_set_termios(struct tty_struct *tty, struct usb_serial_port *port, const struct ktermios *old_termios) @@ -762,7 +746,6 @@ static void digi_set_termios(struct tty_struct *tty, /* set stop bits */ if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) { - if ((cflag & CSTOPB)) arg = DIGI_STOP_BITS_2; else @@ -772,7 +755,6 @@ static void digi_set_termios(struct tty_struct *tty, buf[i++] = priv->dp_port_num; buf[i++] = arg; buf[i++] = 0; - } /* set input flow control */ @@ -841,7 +823,6 @@ static void digi_set_termios(struct tty_struct *tty, tty_encode_baud_rate(tty, baud, baud); } - static int digi_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; @@ -855,50 +836,48 @@ static int digi_break_ctl(struct tty_struct *tty, int break_state) return digi_write_inb_command(port, buf, 4, 0); } - static int digi_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); - unsigned int val; unsigned long flags; + unsigned int val; spin_lock_irqsave(&priv->dp_port_lock, flags); val = priv->dp_modem_signals; spin_unlock_irqrestore(&priv->dp_port_lock, flags); + return val; } - static int digi_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; struct digi_port *priv = usb_get_serial_port_data(port); - unsigned int val; unsigned long flags; + unsigned int val; spin_lock_irqsave(&priv->dp_port_lock, flags); val = (priv->dp_modem_signals & ~clear) | set; spin_unlock_irqrestore(&priv->dp_port_lock, flags); + return digi_set_modem_signals(port, val, 1); } - static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { - - int ret, data_len, new_len; struct digi_port *priv = usb_get_serial_port_data(port); unsigned char *data = port->write_urb->transfer_buffer; + int ret, data_len, new_len; unsigned long flags; dev_dbg(&port->dev, "digi_write: TOP: port=%d, count=%d\n", priv->dp_port_num, count); /* copy user data (which can sleep) before getting spin lock */ - count = min(count, port->bulk_out_size-2); + count = min(count, port->bulk_out_size - 2); count = min(64, count); /* be sure only one write proceeds at a time */ @@ -920,7 +899,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, /* allow space for any buffered data and for new data, up to */ /* transfer buffer size - 2 (for command and length bytes) */ - new_len = min(count, port->bulk_out_size-2-priv->dp_out_buf_len); + new_len = min(count, port->bulk_out_size - 2 - priv->dp_out_buf_len); data_len = new_len + priv->dp_out_buf_len; if (data_len == 0) { @@ -928,7 +907,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, return 0; } - port->write_urb->transfer_buffer_length = data_len+2; + port->write_urb->transfer_buffer_length = data_len + 2; *data++ = DIGI_CMD_SEND_DATA; *data++ = data_len; @@ -954,39 +933,22 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port, "%s: usb_submit_urb failed, ret=%d, port=%d\n", __func__, ret, priv->dp_port_num); dev_dbg(&port->dev, "digi_write: returning %d\n", ret); - return ret; + return ret; } static void digi_write_bulk_callback(struct urb *urb) { - struct usb_serial_port *port = urb->context; - struct usb_serial *serial; - struct digi_port *priv; - struct digi_serial *serial_priv; + struct digi_serial *serial_priv = usb_get_serial_data(port->serial); + struct digi_port *priv = usb_get_serial_port_data(port); + unsigned char *data = urb->transfer_buffer; unsigned long flags; - int ret = 0; - int status = urb->status; bool wakeup; - - /* port and serial sanity check */ - if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) { - pr_err("%s: port or port->private is NULL, status=%d\n", - __func__, status); - return; - } - serial = port->serial; - if (serial == NULL || (serial_priv = usb_get_serial_data(serial)) == NULL) { - dev_err(&port->dev, - "%s: serial or serial->private is NULL, status=%d\n", - __func__, status); - return; - } + int ret = 0; /* handle oob callback */ if (priv->dp_port_num == serial_priv->ds_oob_port_num) { - dev_dbg(&port->dev, "digi_write_bulk_callback: oob callback\n"); spin_lock_irqsave(&priv->dp_port_lock, flags); priv->dp_write_urb_in_use = 0; wake_up_interruptible(&priv->write_wait); @@ -999,15 +961,13 @@ static void digi_write_bulk_callback(struct urb *urb) spin_lock_irqsave(&priv->dp_port_lock, flags); priv->dp_write_urb_in_use = 0; if (priv->dp_out_buf_len > 0) { - *((unsigned char *)(port->write_urb->transfer_buffer)) - = (unsigned char)DIGI_CMD_SEND_DATA; - *((unsigned char *)(port->write_urb->transfer_buffer) + 1) - = (unsigned char)priv->dp_out_buf_len; - port->write_urb->transfer_buffer_length = - priv->dp_out_buf_len + 2; - memcpy(port->write_urb->transfer_buffer + 2, priv->dp_out_buf, - priv->dp_out_buf_len); - ret = usb_submit_urb(port->write_urb, GFP_ATOMIC); + data[0] = DIGI_CMD_SEND_DATA; + data[1] = priv->dp_out_buf_len; + memcpy(data + 2, priv->dp_out_buf, priv->dp_out_buf_len); + + urb->transfer_buffer_length = priv->dp_out_buf_len + 2; + + ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret == 0) { priv->dp_write_urb_in_use = 1; priv->dp_out_buf_len = 0; @@ -1041,8 +1001,8 @@ static unsigned int digi_write_room(struct tty_struct *tty) spin_unlock_irqrestore(&priv->dp_port_lock, flags); dev_dbg(&port->dev, "digi_write_room: port=%d, room=%u\n", priv->dp_port_num, room); - return room; + return room; } static unsigned int digi_chars_in_buffer(struct tty_struct *tty) @@ -1070,17 +1030,53 @@ static void digi_dtr_rts(struct usb_serial_port *port, int on) digi_set_modem_signals(port, on * (TIOCM_DTR | TIOCM_RTS), 1); } +static int digi_open_oob_port(struct usb_serial *serial) +{ + struct digi_serial *serial_priv = usb_get_serial_data(serial); + struct usb_serial_port *oob_port = serial_priv->ds_oob_port; + int ret = 0; + + mutex_lock(&serial_priv->open_mutex); + + if (serial_priv->open_count++ == 0) { + ret = usb_submit_urb(oob_port->read_urb, GFP_KERNEL); + if (ret) { + dev_err(&serial->interface->dev, "failed to submit OOB read urb: %d\n", + ret); + serial_priv->open_count--; + } + } + + mutex_unlock(&serial_priv->open_mutex); + + return ret; +} + +static void digi_close_oob_port(struct usb_serial *serial) +{ + struct digi_serial *serial_priv = usb_get_serial_data(serial); + struct usb_serial_port *oob_port = serial_priv->ds_oob_port; + + mutex_lock(&serial_priv->open_mutex); + + if (serial_priv->open_count-- == 1) { + usb_kill_urb(oob_port->read_urb); + usb_kill_urb(oob_port->write_urb); + } + + mutex_unlock(&serial_priv->open_mutex); +} + static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) { - int ret; - unsigned char buf[32]; struct digi_port *priv = usb_get_serial_port_data(port); struct ktermios not_termios; - int throttled; + unsigned char buf[32]; + int ret; - /* be sure the device is started up */ - if (digi_startup_device(port->serial) != 0) - return -ENXIO; + ret = digi_open_oob_port(port->serial); + if (ret) + return ret; /* read modem signals automatically whenever they change */ buf[0] = DIGI_CMD_READ_INPUT_SIGNALS; @@ -1106,32 +1102,34 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) } spin_lock_irq(&priv->dp_port_lock); - throttled = priv->dp_throttle_restart; priv->dp_throttled = 0; priv->dp_throttle_restart = 0; spin_unlock_irq(&priv->dp_port_lock); - if (throttled) { - ret = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (ret) { - dev_err(&port->dev, "failed to submit read urb: %d\n", ret); - return ret; - } + ret = usb_submit_urb(port->read_urb, GFP_KERNEL); + if (ret) { + dev_err(&port->dev, "failed to submit read urb: %d\n", ret); + goto err_close_oob; } return 0; -} +err_close_oob: + digi_close_oob_port(port->serial); + + return ret; +} static void digi_close(struct usb_serial_port *port) { + struct digi_port *priv = usb_get_serial_port_data(port); + unsigned char buf[32]; DEFINE_WAIT(wait); int ret; - unsigned char buf[32]; - struct digi_port *priv = usb_get_serial_port_data(port); + + usb_kill_urb(port->read_urb); mutex_lock(&port->serial->disc_mutex); - /* if disconnected, just clear flags */ if (port->serial->disconnected) goto exit; @@ -1177,57 +1175,18 @@ static void digi_close(struct usb_serial_port *port) TASK_INTERRUPTIBLE); schedule_timeout(DIGI_CLOSE_TIMEOUT); finish_wait(&priv->dp_flush_wait, &wait); +exit: + mutex_unlock(&port->serial->disc_mutex); /* shutdown any outstanding bulk writes */ usb_kill_urb(port->write_urb); -exit: - spin_lock_irq(&priv->dp_port_lock); - priv->dp_write_urb_in_use = 0; - wake_up_interruptible(&priv->dp_close_wait); - spin_unlock_irq(&priv->dp_port_lock); - mutex_unlock(&port->serial->disc_mutex); -} - -/* - * Digi Startup Device - * - * Starts reads on all ports. Must be called AFTER startup, with - * urbs initialized. Returns 0 if successful, non-zero error otherwise. - */ - -static int digi_startup_device(struct usb_serial *serial) -{ - int i, ret = 0; - struct digi_serial *serial_priv = usb_get_serial_data(serial); - struct usb_serial_port *port; - - /* be sure this happens exactly once */ - spin_lock(&serial_priv->ds_serial_lock); - if (serial_priv->ds_device_started) { - spin_unlock(&serial_priv->ds_serial_lock); - return 0; - } - serial_priv->ds_device_started = 1; - spin_unlock(&serial_priv->ds_serial_lock); - - /* start reading from each bulk in endpoint for the device */ - /* set USB_DISABLE_SPD flag for write bulk urbs */ - for (i = 0; i < serial->type->num_ports + 1; i++) { - port = serial->port[i]; - ret = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (ret != 0) { - dev_err(&port->dev, - "%s: usb_submit_urb failed, ret=%d, port=%d\n", - __func__, ret, i); - break; - } - } - return ret; + digi_close_oob_port(port->serial); } static int digi_port_init(struct usb_serial_port *port, unsigned port_num) { + struct digi_serial *serial_priv = usb_get_serial_data(port->serial); struct digi_port *priv; priv = kzalloc_obj(*priv); @@ -1235,10 +1194,13 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num) return -ENOMEM; spin_lock_init(&priv->dp_port_lock); + + if (port == serial_priv->ds_oob_port) + lockdep_set_subclass(&priv->dp_port_lock, SINGLE_DEPTH_NESTING); + priv->dp_port_num = port_num; init_waitqueue_head(&priv->dp_transmit_idle_wait); init_waitqueue_head(&priv->dp_flush_wait); - init_waitqueue_head(&priv->dp_close_wait); init_waitqueue_head(&priv->write_wait); priv->dp_port = port; @@ -1275,10 +1237,13 @@ static int digi_startup(struct usb_serial *serial) if (!serial_priv) return -ENOMEM; - spin_lock_init(&serial_priv->ds_serial_lock); + mutex_init(&serial_priv->open_mutex); + serial_priv->ds_oob_port_num = oob_port_num; serial_priv->ds_oob_port = serial->port[oob_port_num]; + usb_set_serial_data(serial, serial_priv); + ret = digi_port_init(serial_priv->ds_oob_port, serial_priv->ds_oob_port_num); if (ret) { @@ -1286,24 +1251,9 @@ static int digi_startup(struct usb_serial *serial) return ret; } - usb_set_serial_data(serial, serial_priv); - return 0; } - -static void digi_disconnect(struct usb_serial *serial) -{ - int i; - - /* stop reads and writes on all ports */ - for (i = 0; i < serial->type->num_ports + 1; i++) { - usb_kill_urb(serial->port[i]->read_urb); - usb_kill_urb(serial->port[i]->write_urb); - } -} - - static void digi_release(struct usb_serial *serial) { struct digi_serial *serial_priv; @@ -1333,30 +1283,25 @@ static void digi_port_remove(struct usb_serial_port *port) static void digi_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct digi_port *priv; - struct digi_serial *serial_priv; - int ret; + struct usb_serial *serial = port->serial; + struct digi_serial *serial_priv = usb_get_serial_data(serial); + struct digi_port *priv = usb_get_serial_port_data(port); int status = urb->status; - - /* port sanity check, do not resubmit if port is not valid */ - if (port == NULL) - return; - priv = usb_get_serial_port_data(port); - if (priv == NULL) { - dev_err(&port->dev, "%s: port->private is NULL, status=%d\n", - __func__, status); - return; - } - if (port->serial == NULL || - (serial_priv = usb_get_serial_data(port->serial)) == NULL) { - dev_err(&port->dev, "%s: serial is bad or serial->private " - "is NULL, status=%d\n", __func__, status); - return; - } + int ret; /* do not resubmit urb if it has any status error */ - if (status) { - dev_err(&port->dev, + switch (status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dev_err(&serial->interface->dev, + "%s: nonzero read bulk status: status=%d, port=%d\n", + __func__, status, priv->dp_port_num); + return; + default: + dev_err(&serial->interface->dev, "%s: nonzero read bulk status: status=%d, port=%d\n", __func__, status, priv->dp_port_num); return; @@ -1374,11 +1319,10 @@ static void digi_read_bulk_callback(struct urb *urb) /* continue read */ ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret != 0 && ret != -EPERM) { - dev_err(&port->dev, + dev_err(&serial->interface->dev, "%s: failed resubmitting urb, ret=%d, port=%d\n", __func__, ret, priv->dp_port_num); } - } /* @@ -1390,7 +1334,6 @@ static void digi_read_bulk_callback(struct urb *urb) * It returns 0 if successful, 1 if successful but the port is * throttled, and -1 if the sanity checks failed. */ - static int digi_read_inb_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; @@ -1468,10 +1411,8 @@ static int digi_read_inb_callback(struct urb *urb) dev_dbg(&port->dev, "%s: unknown opcode: %d\n", __func__, opcode); return throttled ? 1 : 0; - } - /* * Digi Read OOB Callback * @@ -1480,10 +1421,8 @@ static int digi_read_inb_callback(struct urb *urb) * the port->serial is valid. It returns 0 if successful, and * -1 if the sanity checks failed. */ - static int digi_read_oob_callback(struct urb *urb) { - struct usb_serial_port *port = urb->context; struct usb_serial *serial = port->serial; struct tty_struct *tty; @@ -1491,8 +1430,8 @@ static int digi_read_oob_callback(struct urb *urb) unsigned char *buf = urb->transfer_buffer; int opcode, line, status, val; unsigned long flags; - int i; unsigned int rts; + int i; if (urb->actual_length < 4) return -1; @@ -1504,7 +1443,8 @@ static int digi_read_oob_callback(struct urb *urb) status = buf[i + 2]; val = buf[i + 3]; - dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n", + dev_dbg(&serial->interface->dev, + "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n", opcode, line, status, val); if (status != 0 || line >= serial->type->num_ports) @@ -1562,8 +1502,8 @@ static int digi_read_oob_callback(struct urb *urb) } tty_kref_put(tty); } - return 0; + return 0; } module_usb_serial_driver(serial_drivers, id_table_combined); diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -31,7 +31,6 @@ struct keyspan_pda_private { int tx_room; struct work_struct unthrottle_work; - struct usb_serial *serial; struct usb_serial_port *port; bool throttled; bool throttle_req; diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -36,6 +36,7 @@ struct metrousb_private { spinlock_t lock; int throttled; + int throttle_req; unsigned long control_state; }; @@ -108,7 +109,7 @@ static void metrousb_read_int_callback(struct urb *urb) unsigned char *data = urb->transfer_buffer; unsigned long flags; int throttled = 0; - int result = 0; + int result; dev_dbg(&port->dev, "%s\n", __func__); @@ -143,7 +144,10 @@ static void metrousb_read_int_callback(struct urb *urb) /* Set any port variables. */ spin_lock_irqsave(&metro_priv->lock, flags); - throttled = metro_priv->throttled; + if (metro_priv->throttle_req) { + metro_priv->throttled = 1; + throttled = 1; + } spin_unlock_irqrestore(&metro_priv->lock, flags); if (throttled) @@ -169,12 +173,13 @@ static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port) struct usb_serial *serial = port->serial; struct metrousb_private *metro_priv = usb_get_serial_port_data(port); unsigned long flags; - int result = 0; + int result; /* Set the private data information for the port. */ spin_lock_irqsave(&metro_priv->lock, flags); metro_priv->control_state = 0; metro_priv->throttled = 0; + metro_priv->throttle_req = 0; spin_unlock_irqrestore(&metro_priv->lock, flags); /* Clear the urb pipe. */ @@ -269,7 +274,7 @@ static void metrousb_throttle(struct tty_struct *tty) /* Set the private information for the port to stop reading data. */ spin_lock_irqsave(&metro_priv->lock, flags); - metro_priv->throttled = 1; + metro_priv->throttle_req = 1; spin_unlock_irqrestore(&metro_priv->lock, flags); } @@ -321,19 +326,23 @@ static void metrousb_unthrottle(struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; struct metrousb_private *metro_priv = usb_get_serial_port_data(port); unsigned long flags; - int result = 0; + int throttled; + int result; /* Set the private information for the port to resume reading data. */ spin_lock_irqsave(&metro_priv->lock, flags); + throttled = metro_priv->throttled; metro_priv->throttled = 0; + metro_priv->throttle_req = 0; spin_unlock_irqrestore(&metro_priv->lock, flags); - /* Submit the urb to read from the port. */ - result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, - "failed submitting interrupt in urb error code=%d\n", - result); + if (throttled) { + result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); + if (result) { + dev_err(&port->dev, "failed to submit interrupt in urb: %d\n", + result); + } + } } static struct usb_serial_driver metrousb_device = { diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -51,6 +51,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GL) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GE) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GS) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL256X_PRODUCT_ID_4P) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID), @@ -141,10 +142,15 @@ MODULE_DEVICE_TABLE(usb, id_table); #define VENDOR_WRITE_REQUEST_TYPE 0x40 #define VENDOR_WRITE_REQUEST 0x01 #define VENDOR_WRITE_NREQUEST 0x80 +#define VENDOR_WRITE_MPREQUEST 0x80 #define VENDOR_READ_REQUEST_TYPE 0xc0 #define VENDOR_READ_REQUEST 0x01 #define VENDOR_READ_NREQUEST 0x81 +#define VENDOR_READ_MPREQUEST 0x80 + +#define PL256X_RESET_REQUEST_TYPE 0x40 +#define PL256X_RESET_REQUEST 0x96 #define UART_STATE_INDEX 8 #define UART_STATE_MSR_MASK 0x8b @@ -172,6 +178,16 @@ MODULE_DEVICE_TABLE(usb, id_table); #define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18 #define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c +#define PL256X_PORT_A_FLOWCTRL_REG 0xc005 +#define PL256X_PORT_B_FLOWCTRL_REG 0xd005 +#define PL256X_PORT_C_FLOWCTRL_REG 0xe005 +#define PL256X_PORT_D_FLOWCTRL_REG 0xf005 + +#define PL256X_FLOWCTRL_MASK 0x43 +#define PL256X_FLOWCTRL_XON_XOFF 0x40 +#define PL256X_FLOWCTRL_RTS_CTS 0x03 +#define PL256X_FLOWCTRL_NONE 0x00 + static int pl2303_set_break(struct usb_serial_port *port, bool enable); enum pl2303_type { @@ -181,6 +197,7 @@ enum pl2303_type { TYPE_TB, TYPE_HXD, TYPE_HXN, + TYPE_MP, TYPE_COUNT }; @@ -196,6 +213,8 @@ struct pl2303_type_data { struct pl2303_serial_private { const struct pl2303_type_data *type; unsigned long quirks; + u16 interface_num; + u16 flowctrl_reg; }; struct pl2303_private { @@ -236,8 +255,30 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = { .max_baud_rate = 12000000, .no_divisors = true, }, + [TYPE_MP] = { + .name = "MP", + .max_baud_rate = 24000000, + .no_divisors = true, + }, }; +static int pl256x_uart_reset(struct usb_serial *serial) +{ + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); + struct device *dev = &serial->interface->dev; + int res; + + res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + PL256X_RESET_REQUEST, PL256X_RESET_REQUEST_TYPE, + 0, spriv->interface_num, NULL, 0, 100); + if (res) { + dev_err(dev, "failed to reset device: %d\n", res); + return res; + } + + return 0; +} + static int pl2303_vendor_read(struct usb_serial *serial, u16 value, unsigned char buf[1]) { @@ -248,6 +289,8 @@ static int pl2303_vendor_read(struct usb_serial *serial, u16 value, if (spriv->type == &pl2303_type_data[TYPE_HXN]) request = VENDOR_READ_NREQUEST; + else if (spriv->type == &pl2303_type_data[TYPE_MP]) + request = VENDOR_READ_MPREQUEST; else request = VENDOR_READ_REQUEST; @@ -279,6 +322,8 @@ static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index) if (spriv->type == &pl2303_type_data[TYPE_HXN]) request = VENDOR_WRITE_NREQUEST; + else if (spriv->type == &pl2303_type_data[TYPE_MP]) + request = VENDOR_WRITE_MPREQUEST; else request = VENDOR_WRITE_REQUEST; @@ -304,10 +349,12 @@ static int pl2303_update_reg(struct usb_serial *serial, u8 reg, u8 mask, u8 val) if (!buf) return -ENOMEM; - if (spriv->type == &pl2303_type_data[TYPE_HXN]) + if (spriv->type == &pl2303_type_data[TYPE_HXN] || + spriv->type == &pl2303_type_data[TYPE_MP]) { ret = pl2303_vendor_read(serial, reg, buf); - else + } else { ret = pl2303_vendor_read(serial, reg | 0x80, buf); + } if (ret) goto out_free; @@ -458,6 +505,14 @@ static int pl2303_detect_type(struct usb_serial *serial) case 0x905: /* GT-2AB */ case 0x1005: /* GC-Q20 */ return TYPE_HXN; + case 0x3302: /* PL2533 VC 2 Port */ + case 0x3304: /* PL2533 VC 4 Port */ + case 0x4302: /* PL2543 VC 2 Port */ + case 0x4304: /* PL2543 VC 4 Port */ + case 0x6502: /* PL2565 VC 2 Port */ + case 0x6504: /* PL2565 VC 4 Port */ + case 0x6506: /* PL2565 VC 4 Port QFN64 package */ + return TYPE_MP; } break; } @@ -491,6 +546,7 @@ static int pl2303_startup(struct usb_serial *serial) struct pl2303_serial_private *spriv; enum pl2303_type type; unsigned char *buf; + unsigned int ifnum; int ret; ret = pl2303_detect_type(serial); @@ -500,20 +556,43 @@ static int pl2303_startup(struct usb_serial *serial) type = ret; dev_dbg(&serial->interface->dev, "device type: %s\n", pl2303_type_data[type].name); + ifnum = serial->interface->altsetting->desc.bInterfaceNumber; + spriv = kzalloc_obj(*spriv); if (!spriv) return -ENOMEM; + if (type == TYPE_MP) { + switch (ifnum) { + case 0: + spriv->flowctrl_reg = PL256X_PORT_A_FLOWCTRL_REG; + break; + case 1: + spriv->flowctrl_reg = PL256X_PORT_B_FLOWCTRL_REG; + break; + case 2: + spriv->flowctrl_reg = PL256X_PORT_C_FLOWCTRL_REG; + break; + case 3: + spriv->flowctrl_reg = PL256X_PORT_D_FLOWCTRL_REG; + break; + default: + kfree(spriv); + return -ENODEV; + } + } + spriv->type = &pl2303_type_data[type]; spriv->quirks = (unsigned long)usb_get_serial_data(serial); spriv->quirks |= spriv->type->quirks; + spriv->interface_num = ifnum; if (type == TYPE_HXD && pl2303_is_hxd_clone(serial)) spriv->quirks |= PL2303_QUIRK_NO_BREAK_GETLINE; usb_set_serial_data(serial, spriv); - if (type != TYPE_HXN) { + if (type != TYPE_HXN && type != TYPE_MP) { buf = kmalloc(1, GFP_KERNEL); if (!buf) { kfree(spriv); @@ -575,13 +654,15 @@ static void pl2303_port_remove(struct usb_serial_port *port) static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value) { struct usb_device *dev = port->serial->dev; + struct usb_serial *serial = port->serial; + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); int retval; dev_dbg(&port->dev, "%s - %02x\n", __func__, value); retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE, - value, 0, NULL, 0, 100); + value, spriv->interface_num, NULL, 0, 100); if (retval) dev_err(&port->dev, "%s - failed: %d\n", __func__, retval); @@ -761,7 +842,7 @@ static int pl2303_get_line_request(struct usb_serial_port *port, ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, - 0, 0, buf, 7, 100); + 0, spriv->interface_num, buf, 7, 100); if (ret != 7) { dev_err(&port->dev, "%s - failed: %d\n", __func__, ret); @@ -780,11 +861,13 @@ static int pl2303_set_line_request(struct usb_serial_port *port, unsigned char buf[7]) { struct usb_device *udev = port->serial->dev; + struct usb_serial *serial = port->serial; + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); int ret; ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, - 0, 0, buf, 7, 100); + 0, spriv->interface_num, buf, 7, 100); if (ret < 0) { dev_err(&port->dev, "%s - failed: %d\n", __func__, ret); return ret; @@ -939,6 +1022,9 @@ static void pl2303_set_termios(struct tty_struct *tty, pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG, PL2303_HXN_FLOWCTRL_MASK, PL2303_HXN_FLOWCTRL_RTS_CTS); + } else if (spriv->type == &pl2303_type_data[TYPE_MP]) { + pl2303_vendor_write(serial, spriv->flowctrl_reg, + PL256X_FLOWCTRL_RTS_CTS); } else { pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x60); } @@ -947,6 +1033,9 @@ static void pl2303_set_termios(struct tty_struct *tty, pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG, PL2303_HXN_FLOWCTRL_MASK, PL2303_HXN_FLOWCTRL_XON_XOFF); + } else if (spriv->type == &pl2303_type_data[TYPE_MP]) { + pl2303_vendor_write(serial, spriv->flowctrl_reg, + PL256X_FLOWCTRL_XON_XOFF); } else { pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0xc0); } @@ -955,6 +1044,9 @@ static void pl2303_set_termios(struct tty_struct *tty, pl2303_update_reg(serial, PL2303_HXN_FLOWCTRL_REG, PL2303_HXN_FLOWCTRL_MASK, PL2303_HXN_FLOWCTRL_NONE); + } else if (spriv->type == &pl2303_type_data[TYPE_MP]) { + pl2303_vendor_write(serial, spriv->flowctrl_reg, + PL256X_FLOWCTRL_NONE); } else { pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0); } @@ -1002,6 +1094,8 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port) pl2303_vendor_write(serial, PL2303_HXN_RESET_REG, PL2303_HXN_RESET_UPSTREAM_PIPE | PL2303_HXN_RESET_DOWNSTREAM_PIPE); + } else if (spriv->type == &pl2303_type_data[TYPE_MP]) { + pl256x_uart_reset(serial); } else { pl2303_vendor_write(serial, 8, 0); pl2303_vendor_write(serial, 9, 0); @@ -1112,7 +1206,7 @@ static int pl2303_set_break(struct usb_serial_port *port, bool enable) result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), BREAK_REQUEST, BREAK_REQUEST_TYPE, state, - 0, NULL, 0, 100); + spriv->interface_num, NULL, 0, 100); if (result) { dev_err(&port->dev, "error sending break = %d\n", result); return result; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -26,7 +26,7 @@ #define PL2303_PRODUCT_ID_HCR331 0x331a #define PL2303_PRODUCT_ID_MOTOROLA 0x0307 #define PL2303_PRODUCT_ID_ZTEK 0xe1f1 - +#define PL256X_PRODUCT_ID_4P 0x2533 #define ATEN_VENDOR_ID 0x0557 #define ATEN_VENDOR_ID2 0x0547 diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig --- a/drivers/usb/typec/altmodes/Kconfig +++ b/drivers/usb/typec/altmodes/Kconfig @@ -5,6 +5,7 @@ menu "USB Type-C Alternate Mode drivers" config TYPEC_DP_ALTMODE tristate "DisplayPort Alternate Mode driver" depends on DRM + select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF help DisplayPort USB Type-C Alternate Mode allows DisplayPort displays and adapters to be attached to the USB Type-C diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -764,6 +764,7 @@ int dp_altmode_probe(struct typec_altmode *alt) struct typec_altmode *plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); struct fwnode_handle *fwnode; struct dp_altmode *dp; + u32 cap = DP_CAP_CAPABILITY(alt->vdo); /* Port can only be DFP_U. */ if (typec_altmode_get_data_role(alt) != TYPEC_HOST) @@ -778,6 +779,18 @@ int dp_altmode_probe(struct typec_altmode *alt) return -ENODEV; } + /* + * Make sure the DisplayPort VDO is valid (VESA DPAM v2.1a, Section + * 5.4.1, Table 5-6, DP Capabilities VDO). A device exposing DP on a + * USB-C receptacle must advertise at least one pin assignment for the + * capability it claims, otherwise Alt Mode can never be configured. + */ + if ((cap == DP_CAP_DFP_D && !DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)) || + (cap == DP_CAP_UFP_D && !DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo))) { + typec_altmode_put_plug(plug); + return -ENODEV; + } + dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); if (!dp) { typec_altmode_put_plug(plug); @@ -790,7 +803,6 @@ int dp_altmode_probe(struct typec_altmode *alt) dp->alt = alt; alt->desc = "DisplayPort"; - typec_altmode_set_ops(alt, &dp_altmode_ops); if (plug) { plug->desc = "Displayport"; @@ -811,6 +823,10 @@ int dp_altmode_probe(struct typec_altmode *alt) if (plug) typec_altmode_set_drvdata(plug, dp); + if ((alt->vdo & DP_CAP_RECEPTACLE) && typec_cable_altmode_unsupported(alt)) + return 0; + + typec_altmode_set_ops(alt, &dp_altmode_ops); if (!alt->mode_selection) { dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; schedule_work(&dp->work); diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c --- a/drivers/usb/typec/altmodes/thunderbolt.c +++ b/drivers/usb/typec/altmodes/thunderbolt.c @@ -284,6 +284,10 @@ static int tbt_altmode_probe(struct typec_altmode *alt) alt->desc = "Thunderbolt3"; typec_altmode_set_drvdata(alt, tbt); + + if (typec_cable_altmode_unsupported(alt)) + return 0; + typec_altmode_set_ops(alt, &tbt_altmode_ops); if (!alt->mode_selection && tbt_ready(alt)) { diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1429,6 +1429,77 @@ int typec_cable_is_active(struct typec_cable *cable) } EXPORT_SYMBOL_GPL(typec_cable_is_active); +enum typec_cable_altmode_support { + CABLE_SUPPORT_UNKNOWN, + CABLE_SUPPORTED, + CABLE_NOT_SUPPORTED, +}; + +static enum typec_cable_altmode_support +typec_cable_check_altmode_support(struct typec_cable *cable, + struct typec_altmode *alt) +{ + struct typec_altmode *plug; + u32 speed; + + /* + * Check if the cable has an e-marker, supports modal operation, and the + * SOP' altmode nodes are created. + */ + plug = typec_altmode_get_plug(alt, TYPEC_PLUG_SOP_P); + if (plug) { + typec_altmode_put_plug(plug); + return CABLE_SUPPORTED; + } + + /* The identity is not specified */ + if (!cable->identity) + return CABLE_SUPPORT_UNKNOWN; + + /* Non-e-marked cable */ + if (!cable->identity->id_header) + return CABLE_NOT_SUPPORTED; + + switch (PD_IDH_PTYPE(cable->identity->id_header)) { + case IDH_PTYPE_PCABLE: + speed = VDO_TYPEC_CABLE_SPEED(cable->identity->vdo[0]); + if (speed == CABLE_USB2_ONLY) + return CABLE_NOT_SUPPORTED; + return CABLE_SUPPORTED; + case IDH_PTYPE_ACABLE: + /* + * Active cables must establish an SOP' communication + * node. Since that check failed at the beginning of + * this function, this active cable does not support + * this specific altmode. + */ + return CABLE_NOT_SUPPORTED; + } + + return CABLE_SUPPORT_UNKNOWN; +} + +/** + * typec_cable_altmode_unsupported - Check if a cable restricts altmode + * @alt: The Alternate Mode to evaluate + * + * Returns true if the connected cable is incapable of handling the altmode. + */ +bool typec_cable_altmode_unsupported(struct typec_altmode *alt) +{ + enum typec_cable_altmode_support support = CABLE_SUPPORT_UNKNOWN; + struct typec_cable *cable; + + cable = typec_cable_get(typec_altmode2port(alt)); + if (cable) { + support = typec_cable_check_altmode_support(cable, alt); + typec_cable_put(cable); + } + + return support == CABLE_NOT_SUPPORTED; +} +EXPORT_SYMBOL_GPL(typec_cable_altmode_unsupported); + /** * typec_cable_set_identity - Report result from Discover Identity command * @cable: The cable updated identity values diff --git a/drivers/usb/typec/mux/tusb1046.c b/drivers/usb/typec/mux/tusb1046.c --- a/drivers/usb/typec/mux/tusb1046.c +++ b/drivers/usb/typec/mux/tusb1046.c @@ -178,6 +178,7 @@ static const struct of_device_id tusb1046_match_table[] = { {.compatible = "ti,tusb1046"}, {}, }; +MODULE_DEVICE_TABLE(of, tusb1046_match_table); static struct i2c_driver tusb1046_driver = { .driver = { diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -212,6 +213,24 @@ static const char * const tcpm_ams_str[] = { FOREACH_AMS(GENERATE_STRING) }; +#define FOREACH_VDM_DISCOVERY(S) \ + S(VDM_DISCOVERY_UNKNOWN), \ + S(VDM_DISCOVERY_PARTNER_IDENT), \ + S(VDM_DISCOVERY_CABLE_IDENT), \ + S(VDM_DISCOVERY_PARTNER_SVIDS), \ + S(VDM_DISCOVERY_PARTNER_MODES), \ + S(VDM_DISCOVERY_CABLE_SVIDS), \ + S(VDM_DISCOVERY_CABLE_MODES), \ + S(VDM_DISCOVERY_COMPLETE) + +enum vdm_discovery_states { + FOREACH_VDM_DISCOVERY(GENERATE_ENUM) +}; + +static const char * const vdm_discovery_state_strings[] = { + FOREACH_VDM_DISCOVERY(GENERATE_STRING) +}; + enum vdm_states { VDM_STATE_ERR_BUSY = -3, VDM_STATE_ERR_SEND = -2, @@ -232,7 +251,9 @@ enum pd_msg_request { PD_MSG_DATA_SINK_CAP, PD_MSG_DATA_SOURCE_CAP, PD_MSG_DATA_REV, - PD_MSG_EXT_SINK_CAP_EXT + PD_MSG_EXT_SINK_CAP_EXT, + PD_MSG_DATA_BATT_STATUS, + PD_MSG_EXT_BATT_CAP, }; enum adev_actions { @@ -274,7 +295,7 @@ enum frs_typec_current { #define ALTMODE_DISCOVERY_MAX (SVID_DISCOVERY_MAX * MODE_DISCOVERY_MAX) #define GET_SINK_CAP_RETRY_MS 100 -#define SEND_DISCOVER_RETRY_MS 100 +#define SEND_DISCOVERY_VDM_RETRY_MS 100 struct pd_mode_data { int svid_index; /* current SVID index */ @@ -387,7 +408,15 @@ struct pd_timings { }; /* Convert microwatt to watt */ -#define UW_TO_W(pow) ((pow) / 1000000) +#define UW_TO_W(pow) (div_u64((pow), 1000000)) + +/* + * As per USB PD Spec Rev 3.18 (Sec. 6.5.13.11), the number of fixed batteries + * that a port can be queried is restricted to 4. + */ +#define MAX_NUM_FIXED_BATT 4 + +#define BATTERY_PROPERTY_UNKNOWN 0xffff /* * struct pd_identifier - Contains info about PD identifiers @@ -483,8 +512,6 @@ struct tcpm_port { bool vbus_source; bool vbus_charge; - /* Set to true when Discover_Identity Command is expected to be sent in Ready states. */ - bool send_discover; bool op_vsafe5v; int try_role; @@ -510,8 +537,8 @@ struct tcpm_port { struct kthread_work vdm_state_machine; struct hrtimer enable_frs_timer; struct kthread_work enable_frs; - struct hrtimer send_discover_timer; - struct kthread_work send_discover_work; + struct hrtimer vdm_discovery_timer; + struct kthread_work vdm_discovery_work; bool state_machine_running; /* Set to true when VDM State Machine has following actions. */ bool vdm_sm_running; @@ -578,6 +605,9 @@ struct tcpm_port { u32 bist_request; + /* VDM Discovery State to determine message sent */ + enum vdm_discovery_states vdm_discovery_state; + /* PD state for Vendor Defined Messages */ enum vdm_states vdm_state; u32 vdm_retries; @@ -641,12 +671,6 @@ struct tcpm_port { bool potential_contaminant; /* SOP* Related Fields */ - /* - * Flag to determine if SOP' Discover Identity is available. The flag - * is set if Discover Identity on SOP' does not immediately follow - * Discover Identity on SOP. - */ - bool send_discover_prime; /* * tx_sop_type determines which SOP* a message is being sent on. * For messages that are queued and not sent immediately such as in @@ -683,6 +707,9 @@ struct tcpm_port { struct pd_identifier pd_ident; struct sink_caps_ext_data sink_caps_ext; + struct power_supply **fixed_batt; + u32 fixed_batt_cnt; + u32 batt_request_id; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -771,6 +798,9 @@ static const char * const pd_rev[] = { #define tcpm_wait_for_discharge(port) \ (((port)->auto_vbus_discharge_enabled && !(port)->vbus_vsafe0v) ? PD_T_SAFE_0V : 0) +#define tcpm_can_send_vdm(state) \ + ((state == SRC_READY || state == SNK_READY || state == SRC_VDM_IDENTITY_REQUEST)) + static enum tcpm_state tcpm_default_state(struct tcpm_port *port) { if (port->port_type == TYPEC_PORT_DRP) { @@ -1470,6 +1500,20 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port) return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); } +static void tcpm_get_fixed_batt(struct tcpm_port *port) +{ + int ret; + + if (!port->self_powered || port->fixed_batt_cnt > 0) + return; + + ret = power_supply_get_system_batteries(port->dev, &port->fixed_batt); + if (ret < 0) + tcpm_log(port, "Failed to get battery array, ret=%d", ret); + else + port->fixed_batt_cnt = ret; +} + static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) { u16 operating_snk_watt = port->operating_snk_mw / 1000; @@ -1482,6 +1526,8 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) if (!port->self_powered) data->spr_op_pdp = operating_snk_watt; + tcpm_get_fixed_batt(port); + /* * SPR Sink Minimum PDP indicates the minimum power required to operate * a sink device in its lowest level of functionality without requiring @@ -1507,6 +1553,7 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) skedb.load_step = data->load_step; skedb.load_char = cpu_to_le16(data->load_char); skedb.compliance = data->compliance; + skedb.batt_info = min(port->fixed_batt_cnt, MAX_NUM_FIXED_BATT); skedb.modes = data->modes; skedb.spr_min_pdp = data->spr_min_pdp; skedb.spr_op_pdp = data->spr_op_pdp; @@ -1525,6 +1572,153 @@ static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) port->message_id, data_obj_cnt, 1 /* Denotes if ext header */)); + + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + +static u16 tcpm_charge_to_energy(int charge, int voltage) +{ + u64 energy = div_u64((u64)charge * voltage, 1000000); + + /* Battery telemetry is reported in increments of 0.1Wh */ + return (u16)UW_TO_W(energy * 10); +} + +static int tcpm_pd_send_batt_status(struct tcpm_port *port) +{ + u16 present_charge = BATTERY_PROPERTY_UNKNOWN; + bool batt_present = false, invalid_ref = true; + u32 batt_id = port->batt_request_id; + union power_supply_propval val; + struct power_supply *batt; + u8 charging_status = 0; + struct pd_message msg; + int ret, charge_now; + u32 bsdo; + + tcpm_get_fixed_batt(port); + memset(&msg, 0, sizeof(msg)); + + if (batt_id >= port->fixed_batt_cnt || batt_id >= MAX_NUM_FIXED_BATT) + goto send_status; + + invalid_ref = false; + batt = port->fixed_batt[batt_id]; + ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) + tcpm_log(port, + "Failed to fetch power_supply_prop_present ret %d", + ret); + else + batt_present = val.intval > 0; + + ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_CHARGE_NOW, + &val); + if (!ret) { + charge_now = val.intval; + ret = power_supply_get_property(batt, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + &val); + if (!ret) + present_charge = tcpm_charge_to_energy(charge_now, + val.intval); + } + + ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_STATUS, &val); + if (!ret) { + switch (val.intval) { + case POWER_SUPPLY_STATUS_CHARGING: + charging_status = BSDO_BATTERY_INFO_CHARGING; + break; + case POWER_SUPPLY_STATUS_DISCHARGING: + charging_status = BSDO_BATTERY_INFO_DISCHARGING; + break; + case POWER_SUPPLY_STATUS_NOT_CHARGING: + case POWER_SUPPLY_STATUS_FULL: + charging_status = BSDO_BATTERY_INFO_IDLE; + break; + default: + charging_status = BSDO_BATTERY_INFO_RSVD; + break; + } + } + +send_status: + + bsdo = BSDO(present_charge, charging_status, batt_present, invalid_ref); + msg.payload[0] = cpu_to_le32(bsdo); + msg.header = PD_HEADER_LE(PD_DATA_BATT_STATUS, + port->pwr_role, + port->data_role, + port->negotiated_rev, + port->message_id, + 1); + + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + +static int tcpm_pd_send_batt_cap(struct tcpm_port *port) +{ + u16 design_cap = BATTERY_PROPERTY_UNKNOWN; + u16 charge_cap = BATTERY_PROPERTY_UNKNOWN; + u32 batt_id = port->batt_request_id; + union power_supply_propval val; + struct batt_cap_ext_msg bcdb; + struct power_supply *batt; + bool invalid_ref = true; + struct pd_message msg; + u8 data_obj_cnt; + int ret, vol; + + tcpm_get_fixed_batt(port); + memset(&msg, 0, sizeof(msg)); + + if (batt_id >= port->fixed_batt_cnt || batt_id >= MAX_NUM_FIXED_BATT) + goto send_cap; + + invalid_ref = false; + batt = port->fixed_batt[batt_id]; + ret = power_supply_get_property(batt, POWER_SUPPLY_PROP_VOLTAGE_AVG, + &val); + if (!ret) { + vol = val.intval; + ret = power_supply_get_property(batt, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + &val); + if (!ret) + design_cap = tcpm_charge_to_energy(val.intval, vol); + + ret = power_supply_get_property(batt, + POWER_SUPPLY_PROP_CHARGE_FULL, + &val); + if (!ret) + charge_cap = tcpm_charge_to_energy(val.intval, vol); + } + +send_cap: + + /* + * As per the USB PD Rev3.1 v1.8 spec, if a battery VID (assigned by the + * USB-IF) does not exist or an invalid battery reference is made by the + * requestor, then set the VID field to 0xffff. If the VID field is + * 0xffff, set the PID field to 0. + */ + bcdb.vid = BATTERY_PROPERTY_UNKNOWN; + bcdb.pid = 0; + bcdb.batt_design_cap = cpu_to_le16(design_cap); + bcdb.batt_last_chg_cap = cpu_to_le16(charge_cap); + bcdb.batt_type = invalid_ref ? BATT_CAP_BATT_TYPE_INVALID_REF : 0; + memcpy(msg.ext_msg.data, &bcdb, sizeof(bcdb)); + msg.ext_msg.header = PD_EXT_HDR_LE(sizeof(bcdb), + 0, /* Denotes if request chunk */ + 0, /* Chunk number */ + 1 /* Chunked */); + + data_obj_cnt = count_chunked_data_objs(sizeof(bcdb)); + msg.header = PD_HEADER_EXT_LE(PD_EXT_BATT_CAP, port->pwr_role, + port->data_role, port->negotiated_rev, + port->message_id, data_obj_cnt); + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); } @@ -1559,13 +1753,19 @@ static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int del } } -static void mod_send_discover_delayed_work(struct tcpm_port *port, unsigned int delay_ms) +static void mod_vdm_discovery_cancel_delayed_work(struct tcpm_port *port) +{ + hrtimer_cancel(&port->vdm_discovery_timer); + kthread_cancel_work_sync(&port->vdm_discovery_work); +} + +static void mod_vdm_discovery_delayed_work(struct tcpm_port *port, unsigned int delay_ms) { if (delay_ms) { - hrtimer_start(&port->send_discover_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL); + hrtimer_start(&port->vdm_discovery_timer, ms_to_ktime(delay_ms), HRTIMER_MODE_REL); } else { - hrtimer_cancel(&port->send_discover_timer); - kthread_queue_work(port->wq, &port->send_discover_work); + hrtimer_cancel(&port->vdm_discovery_timer); + kthread_queue_work(port->wq, &port->vdm_discovery_work); } } @@ -1773,16 +1973,11 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header, WARN_ON(!mutex_is_locked(&port->lock)); /* If is sending discover_identity, handle received message first */ - if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMD(vdo_hdr) == CMD_DISCOVER_IDENT) { - if (tx_sop_type == TCPC_TX_SOP_PRIME) - port->send_discover_prime = true; - else - port->send_discover = true; - mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); - } else { + if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMD(vdo_hdr) == CMD_DISCOVER_IDENT) + mod_vdm_discovery_delayed_work(port, SEND_DISCOVERY_VDM_RETRY_MS); + else /* Make sure we are not still processing a previous VDM packet */ WARN_ON(port->vdm_state > VDM_STATE_DONE); - } port->vdo_count = cnt + 1; port->vdo_data[0] = header; @@ -1805,8 +2000,7 @@ static void tcpm_queue_vdm_work(struct kthread_work *work) struct tcpm_port *port = event->port; mutex_lock(&port->lock); - if (port->state != SRC_READY && port->state != SNK_READY && - port->state != SRC_VDM_IDENTITY_REQUEST) { + if (!tcpm_can_send_vdm(port->state)) { tcpm_log_force(port, "dropping altmode_vdm_event"); goto port_unlock; } @@ -2034,7 +2228,7 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port) if (!port->partner) return; - for (i = 0; i < modep->altmodes; i++) { + for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { altmode = typec_partner_register_altmode(port->partner, &modep->altmode_desc[i]); if (IS_ERR(altmode)) { @@ -2052,9 +2246,10 @@ static void tcpm_register_plug_altmodes(struct tcpm_port *port) struct typec_altmode *altmode; int i; - typec_plug_set_num_altmodes(port->plug_prime, modep->altmodes); + typec_plug_set_num_altmodes(port->plug_prime, + min(modep->altmodes, ALTMODE_DISCOVERY_MAX)); - for (i = 0; i < modep->altmodes; i++) { + for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { altmode = typec_plug_register_altmode(port->plug_prime, &modep->altmode_desc[i]); if (IS_ERR(altmode)) { @@ -2154,6 +2349,19 @@ static bool tcpm_cable_vdm_supported(struct tcpm_port *port) tcpm_can_communicate_sop_prime(port); } +static void tcpm_update_vdm_discovery_state(struct tcpm_port *port, + enum vdm_discovery_states new_state) +{ + enum vdm_discovery_states old_state = port->vdm_discovery_state; + + if (old_state != new_state) + tcpm_log_force(port, "vdm discovery state changed: %s -> %s", + vdm_discovery_state_strings[old_state], + vdm_discovery_state_strings[new_state]); + + port->vdm_discovery_state = new_state; +} + static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, enum tcpm_transmit_type rx_sop_type, enum tcpm_transmit_type *response_tx_sop_type) @@ -2171,6 +2379,7 @@ static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, response[0] = VDO(svid, 1, typec_get_negotiated_svdm_version(typec), CMD_DISCOVER_MODES); + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_PARTNER_MODES); return 1; } @@ -2179,10 +2388,12 @@ static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, response[0] = VDO(USB_SID_PD, 1, typec_get_cable_svdm_version(typec), CMD_DISCOVER_SVID); + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_PARTNER_MODES); return 1; } tcpm_register_partner_altmodes(port); + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { modep = &port->mode_data_prime; modep->svid_index++; @@ -2193,11 +2404,13 @@ static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, response[0] = VDO(svid, 1, typec_get_cable_svdm_version(typec), CMD_DISCOVER_MODES); + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_MODES); return 1; } tcpm_register_plug_altmodes(port); tcpm_register_partner_altmodes(port); + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); } return 0; @@ -2376,18 +2589,18 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, typec_cable_set_svdm_version(port->cable, svdm_version); } + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_PARTNER_IDENT); + /* 6.4.4.3.1 */ svdm_consume_identity(port, p, cnt); /* Attempt Vconn swap, delay SOP' discovery if necessary */ if (tcpm_attempt_vconn_swap_discovery(port)) { - port->send_discover_prime = true; port->upcoming_state = VCONN_SWAP_SEND; ret = tcpm_ams_start(port, VCONN_SWAP); if (!ret) return 0; /* Cannot perform Vconn swap */ port->upcoming_state = INVALID_STATE; - port->send_discover_prime = false; } /* @@ -2398,7 +2611,6 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, if (IS_ERR_OR_NULL(port->cable) && tcpm_can_communicate_sop_prime(port)) { *response_tx_sop_type = TCPC_TX_SOP_PRIME; - port->send_discover_prime = true; response[0] = VDO(USB_SID_PD, 1, typec_get_negotiated_svdm_version(typec), CMD_DISCOVER_IDENT); @@ -2426,6 +2638,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); return 0; } + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT); *response_tx_sop_type = TCPC_TX_SOP; response[0] = VDO(USB_SID_PD, 1, @@ -2445,16 +2658,27 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, rlen = 1; } else { if (rx_sop_type == TCPC_TX_SOP) { + tcpm_update_vdm_discovery_state(port, + VDM_DISCOVERY_PARTNER_SVIDS); if (modep->nsvids && supports_modal(port)) { response[0] = VDO(modep->svids[0], 1, svdm_version, CMD_DISCOVER_MODES); rlen = 1; + } else { + tcpm_update_vdm_discovery_state(port, + VDM_DISCOVERY_COMPLETE); } } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { + tcpm_update_vdm_discovery_state(port, + VDM_DISCOVERY_CABLE_SVIDS); if (modep_prime->nsvids) { response[0] = VDO(modep_prime->svids[0], 1, svdm_version, CMD_DISCOVER_MODES); rlen = 1; + } else { + tcpm_register_partner_altmodes(port); + tcpm_update_vdm_discovery_state(port, + VDM_DISCOVERY_COMPLETE); } } } @@ -2505,8 +2729,13 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, case CMDT_RSP_NAK: tcpm_ams_finish(port); switch (cmd) { + /* + * The cable is not allowed to respond with NAK so this must've happened over SOP + */ case CMD_DISCOVER_IDENT: case CMD_DISCOVER_SVID: + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + break; case VDO_CMD_VENDOR(0) ... VDO_CMD_VENDOR(15): break; case CMD_DISCOVER_MODES: @@ -2686,44 +2915,6 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port, port->vdm_sm_running = false; } -static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd, - const u32 *data, int count, enum tcpm_transmit_type tx_sop_type) -{ - int svdm_version; - u32 header; - - switch (tx_sop_type) { - case TCPC_TX_SOP_PRIME: - /* - * If the port partner is discovered, then the port partner's - * SVDM Version will be returned - */ - svdm_version = typec_get_cable_svdm_version(port->typec_port); - if (svdm_version < 0) - svdm_version = SVDM_VER_MAX; - break; - case TCPC_TX_SOP: - svdm_version = typec_get_negotiated_svdm_version(port->typec_port); - if (svdm_version < 0) - return; - break; - default: - svdm_version = typec_get_negotiated_svdm_version(port->typec_port); - if (svdm_version < 0) - return; - break; - } - - if (WARN_ON(count > VDO_MAX_SIZE - 1)) - count = VDO_MAX_SIZE - 1; - - /* set VDM header with VID & CMD */ - header = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? - 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), - svdm_version, cmd); - tcpm_queue_vdm(port, header, data, count, tx_sop_type); -} - static unsigned int vdm_ready_timeout(u32 vdm_hdr) { unsigned int timeout; @@ -2769,8 +2960,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) * if there's traffic or we're not in PDO ready state don't send * a VDM. */ - if (port->state != SRC_READY && port->state != SNK_READY && - port->state != SRC_VDM_IDENTITY_REQUEST) { + if (!tcpm_can_send_vdm(port->state)) { port->vdm_sm_running = false; break; } @@ -2780,22 +2970,10 @@ static void vdm_run_state_machine(struct tcpm_port *port) switch (PD_VDO_CMD(vdo_hdr)) { case CMD_DISCOVER_IDENT: res = tcpm_ams_start(port, DISCOVER_IDENTITY); - if (res == 0) { - switch (port->tx_sop_type) { - case TCPC_TX_SOP_PRIME: - port->send_discover_prime = false; - break; - case TCPC_TX_SOP: - port->send_discover = false; - break; - default: - port->send_discover = false; - break; - } - } else if (res == -EAGAIN) { + if (res == -EAGAIN) { port->vdo_data[0] = 0; - mod_send_discover_delayed_work(port, - SEND_DISCOVER_RETRY_MS); + mod_vdm_discovery_delayed_work(port, + SEND_DISCOVERY_VDM_RETRY_MS); } break; case CMD_DISCOVER_SVID: @@ -2853,6 +3031,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) */ if (port->state == SRC_VDM_IDENTITY_REQUEST) { tcpm_ams_finish(port); + port->vdo_data[0] = 0; port->vdm_state = VDM_STATE_DONE; tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); /* @@ -2869,6 +3048,7 @@ static void vdm_run_state_machine(struct tcpm_port *port) tcpm_ams_finish(port); } else { tcpm_ams_finish(port); + port->vdo_data[0] = 0; if (port->tx_sop_type == TCPC_TX_SOP) break; /* Handle SOP' Transmission Errors */ @@ -2878,11 +3058,11 @@ static void vdm_run_state_machine(struct tcpm_port *port) * discovery process on SOP only. */ case CMD_DISCOVER_IDENT: - port->vdo_data[0] = 0; response[0] = VDO(USB_SID_PD, 1, typec_get_negotiated_svdm_version( port->typec_port), CMD_DISCOVER_SVID); + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT); tcpm_queue_vdm(port, response[0], &response[1], 0, TCPC_TX_SOP); break; @@ -2892,9 +3072,11 @@ static void vdm_run_state_machine(struct tcpm_port *port) */ case CMD_DISCOVER_SVID: tcpm_register_partner_altmodes(port); + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); break; case CMD_DISCOVER_MODES: tcpm_register_partner_altmodes(port); + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); break; default: break; @@ -3807,7 +3989,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); } else { - if (port->send_discover && port->negotiated_rev < PD_REV30) { + if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN && + port->negotiated_rev < PD_REV30) { tcpm_queue_message(port, PD_MSG_CTRL_WAIT); break; } @@ -3823,7 +4006,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); } else { - if (port->send_discover && port->negotiated_rev < PD_REV30) { + if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN && + port->negotiated_rev < PD_REV30) { tcpm_queue_message(port, PD_MSG_CTRL_WAIT); break; } @@ -3832,7 +4016,8 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, } break; case PD_CTRL_VCONN_SWAP: - if (port->send_discover && port->negotiated_rev < PD_REV30) { + if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN && + port->negotiated_rev < PD_REV30) { tcpm_queue_message(port, PD_MSG_CTRL_WAIT); break; } @@ -3891,6 +4076,7 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port, { enum pd_ext_msg_type type = pd_header_type_le(msg->header); unsigned int data_size = pd_ext_header_data_size_le(msg->ext_msg.header); + const struct pd_chunked_ext_message_data *ext_msg = &msg->ext_msg; /* stopping VDM state machine if interrupted by other Messages */ if (tcpm_vdm_ams(port)) { @@ -3899,7 +4085,7 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port, mod_vdm_delayed_work(port, 0); } - if (!(le16_to_cpu(msg->ext_msg.header) & PD_EXT_HDR_CHUNKED)) { + if (!(le16_to_cpu(ext_msg->header) & PD_EXT_HDR_CHUNKED)) { tcpm_pd_handle_msg(port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); tcpm_log(port, "Unchunked extended messages unsupported"); return; @@ -3924,9 +4110,25 @@ static void tcpm_pd_ext_msg_request(struct tcpm_port *port, NONE_AMS, 0); } break; - case PD_EXT_SOURCE_CAP_EXT: - case PD_EXT_GET_BATT_CAP: case PD_EXT_GET_BATT_STATUS: + if (data_size >= 1) { + port->batt_request_id = ext_msg->data[0]; + tcpm_pd_handle_msg(port, PD_MSG_DATA_BATT_STATUS, + GETTING_BATTERY_STATUS); + } else { + tcpm_set_state(port, SOFT_RESET_SEND, 0); + } + break; + case PD_EXT_GET_BATT_CAP: + if (data_size >= 1) { + port->batt_request_id = ext_msg->data[0]; + tcpm_pd_handle_msg(port, PD_MSG_EXT_BATT_CAP, + GETTING_BATTERY_CAPABILITIES); + } else { + tcpm_set_state(port, SOFT_RESET_SEND, 0); + } + break; + case PD_EXT_SOURCE_CAP_EXT: case PD_EXT_BATT_CAP: case PD_EXT_GET_MANUFACTURER_INFO: case PD_EXT_MANUFACTURER_INFO: @@ -4137,6 +4339,22 @@ static bool tcpm_send_queued_message(struct tcpm_port *port) ret); tcpm_ams_finish(port); break; + case PD_MSG_DATA_BATT_STATUS: + ret = tcpm_pd_send_batt_status(port); + if (ret) + tcpm_log(port, + "Failed to send battery status ret=%d", + ret); + tcpm_ams_finish(port); + break; + case PD_MSG_EXT_BATT_CAP: + ret = tcpm_pd_send_batt_cap(port); + if (ret) + tcpm_log(port, + "Failed to send battery cap ret=%d", + ret); + tcpm_ams_finish(port); + break; default: break; } @@ -4853,8 +5071,7 @@ static int tcpm_src_attach(struct tcpm_port *port) port->partner = NULL; port->attached = true; - port->send_discover = true; - port->send_discover_prime = false; + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); return 0; @@ -4896,11 +5113,11 @@ static void tcpm_unregister_altmodes(struct tcpm_port *port) struct pd_mode_data *modep_prime = &port->mode_data_prime; int i; - for (i = 0; i < modep->altmodes; i++) { + for (i = 0; i < modep->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { typec_unregister_altmode(port->partner_altmode[i]); port->partner_altmode[i] = NULL; } - for (i = 0; i < modep_prime->altmodes; i++) { + for (i = 0; i < modep_prime->altmodes && i < ALTMODE_DISCOVERY_MAX; i++) { typec_unregister_altmode(port->plug_prime_altmode[i]); port->plug_prime_altmode[i] = NULL; } @@ -4931,6 +5148,8 @@ static void tcpm_reset_port(struct tcpm_port *port) port->in_ams = false; port->ams = NONE_AMS; port->vdm_sm_running = false; + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); + mod_vdm_discovery_cancel_delayed_work(port); tcpm_unregister_altmodes(port); tcpm_typec_disconnect(port); port->attached = false; @@ -5014,8 +5233,7 @@ static int tcpm_snk_attach(struct tcpm_port *port) port->partner = NULL; port->attached = true; - port->send_discover = true; - port->send_discover_prime = false; + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); return 0; } @@ -5422,16 +5640,11 @@ static void run_state_machine(struct tcpm_port *port) * as well. */ if (port->explicit_contract) { - if (port->send_discover_prime) { - port->tx_sop_type = TCPC_TX_SOP_PRIME; - } else { - port->tx_sop_type = TCPC_TX_SOP; + if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN) tcpm_set_initial_svdm_version(port); - } - mod_send_discover_delayed_work(port, 0); + mod_vdm_discovery_delayed_work(port, 0); } else { - port->send_discover = false; - port->send_discover_prime = false; + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); } /* @@ -5814,16 +6027,11 @@ static void run_state_machine(struct tcpm_port *port) * as well. */ if (port->explicit_contract) { - if (port->send_discover_prime) { - port->tx_sop_type = TCPC_TX_SOP_PRIME; - } else { - port->tx_sop_type = TCPC_TX_SOP; + if (port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN) tcpm_set_initial_svdm_version(port); - } - mod_send_discover_delayed_work(port, 0); + mod_vdm_discovery_delayed_work(port, 0); } else { - port->send_discover = false; - port->send_discover_prime = false; + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); } power_supply_changed(port->psy); @@ -5869,8 +6077,8 @@ static void run_state_machine(struct tcpm_port *port) port->tcpc->set_pd_rx(port->tcpc, false); tcpm_unregister_altmodes(port); port->nr_sink_caps = 0; - port->send_discover = true; - port->send_discover_prime = false; + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); + mod_vdm_discovery_cancel_delayed_work(port); if (port->pwr_role == TYPEC_SOURCE) tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF, PD_T_PS_HARD_RESET); @@ -6017,25 +6225,15 @@ static void run_state_machine(struct tcpm_port *port) /* DR_Swap states */ case DR_SWAP_SEND: tcpm_pd_send_control(port, PD_CTRL_DR_SWAP, TCPC_TX_SOP); - if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) { - port->send_discover = true; - port->send_discover_prime = false; - } tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; case DR_SWAP_ACCEPT: tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); - if (port->data_role == TYPEC_DEVICE || port->negotiated_rev > PD_REV20) { - port->send_discover = true; - port->send_discover_prime = false; - } tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0); break; case DR_SWAP_SEND_TIMEOUT: tcpm_swap_complete(port, -ETIMEDOUT); - port->send_discover = false; - port->send_discover_prime = false; tcpm_ams_finish(port); tcpm_set_state(port, ready_state(port), 0); break; @@ -6047,6 +6245,8 @@ static void run_state_machine(struct tcpm_port *port) else tcpm_set_roles(port, true, TYPEC_STATE_USB, port->pwr_role, TYPEC_HOST); + if (port->data_role == TYPEC_HOST || port->negotiated_rev > PD_REV20) + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_UNKNOWN); tcpm_ams_finish(port); tcpm_set_state(port, ready_state(port), 0); break; @@ -6330,9 +6530,7 @@ static void run_state_machine(struct tcpm_port *port) /* Cable states */ case SRC_VDM_IDENTITY_REQUEST: - port->send_discover_prime = true; - port->tx_sop_type = TCPC_TX_SOP_PRIME; - mod_send_discover_delayed_work(port, 0); + mod_vdm_discovery_delayed_work(port, 0); port->upcoming_state = SRC_SEND_CAPABILITIES; break; @@ -7052,8 +7250,8 @@ static void tcpm_enable_frs_work(struct kthread_work *work) goto unlock; /* Send when the state machine is idle */ - if (port->state != SNK_READY || port->vdm_sm_running || port->send_discover || - port->send_discover_prime) + if (port->state != SNK_READY || port->vdm_sm_running || + port->vdm_discovery_state == VDM_DISCOVERY_UNKNOWN) goto resched; port->upcoming_state = GET_SINK_CAP; @@ -7070,29 +7268,160 @@ static void tcpm_enable_frs_work(struct kthread_work *work) mutex_unlock(&port->lock); } -static void tcpm_send_discover_work(struct kthread_work *work) +static void tcpm_vdm_discovery_work(struct kthread_work *work) { - struct tcpm_port *port = container_of(work, struct tcpm_port, send_discover_work); + struct tcpm_port *port = container_of(work, struct tcpm_port, vdm_discovery_work); + enum tcpm_transmit_type tx_sop_type = TCPC_TX_SOP; + struct typec_port *typec = port->typec_port; + struct pd_mode_data *modep, *modep_prime; + u32 msg[2] = { }; + int svdm_version; mutex_lock(&port->lock); - /* No need to send DISCOVER_IDENTITY anymore */ - if (!port->send_discover && !port->send_discover_prime) - goto unlock; - if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) { - port->send_discover = false; - port->send_discover_prime = false; + tcpm_log_force(port, "%s state [%s]", __func__, + vdm_discovery_state_strings[port->vdm_discovery_state]); + + /* No need to perform work if Discovery process is complete */ + if (port->vdm_discovery_state == VDM_DISCOVERY_COMPLETE) goto unlock; - } /* Retry if the port is not idle */ - if ((port->state != SRC_READY && port->state != SNK_READY && - port->state != SRC_VDM_IDENTITY_REQUEST) || port->vdm_sm_running) { - mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); + if (!tcpm_can_send_vdm(port->state) || port->vdm_sm_running) { + mod_vdm_discovery_delayed_work(port, SEND_DISCOVERY_VDM_RETRY_MS); goto unlock; } - tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0, port->tx_sop_type); + modep = &port->mode_data; + modep_prime = &port->mode_data_prime; + + svdm_version = typec_get_negotiated_svdm_version(typec); + + switch (port->vdm_discovery_state) { + /* + * The port has not received a Discover Identity response from the port partner. + * + * 1. The port will send Discover Identity to the partner over SOP in the SRC_READY and + * SNK_READY states if there is an explicit contract + * 2. The port will send Discover Identity to the cable over SOP' in the + * SRC_VDM_IDENTITY_REQUEST state if capable of doing so. + */ + case VDM_DISCOVERY_UNKNOWN: + /* Can't send Discover Identity, VDM discovery is complete */ + if (port->data_role == TYPEC_DEVICE && port->negotiated_rev < PD_REV30) { + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + goto unlock; + } + + if (port->state == SRC_VDM_IDENTITY_REQUEST) { + tx_sop_type = TCPC_TX_SOP_PRIME; + svdm_version = SVDM_VER_MAX; + } + + msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_IDENT); + break; + /* + * The port has received a Discover Identity ACK from the port partner. + * + * 1. The port will send Discover Identity to the cable over SOP' in the SRC_READY and + * SNK_READY states if it did not previously discover the cable but is capable of doing + * so. + * 2. The port will send Discover SVIDs to the partner over SOP in the SRC_READY and + * SNK_READY states otherwise. + */ + case VDM_DISCOVERY_PARTNER_IDENT: + if (tcpm_can_communicate_sop_prime(port) && !port->cable) { + tx_sop_type = TCPC_TX_SOP_PRIME; + msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_IDENT); + } else { + if (tcpm_can_communicate_sop_prime(port)) + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_CABLE_IDENT); + msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID); + } + break; + /* + * The port has received a Discover Identity ACK from the cable. + * + * 1. The port will send Discover SVIDs to the partner over SOP. + */ + case VDM_DISCOVERY_CABLE_IDENT: + msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID); + break; + /* + * The port has received a Discover SVIDs ACK from the partner or the last SVIDs supported + * by the partner. + * + * 1. The port will send Discover Modes for the first SVID over SOP if the partner supports + * modal operation and valid SVIDs were registered. + * 2. The vdm_discovery_state will move to VDM_DISCOVERY_COMPLETE otherwise. + */ + case VDM_DISCOVERY_PARTNER_SVIDS: + if (modep->nsvids && supports_modal(port)) { + msg[0] = VDO(modep->svids[0], 1, svdm_version, CMD_DISCOVER_MODES); + } else { + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + goto unlock; + } + break; + /* + * The port has received a Discover Modes ACK from the partner for any mode. + * + * 1. The port will send Discover Modes for the next SVID that has not been discovered to + * the port partner over SOP. + * 2. The port will send Discover SVIDs over SOP' if the port can communicate over SOP' + * and the cable supports VDMs. + * 3. The vdm_discovery_state will move to VDM_DISCOVERY_COMPLETE otherwise. + */ + case VDM_DISCOVERY_PARTNER_MODES: + /* Not all modes have been discovered yet */ + if (modep->svid_index < modep->nsvids) { + msg[0] = VDO(modep_prime->svids[modep->svid_index], 1, svdm_version, + CMD_DISCOVER_MODES); + } else if (tcpm_can_communicate_sop_prime(port) && tcpm_cable_vdm_supported(port)) { + tx_sop_type = TCPC_TX_SOP_PRIME; + svdm_version = typec_get_cable_svdm_version(typec); + msg[0] = VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID); + } else { + tcpm_update_vdm_discovery_state(port, VDM_DISCOVERY_COMPLETE); + goto unlock; + } + break; + /* + * The port has received a Discover SVIDs ACK from the cable over SOP'. + * + * 1. The port will send Discover Modes for the first SVID over SOP'. + */ + case VDM_DISCOVERY_CABLE_SVIDS: + if (modep_prime->nsvids) { + tx_sop_type = TCPC_TX_SOP_PRIME; + svdm_version = typec_get_cable_svdm_version(typec); + msg[0] = VDO(modep_prime->svids[0], 1, svdm_version, CMD_DISCOVER_MODES); + } else { + goto unlock; + } + break; + /* + * The port has received a Discover Modes ACK from the cable for any mode. + * + * 1. The port will send Discover Modes for the next SVID that has not been discovered to + * the cable over SOP'. + */ + case VDM_DISCOVERY_CABLE_MODES: + if (modep_prime->svid_index < modep_prime->nsvids) { + tx_sop_type = TCPC_TX_SOP_PRIME; + svdm_version = typec_get_cable_svdm_version(typec); + msg[0] = VDO(modep_prime->svids[modep->svid_index], 1, svdm_version, + CMD_DISCOVER_MODES); + } else { + goto unlock; + } + break; + default: + goto unlock; + } + + if (svdm_version >= 0) + tcpm_queue_vdm(port, msg[0], &msg[1], 0, tx_sop_type); unlock: mutex_unlock(&port->lock); @@ -8508,12 +8837,12 @@ static enum hrtimer_restart enable_frs_timer_handler(struct hrtimer *timer) return HRTIMER_NORESTART; } -static enum hrtimer_restart send_discover_timer_handler(struct hrtimer *timer) +static enum hrtimer_restart vdm_discovery_timer_handler(struct hrtimer *timer) { - struct tcpm_port *port = container_of(timer, struct tcpm_port, send_discover_timer); + struct tcpm_port *port = container_of(timer, struct tcpm_port, vdm_discovery_timer); if (port->registered) - kthread_queue_work(port->wq, &port->send_discover_work); + kthread_queue_work(port->wq, &port->vdm_discovery_work); return HRTIMER_NORESTART; } @@ -8547,14 +8876,14 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) kthread_init_work(&port->vdm_state_machine, vdm_state_machine_work); kthread_init_work(&port->event_work, tcpm_pd_event_handler); kthread_init_work(&port->enable_frs, tcpm_enable_frs_work); - kthread_init_work(&port->send_discover_work, tcpm_send_discover_work); + kthread_init_work(&port->vdm_discovery_work, tcpm_vdm_discovery_work); hrtimer_setup(&port->state_machine_timer, state_machine_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer_setup(&port->vdm_state_machine_timer, vdm_state_machine_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer_setup(&port->enable_frs_timer, enable_frs_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hrtimer_setup(&port->send_discover_timer, send_discover_timer_handler, CLOCK_MONOTONIC, + hrtimer_setup(&port->vdm_discovery_timer, vdm_discovery_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL); spin_lock_init(&port->pd_event_lock); @@ -8649,11 +8978,12 @@ void tcpm_unregister_port(struct tcpm_port *port) port->registered = false; kthread_destroy_worker(port->wq); - hrtimer_cancel(&port->send_discover_timer); + hrtimer_cancel(&port->vdm_discovery_timer); hrtimer_cancel(&port->enable_frs_timer); hrtimer_cancel(&port->vdm_state_machine_timer); hrtimer_cancel(&port->state_machine_timer); + power_supply_put_system_batteries(port->fixed_batt, port->fixed_batt_cnt); tcpm_reset_port(port); tcpm_port_unregister_pd(port); diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -129,6 +129,7 @@ enum { TPS_MODE_BIST, TPS_MODE_DISC, TPS_MODE_PTCH, + TPS_MODE_APP1, }; static const char *const modes[] = { @@ -137,6 +138,7 @@ static const char *const modes[] = { [TPS_MODE_BIST] = "BIST", [TPS_MODE_DISC] = "DISC", [TPS_MODE_PTCH] = "PTCH", + [TPS_MODE_APP1] = "APP1", }; /* Unrecognized commands will be replaced with "!CMD" */ @@ -158,6 +160,7 @@ struct tipd_data { int (*init)(struct tps6598x *tps); int (*switch_power_state)(struct tps6598x *tps, u8 target_state); bool (*read_data_status)(struct tps6598x *tps); + bool (*read_power_status)(struct tps6598x *tps); int (*reset)(struct tps6598x *tps); int (*connect)(struct tps6598x *tps, u32 status); }; @@ -629,6 +632,34 @@ static bool tps6598x_read_power_status(struct tps6598x *tps) return true; } +/* + * TPS66993 deprecated Power_Status register (0x3F). BC1.2 is not supported + * and the remaining bits are redundant with STATUS register (0x1A). + * Synthesize pwr_status from the already-read STATUS register. + */ +static bool tps66993_read_power_status(struct tps6598x *tps) +{ + u16 pwr_status = 0; + + /* Same masks as TPS_POWER_STATUS_CONNECTION() / SOURCESINK() / PWROPMODE() in tps6598x.h */ + if (tps->status & TPS_STATUS_PLUG_PRESENT) + pwr_status |= FIELD_PREP(TPS_POWER_STATUS_CONNECTION_MASK, 1); + + /* SOURCESINK: 1=sink; STATUS.PortRole 1=source, opposite convention */ + if (!TPS_STATUS_TO_TYPEC_PORTROLE(tps->status)) + pwr_status |= FIELD_PREP(TPS_POWER_STATUS_SOURCESINK_MASK, 1); + + if (TPS_STATUS_VBUS_STATUS(tps->status) == TPS_STATUS_VBUS_STATUS_PD) + pwr_status |= FIELD_PREP(TPS_POWER_STATUS_TYPEC_CURRENT_MASK, + TPS_POWER_STATUS_TYPEC_CURRENT_PD); + + tps->pwr_status = pwr_status; + + tps->data->trace_power_status(pwr_status); + + return true; +} + static void tps6598x_handle_plug_event(struct tps6598x *tps, u32 status) { int ret; @@ -906,7 +937,7 @@ static irqreturn_t cd321x_interrupt(int irq, void *data) goto err_unlock; if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE) { - if (!tps6598x_read_power_status(tps)) + if (!tps->data->read_power_status(tps)) goto err_unlock; if (TPS_POWER_STATUS_PWROPMODE(tps->pwr_status) == TYPEC_PWR_MODE_PD) { if (tps6598x_read_partner_identity(tps)) { @@ -961,7 +992,7 @@ static irqreturn_t tps25750_interrupt(int irq, void *data) goto err_clear_ints; if (event[0] & TPS_REG_INT_POWER_STATUS_UPDATE) - if (!tps6598x_read_power_status(tps)) + if (!tps->data->read_power_status(tps)) goto err_clear_ints; if (event[0] & TPS_REG_INT_DATA_STATUS_UPDATE) @@ -1034,17 +1065,25 @@ static irqreturn_t tps6598x_interrupt(int irq, void *data) if (!tps6598x_read_status(tps, &status)) goto err_unlock; + tps->status = status; + if ((event1[0] | event2[0]) & TPS_REG_INT_POWER_STATUS_UPDATE) - if (!tps6598x_read_power_status(tps)) + if (!tps->data->read_power_status(tps)) goto err_unlock; if ((event1[0] | event2[0]) & TPS_REG_INT_DATA_STATUS_UPDATE) if (!tps->data->read_data_status(tps)) goto err_unlock; - /* Handle plug insert or removal */ - if ((event1[0] | event2[0]) & TPS_REG_INT_PLUG_EVENT) + /* + * Refresh power status before connect - needed for TPS66993 which + * synthesizes pwr_status from STATUS and never gets POWER_STATUS_UPDATE. + */ + if ((event1[0] | event2[0]) & TPS_REG_INT_PLUG_EVENT) { + if (!tps->data->read_power_status(tps)) + goto err_unlock; tps6598x_handle_plug_event(tps, status); + } err_unlock: mutex_unlock(&tps->lock); @@ -1080,6 +1119,7 @@ static int tps6598x_check_mode(struct tps6598x *tps) switch (ret) { case TPS_MODE_APP: + case TPS_MODE_APP1: case TPS_MODE_PTCH: return ret; case TPS_MODE_BOOT: @@ -1753,7 +1793,7 @@ static int tps6598x_probe(struct i2c_client *client) struct tps6598x *tps; struct fwnode_handle *fwnode; u32 status; - u32 vid; + u32 vid = 0; int ret; data = i2c_get_match_data(client); @@ -1781,8 +1821,11 @@ static int tps6598x_probe(struct i2c_client *client) if (!device_is_compatible(tps->dev, "ti,tps25750")) { ret = tps6598x_read32(tps, TPS_REG_VID, &vid); - if (ret < 0 || !vid) + if (ret < 0 || !vid) { + dev_err(tps->dev, "failed to read vendor ID: %d, vid: %#x\n", + ret, vid); return -ENODEV; + } } /* @@ -1818,6 +1861,8 @@ static int tps6598x_probe(struct i2c_client *client) goto err_clear_mask; } + tps->status = status; + /* * This fwnode has a "compatible" property, but is never populated as a * struct device. Instead we simply parse it to read the properties. @@ -1845,7 +1890,7 @@ static int tps6598x_probe(struct i2c_client *client) if (status & TPS_STATUS_PLUG_PRESENT) { ret = -EINVAL; - if (!tps6598x_read_power_status(tps)) + if (!tps->data->read_power_status(tps)) goto err_unregister_port; if (!tps->data->read_data_status(tps)) goto err_unregister_port; @@ -1860,7 +1905,7 @@ static int tps6598x_probe(struct i2c_client *client) IRQF_SHARED | IRQF_ONESHOT, dev_name(&client->dev), tps); } else { - dev_warn(tps->dev, "Unable to find the interrupt, switching to polling\n"); + dev_dbg(tps->dev, "no IRQ specified, using polling mode\n"); INIT_DELAYED_WORK(&tps->wq_poll, tps6598x_poll_work); queue_delayed_work(system_power_efficient_wq, &tps->wq_poll, msecs_to_jiffies(POLL_INTERVAL)); @@ -1987,6 +2032,7 @@ static const struct tipd_data cd321x_data = { .trace_status = trace_tps6598x_status, .init = cd321x_init, .read_data_status = cd321x_read_data_status, + .read_power_status = tps6598x_read_power_status, .reset = cd321x_reset, .switch_power_state = cd321x_switch_power_state, .connect = cd321x_connect, @@ -2006,6 +2052,25 @@ static const struct tipd_data tps6598x_data = { .apply_patch = tps6598x_apply_patch, .init = tps6598x_init, .read_data_status = tps6598x_read_data_status, + .read_power_status = tps6598x_read_power_status, + .reset = tps6598x_reset, + .connect = tps6598x_connect, +}; + +static const struct tipd_data tps66993_data = { + .irq_handler = tps6598x_interrupt, + .irq_mask1 = TPS_REG_INT_DATA_STATUS_UPDATE | + TPS_REG_INT_PLUG_EVENT, + .tps_struct_size = sizeof(struct tps6598x), + .register_port = tps6598x_register_port, + .unregister_port = tps6598x_unregister_port, + .trace_data_status = trace_tps6598x_data_status, + .trace_power_status = trace_tps6598x_power_status, + .trace_status = trace_tps6598x_status, + .apply_patch = tps6598x_apply_patch, + .init = tps6598x_init, + .read_data_status = tps6598x_read_data_status, + .read_power_status = tps66993_read_power_status, .reset = tps6598x_reset, .connect = tps6598x_connect, }; @@ -2024,12 +2089,14 @@ static const struct tipd_data tps25750_data = { .apply_patch = tps25750_apply_patch, .init = tps25750_init, .read_data_status = tps6598x_read_data_status, + .read_power_status = tps6598x_read_power_status, .reset = tps25750_reset, .connect = tps6598x_connect, }; static const struct of_device_id tps6598x_of_match[] = { { .compatible = "ti,tps6598x", &tps6598x_data}, + { .compatible = "ti,tps66993", &tps66993_data}, { .compatible = "apple,cd321x", &cd321x_data}, { .compatible = "ti,tps25750", &tps25750_data}, {} diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h --- a/drivers/usb/typec/tipd/tps6598x.h +++ b/drivers/usb/typec/tipd/tps6598x.h @@ -142,9 +142,13 @@ #define TPS_SYSTEM_POWER_STATE_S4 0x04 #define TPS_SYSTEM_POWER_STATE_S5 0x05 -/* TPS_REG_POWER_STATUS bits */ -#define TPS_POWER_STATUS_CONNECTION(x) TPS_FIELD_GET(BIT(0), (x)) -#define TPS_POWER_STATUS_SOURCESINK(x) TPS_FIELD_GET(BIT(1), (x)) +/* TPS_REG_POWER_STATUS bits (masks shared by TPS_FIELD_GET accessors and FIELD_PREP) */ +#define TPS_POWER_STATUS_CONNECTION_MASK BIT(0) +#define TPS_POWER_STATUS_SOURCESINK_MASK BIT(1) +#define TPS_POWER_STATUS_CONNECTION(x) \ + TPS_FIELD_GET(TPS_POWER_STATUS_CONNECTION_MASK, (x)) +#define TPS_POWER_STATUS_SOURCESINK(x) \ + TPS_FIELD_GET(TPS_POWER_STATUS_SOURCESINK_MASK, (x)) #define TPS_POWER_STATUS_BC12_DET(x) TPS_FIELD_GET(BIT(2), (x)) #define TPS_POWER_STATUS_TYPEC_CURRENT_MASK GENMASK(3, 2) diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -82,8 +82,8 @@ static int ucsi_resp_show(struct seq_file *s, void *not_used) if (ucsi->debugfs->status) return ucsi->debugfs->status; - seq_printf(s, "0x%016llx%016llx\n", ucsi->debugfs->response.high, - ucsi->debugfs->response.low); + seq_printf(s, "0x%016llx%016llx%016llx\n", ucsi->debugfs->response.ext, + ucsi->debugfs->response.high, ucsi->debugfs->response.low); return 0; } DEFINE_SHOW_ATTRIBUTE(ucsi_resp); diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1102,8 +1102,8 @@ static int ucsi_register_plug(struct ucsi_connector *con) plug = typec_register_plug(con->cable, &desc); if (IS_ERR(plug)) { dev_err(con->ucsi->dev, - "con%d: failed to register plug (%ld)\n", con->num, - PTR_ERR(plug)); + "con%d: failed to register plug (%pe)\n", con->num, + plug); return PTR_ERR(plug); } @@ -1161,8 +1161,8 @@ static int ucsi_register_cable(struct ucsi_connector *con) cable = typec_register_cable(con->port, &desc); if (IS_ERR(cable)) { dev_err(con->ucsi->dev, - "con%d: failed to register cable (%ld)\n", con->num, - PTR_ERR(cable)); + "con%d: failed to register cable (%pe)\n", con->num, + cable); return PTR_ERR(cable); } @@ -1291,8 +1291,8 @@ static int ucsi_register_partner(struct ucsi_connector *con) partner = typec_register_partner(con->port, &desc); if (IS_ERR(partner)) { dev_err(con->ucsi->dev, - "con%d: failed to register partner (%ld)\n", con->num, - PTR_ERR(partner)); + "con%d: failed to register partner (%pe)\n", con->num, + partner); return PTR_ERR(partner); } @@ -2221,7 +2221,7 @@ static void ucsi_init_work(struct work_struct *work) return; } - queue_delayed_work(system_long_wq, &ucsi->work, + queue_delayed_work(system_dfl_long_wq, &ucsi->work, UCSI_ROLE_SWITCH_INTERVAL); } } @@ -2345,7 +2345,7 @@ int ucsi_register(struct ucsi *ucsi) UCSI_BCD_GET_MINOR(ucsi->version), UCSI_BCD_GET_SUBMINOR(ucsi->version)); - queue_delayed_work(system_long_wq, &ucsi->work, 0); + queue_delayed_work(system_dfl_long_wq, &ucsi->work, 0); ucsi_debugfs_register(ucsi); return 0; diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -466,6 +466,7 @@ struct ucsi_debugfs_entry { struct ucsi_data { u64 low; u64 high; + u64 ext; } response; int status; u8 message_out[MESSAGE_OUT_MAX_LEN]; diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -806,11 +806,26 @@ extern int power_supply_reg_notifier(struct notifier_block *nb); extern void power_supply_unreg_notifier(struct notifier_block *nb); #if IS_ENABLED(CONFIG_POWER_SUPPLY) extern struct power_supply *power_supply_get_by_name(const char *name); +extern int __must_check power_supply_get_system_batteries(struct device *dev, + struct power_supply ***psys); +extern void power_supply_put_system_batteries(struct power_supply **psys, int count); extern void power_supply_put(struct power_supply *psy); #else static inline void power_supply_put(struct power_supply *psy) {} static inline struct power_supply *power_supply_get_by_name(const char *name) { return NULL; } +static inline int __must_check power_supply_get_system_batteries(struct device *dev, + struct power_supply ***psys) +{ + if (psys) + *psys = NULL; + return 0; +} + +static inline void power_supply_put_system_batteries(struct power_supply **psys, + int count) +{ +} #endif extern struct power_supply *power_supply_get_by_reference(struct fwnode_handle *fwnode, const char *property); diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -106,6 +106,9 @@ enum pd_ext_msg_type { #define PD_HEADER_LE(type, pwr, data, rev, id, cnt) \ cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (0))) +#define PD_HEADER_EXT_LE(type, pwr, data, rev, id, cnt) \ + cpu_to_le16(PD_HEADER((type), (pwr), (data), (rev), (id), (cnt), (1))) + static inline unsigned int pd_header_cnt(u16 header) { return (header >> PD_HEADER_CNT_SHIFT) & PD_HEADER_CNT_MASK; @@ -219,6 +222,25 @@ static inline u8 count_chunked_data_objs(u32 size) return ((size / 4) + (size % 4 ? 1 : 0)); } +/** + * batt_cap_ext_msg - Battery capability extended PD message + * @vid: Battery Vendor ID (assigned by USB-IF) + * @pid: Battery Product ID (assigned by battery or device vendor) + * @batt_design_cap: Battery design capacity in 0.1Wh + * @batt_last_chg_cap: Battery last full charge capacity in 0.1Wh + * @batt_type: Battery Type. bit0 when set indicates invalid battery reference. + * Rest of the bits are reserved. + */ +struct batt_cap_ext_msg { + __le16 vid; + __le16 pid; + __le16 batt_design_cap; + __le16 batt_last_chg_cap; + u8 batt_type; +} __packed; + +#define BATT_CAP_BATT_TYPE_INVALID_REF BIT(0) + /* Sink Caps Extended Data Block Version */ #define SKEDB_VER_1_0 1 @@ -724,4 +746,33 @@ void usb_power_delivery_unlink_device(struct usb_power_delivery *pd, struct devi #endif /* CONFIG_TYPEC */ +/* Battery Status Data Object */ +#define BSDO_PRESENT_CAPACITY GENMASK(31, 16) +#define BSDO_CHG_STATUS GENMASK(11, 10) +#define BSDO_BATTERY_PRESENT BIT(9) +#define BSDO_INVALID_BATTERY_REFERENCE BIT(8) + +/* + * Battery Charge Status: Battery Charging Status Values as defined in + * "USB PD Spec Rev3.1 Ver1.8", "Table 6-46 Battery Status Data Object (BSDO)". + */ +#define BSDO_BATTERY_INFO_CHARGING 0x0 +#define BSDO_BATTERY_INFO_DISCHARGING 0x1 +#define BSDO_BATTERY_INFO_IDLE 0x2 +#define BSDO_BATTERY_INFO_RSVD 0x3 + +/** + * BSDO() - Pack data into Battery Status Data Object format. + * @batt_charge: Battery's present state of charge in 0.1WH increment. + * @chg_status: Battery charge status. + * @batt_present: Indicates that battery is present/attached when set else absent when unset. + * @invalid_ref: Indicates that an invalid battery reference was made in the Get_Battery_Status + * request. + */ +#define BSDO(batt_charge, chg_status, batt_present, invalid_ref) \ + ((FIELD_PREP(BSDO_PRESENT_CAPACITY, batt_charge)) | \ + (FIELD_PREP(BSDO_CHG_STATUS, chg_status)) | \ + ((batt_present) ? BSDO_BATTERY_PRESENT : 0) | \ + ((invalid_ref) ? BSDO_INVALID_BATTERY_REFERENCE : 0)) + #endif /* __LINUX_USB_PD_H */ diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -337,6 +337,7 @@ void typec_unregister_cable(struct typec_cable *cable); struct typec_cable *typec_cable_get(struct typec_port *port); void typec_cable_put(struct typec_cable *cable); int typec_cable_is_active(struct typec_cable *cable); +bool typec_cable_altmode_unsupported(struct typec_altmode *alt); struct typec_plug *typec_register_plug(struct typec_cable *cable, struct typec_plug_desc *desc); diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h --- a/include/uapi/linux/usb/functionfs.h +++ b/include/uapi/linux/usb/functionfs.h @@ -25,6 +25,7 @@ enum functionfs_flags { FUNCTIONFS_EVENTFD = 32, FUNCTIONFS_ALL_CTRL_RECIP = 64, FUNCTIONFS_CONFIG0_SETUP = 128, + FUNCTIONFS_RW_PROXY_EPS = 256, }; /* Descriptor of an non-audio endpoint */ @@ -414,4 +415,27 @@ struct usb_functionfs_event { #define FUNCTIONFS_DMABUF_TRANSFER _IOW('g', 133, \ struct usb_ffs_dmabuf_transfer_req) +/* + * Enable or disable automatic zero-length packet (ZLP) appending for the + * endpoint. The argument is a pointer to a __u32: 0 to disable, non-zero to + * enable. + * + * When enabled, the kernel will automatically append a ZLP at the end of + * a transfer if the payload length is an exact multiple of the endpoint's + * max packet size. + * + * This is useful for compatibility with legacy protocols which require + * automatic ZLP appending to data written from userspace. + * + * This ioctl can only be used on IN endpoints. It can be called at any time + * after the FunctionFS instance is active, even before the host has connected + * or enabled the endpoint. + * + * Returns zero on success, or a negative errno value on error: + * -ENODEV: The FunctionFS instance is not active. + * -EINVAL: The endpoint is not an IN endpoint. + * -EFAULT: Invalid user space pointer for the argument. + */ +#define FUNCTIONFS_ENDPOINT_ENABLE_ZLP _IOW('g', 134, __u32) + #endif /* _UAPI__LINUX_FUNCTIONFS_H__ */ diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -24,11 +24,8 @@ }; use core::{ marker::PhantomData, - mem::{ - offset_of, - MaybeUninit, // - }, - ptr::NonNull, + mem::offset_of, + ptr::NonNull, // }; /// An adapter for the registration of USB drivers. @@ -130,8 +127,7 @@ pub const fn from_id(vendor: u16, product: u16) -> Self { match_flags: bindings::USB_DEVICE_ID_MATCH_DEVICE as u16, idVendor: vendor, idProduct: product, - // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. - ..unsafe { MaybeUninit::zeroed().assume_init() } + ..pin_init::zeroed() }) } @@ -143,8 +139,7 @@ pub const fn from_device_ver(vendor: u16, product: u16, bcd_lo: u16, bcd_hi: u16 idProduct: product, bcdDevice_lo: bcd_lo, bcdDevice_hi: bcd_hi, - // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. - ..unsafe { MaybeUninit::zeroed().assume_init() } + ..pin_init::zeroed() }) } @@ -155,8 +150,7 @@ pub const fn from_device_info(class: u8, subclass: u8, protocol: u8) -> Self { bDeviceClass: class, bDeviceSubClass: subclass, bDeviceProtocol: protocol, - // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. - ..unsafe { MaybeUninit::zeroed().assume_init() } + ..pin_init::zeroed() }) } @@ -167,8 +161,7 @@ pub const fn from_interface_info(class: u8, subclass: u8, protocol: u8) -> Self bInterfaceClass: class, bInterfaceSubClass: subclass, bInterfaceProtocol: protocol, - // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. - ..unsafe { MaybeUninit::zeroed().assume_init() } + ..pin_init::zeroed() }) } @@ -180,8 +173,7 @@ pub const fn from_device_interface_class(vendor: u16, product: u16, class: u8) - idVendor: vendor, idProduct: product, bInterfaceClass: class, - // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. - ..unsafe { MaybeUninit::zeroed().assume_init() } + ..pin_init::zeroed() }) } @@ -193,8 +185,7 @@ pub const fn from_device_interface_protocol(vendor: u16, product: u16, protocol: idVendor: vendor, idProduct: product, bInterfaceProtocol: protocol, - // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. - ..unsafe { MaybeUninit::zeroed().assume_init() } + ..pin_init::zeroed() }) } @@ -206,8 +197,7 @@ pub const fn from_device_interface_number(vendor: u16, product: u16, number: u8) idVendor: vendor, idProduct: product, bInterfaceNumber: number, - // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. - ..unsafe { MaybeUninit::zeroed().assume_init() } + ..pin_init::zeroed() }) } @@ -227,8 +217,7 @@ pub const fn from_device_and_interface_info( bInterfaceClass: class, bInterfaceSubClass: subclass, bInterfaceProtocol: protocol, - // SAFETY: It is safe to use all zeroes for the other fields of `usb_device_id`. - ..unsafe { MaybeUninit::zeroed().assume_init() } + ..pin_init::zeroed() }) } } @@ -393,6 +382,7 @@ fn as_ref(&self) -> &Device { // SAFETY: Instances of `Interface` are always reference-counted. unsafe impl AlwaysRefCounted for Interface { + #[inline] fn inc_ref(&self) { // SAFETY: The invariants of `Interface` guarantee that `self.as_raw()` // returns a valid `struct usb_interface` pointer, for which we will @@ -400,6 +390,7 @@ fn inc_ref(&self) { unsafe { bindings::usb_get_intf(self.as_raw()) }; } + #[inline] unsafe fn dec_ref(obj: NonNull) { // SAFETY: The safety requirements guarantee that the refcount is non-zero. unsafe { bindings::usb_put_intf(obj.cast().as_ptr()) } @@ -444,6 +435,7 @@ fn as_raw(&self) -> *mut bindings::usb_device { // SAFETY: Instances of `Device` are always reference-counted. unsafe impl AlwaysRefCounted for Device { + #[inline] fn inc_ref(&self) { // SAFETY: The invariants of `Device` guarantee that `self.as_raw()` // returns a valid `struct usb_device` pointer, for which we will @@ -451,6 +443,7 @@ fn inc_ref(&self) { unsafe { bindings::usb_get_dev(self.as_raw()) }; } + #[inline] unsafe fn dec_ref(obj: NonNull) { // SAFETY: The safety requirements guarantee that the refcount is non-zero. unsafe { bindings::usb_put_dev(obj.cast().as_ptr()) }