USB fixes for 4.3-rc3
Here are some USB driver fixes for 4.3-rc3. There's the usual assortment of new device ids, combined with xhci and gadget driver fixes. Full details in the shortlog. All of these have been in linux-next with no reported problems. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlYGzJ0ACgkQMUfUDdst+yneGACgxrj/ZJGgY09tkItvvejHoYrF tgQAoI/cRL7C9aoWt7DjMxmbFnQE8hC7 =jsj/ -----END PGP SIGNATURE----- Merge tag 'usb-4.3-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB fixes from Greg KH: "Here are some USB driver fixes for 4.3-rc3. There's the usual assortment of new device ids, combined with xhci and gadget driver fixes. Full details in the shortlog. All of these have been in linux-next with no reported problems" * tag 'usb-4.3-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (34 commits) MAINTAINERS: remove amd5536udc USB gadget driver maintainer USB: whiteheat: fix potential null-deref at probe xhci: init command timeout timer earlier to avoid deleting it uninitialized xhci: change xhci 1.0 only restrictions to support xhci 1.1 usb: xhci: exit early in xhci_setup_device() if we're halted or dying usb: xhci: stop everything on the first call to xhci_stop usb: xhci: Clear XHCI_STATE_DYING on start usb: xhci: lock mutex on xhci_stop xhci: Move xhci_pme_quirk() behind #ifdef CONFIG_PM xhci: give command abortion one more chance before killing xhci usb: Use the USB_SS_MULT() macro to get the burst multiplier. usb: dwc3: gadget: Fix BUG in RT config usb: musb: fix cppi channel teardown for isoch transfer usb: phy: isp1301: Export I2C module alias information usb: gadget: drop null test before destroy functions usb: gadget: dummy_hcd: in transfer(), return data sent, not limit usb: gadget: dummy_hcd: fix rescan logic for transfer usb: gadget: dummy_hcd: fix unneeded else-if condition usb: gadget: dummy_hcd: emulate sending zlp in packet logic usb: musb: dsps: fix polling in device-only mode ...
This commit is contained in:
Коммит
bcba282ab3
|
@ -6,6 +6,7 @@ Required properties:
|
|||
"lsi,zevio-usb"
|
||||
"qcom,ci-hdrc"
|
||||
"chipidea,usb2"
|
||||
"xlnx,zynq-usb-2.20a"
|
||||
- reg: base address and length of the registers
|
||||
- interrupts: interrupt for the USB controller
|
||||
|
||||
|
|
|
@ -615,9 +615,8 @@ F: Documentation/hwmon/fam15h_power
|
|||
F: drivers/hwmon/fam15h_power.c
|
||||
|
||||
AMD GEODE CS5536 USB DEVICE CONTROLLER DRIVER
|
||||
M: Thomas Dahlmann <dahlmann.thomas@arcor.de>
|
||||
L: linux-geode@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Supported
|
||||
S: Orphan
|
||||
F: drivers/usb/gadget/udc/amd5536udc.*
|
||||
|
||||
AMD GEODE PROCESSOR/CHIPSET SUPPORT
|
||||
|
|
|
@ -61,7 +61,7 @@ static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
|
|||
{ .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
|
||||
{ .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
|
||||
{ .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
|
||||
{ .compatible = "fsl,imx6sx-usb", .data = &imx6sl_usb_data},
|
||||
{ .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/chipidea.h>
|
||||
|
@ -30,18 +31,36 @@ static const struct ci_hdrc_platform_data ci_default_pdata = {
|
|||
.flags = CI_HDRC_DISABLE_STREAMING,
|
||||
};
|
||||
|
||||
static struct ci_hdrc_platform_data ci_zynq_pdata = {
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
};
|
||||
|
||||
static const struct of_device_id ci_hdrc_usb2_of_match[] = {
|
||||
{ .compatible = "chipidea,usb2"},
|
||||
{ .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match);
|
||||
|
||||
static int ci_hdrc_usb2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ci_hdrc_usb2_priv *priv;
|
||||
struct ci_hdrc_platform_data *ci_pdata = dev_get_platdata(dev);
|
||||
int ret;
|
||||
const struct of_device_id *match;
|
||||
|
||||
if (!ci_pdata) {
|
||||
ci_pdata = devm_kmalloc(dev, sizeof(*ci_pdata), GFP_KERNEL);
|
||||
*ci_pdata = ci_default_pdata; /* struct copy */
|
||||
}
|
||||
|
||||
match = of_match_device(ci_hdrc_usb2_of_match, &pdev->dev);
|
||||
if (match && match->data) {
|
||||
/* struct copy */
|
||||
*ci_pdata = *(struct ci_hdrc_platform_data *)match->data;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
@ -96,12 +115,6 @@ static int ci_hdrc_usb2_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ci_hdrc_usb2_of_match[] = {
|
||||
{ .compatible = "chipidea,usb2" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match);
|
||||
|
||||
static struct platform_driver ci_hdrc_usb2_driver = {
|
||||
.probe = ci_hdrc_usb2_probe,
|
||||
.remove = ci_hdrc_usb2_remove,
|
||||
|
|
|
@ -656,6 +656,44 @@ __acquires(hwep->lock)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int _ep_set_halt(struct usb_ep *ep, int value, bool check_transfer)
|
||||
{
|
||||
struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep);
|
||||
int direction, retval = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (ep == NULL || hwep->ep.desc == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(hwep->ep.desc))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
spin_lock_irqsave(hwep->lock, flags);
|
||||
|
||||
if (value && hwep->dir == TX && check_transfer &&
|
||||
!list_empty(&hwep->qh.queue) &&
|
||||
!usb_endpoint_xfer_control(hwep->ep.desc)) {
|
||||
spin_unlock_irqrestore(hwep->lock, flags);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
direction = hwep->dir;
|
||||
do {
|
||||
retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value);
|
||||
|
||||
if (!value)
|
||||
hwep->wedge = 0;
|
||||
|
||||
if (hwep->type == USB_ENDPOINT_XFER_CONTROL)
|
||||
hwep->dir = (hwep->dir == TX) ? RX : TX;
|
||||
|
||||
} while (hwep->dir != direction);
|
||||
|
||||
spin_unlock_irqrestore(hwep->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* _gadget_stop_activity: stops all USB activity, flushes & disables all endpts
|
||||
* @gadget: gadget
|
||||
|
@ -1051,7 +1089,7 @@ __acquires(ci->lock)
|
|||
num += ci->hw_ep_max / 2;
|
||||
|
||||
spin_unlock(&ci->lock);
|
||||
err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep);
|
||||
err = _ep_set_halt(&ci->ci_hw_ep[num].ep, 1, false);
|
||||
spin_lock(&ci->lock);
|
||||
if (!err)
|
||||
isr_setup_status_phase(ci);
|
||||
|
@ -1117,8 +1155,8 @@ delegate:
|
|||
|
||||
if (err < 0) {
|
||||
spin_unlock(&ci->lock);
|
||||
if (usb_ep_set_halt(&hwep->ep))
|
||||
dev_err(ci->dev, "error: ep_set_halt\n");
|
||||
if (_ep_set_halt(&hwep->ep, 1, false))
|
||||
dev_err(ci->dev, "error: _ep_set_halt\n");
|
||||
spin_lock(&ci->lock);
|
||||
}
|
||||
}
|
||||
|
@ -1149,9 +1187,9 @@ __acquires(ci->lock)
|
|||
err = isr_setup_status_phase(ci);
|
||||
if (err < 0) {
|
||||
spin_unlock(&ci->lock);
|
||||
if (usb_ep_set_halt(&hwep->ep))
|
||||
if (_ep_set_halt(&hwep->ep, 1, false))
|
||||
dev_err(ci->dev,
|
||||
"error: ep_set_halt\n");
|
||||
"error: _ep_set_halt\n");
|
||||
spin_lock(&ci->lock);
|
||||
}
|
||||
}
|
||||
|
@ -1397,41 +1435,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
|
|||
*/
|
||||
static int ep_set_halt(struct usb_ep *ep, int value)
|
||||
{
|
||||
struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep);
|
||||
int direction, retval = 0;
|
||||
unsigned long flags;
|
||||
|
||||
if (ep == NULL || hwep->ep.desc == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (usb_endpoint_xfer_isoc(hwep->ep.desc))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
spin_lock_irqsave(hwep->lock, flags);
|
||||
|
||||
#ifndef STALL_IN
|
||||
/* g_file_storage MS compliant but g_zero fails chapter 9 compliance */
|
||||
if (value && hwep->type == USB_ENDPOINT_XFER_BULK && hwep->dir == TX &&
|
||||
!list_empty(&hwep->qh.queue)) {
|
||||
spin_unlock_irqrestore(hwep->lock, flags);
|
||||
return -EAGAIN;
|
||||
}
|
||||
#endif
|
||||
|
||||
direction = hwep->dir;
|
||||
do {
|
||||
retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value);
|
||||
|
||||
if (!value)
|
||||
hwep->wedge = 0;
|
||||
|
||||
if (hwep->type == USB_ENDPOINT_XFER_CONTROL)
|
||||
hwep->dir = (hwep->dir == TX) ? RX : TX;
|
||||
|
||||
} while (hwep->dir != direction);
|
||||
|
||||
spin_unlock_irqrestore(hwep->lock, flags);
|
||||
return retval;
|
||||
return _ep_set_halt(ep, value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -112,7 +112,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
|||
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
||||
ep->ss_ep_comp.bmAttributes = 16;
|
||||
} else if (usb_endpoint_xfer_isoc(&ep->desc) &&
|
||||
desc->bmAttributes > 2) {
|
||||
USB_SS_MULT(desc->bmAttributes) > 3) {
|
||||
dev_warn(ddev, "Isoc endpoint has Mult of %d in "
|
||||
"config %d interface %d altsetting %d ep %d: "
|
||||
"setting to 3\n", desc->bmAttributes + 1,
|
||||
|
@ -121,7 +121,8 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
|||
}
|
||||
|
||||
if (usb_endpoint_xfer_isoc(&ep->desc))
|
||||
max_tx = (desc->bMaxBurst + 1) * (desc->bmAttributes + 1) *
|
||||
max_tx = (desc->bMaxBurst + 1) *
|
||||
(USB_SS_MULT(desc->bmAttributes)) *
|
||||
usb_endpoint_maxp(&ep->desc);
|
||||
else if (usb_endpoint_xfer_int(&ep->desc))
|
||||
max_tx = usb_endpoint_maxp(&ep->desc) *
|
||||
|
|
|
@ -514,8 +514,6 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
|||
goto err1;
|
||||
}
|
||||
|
||||
dwc3_omap_enable_irqs(omap);
|
||||
|
||||
ret = dwc3_omap_extcon_register(omap);
|
||||
if (ret < 0)
|
||||
goto err2;
|
||||
|
@ -526,6 +524,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
|||
goto err3;
|
||||
}
|
||||
|
||||
dwc3_omap_enable_irqs(omap);
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
|
|
|
@ -2665,8 +2665,6 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
|
|||
int i;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
|
||||
spin_lock(&dwc->lock);
|
||||
|
||||
for (i = 0; i < dwc->num_event_buffers; i++) {
|
||||
irqreturn_t status;
|
||||
|
||||
|
@ -2675,8 +2673,6 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc)
|
|||
ret = status;
|
||||
}
|
||||
|
||||
spin_unlock(&dwc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -186,6 +186,7 @@ void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
|
|||
|
||||
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
|
||||
ep->claimed = false;
|
||||
ep->driver_data = NULL;
|
||||
}
|
||||
gadget->in_epnum = 0;
|
||||
gadget->out_epnum = 0;
|
||||
|
|
|
@ -3138,8 +3138,8 @@ static void udc_pci_remove(struct pci_dev *pdev)
|
|||
writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
|
||||
if (dev->irq_registered)
|
||||
free_irq(pdev->irq, dev);
|
||||
if (dev->regs)
|
||||
iounmap(dev->regs);
|
||||
if (dev->virt_addr)
|
||||
iounmap(dev->virt_addr);
|
||||
if (dev->mem_region)
|
||||
release_mem_region(pci_resource_start(pdev, 0),
|
||||
pci_resource_len(pdev, 0));
|
||||
|
@ -3226,17 +3226,13 @@ static int udc_pci_probe(
|
|||
|
||||
/* init */
|
||||
dev = kzalloc(sizeof(struct udc), GFP_KERNEL);
|
||||
if (!dev) {
|
||||
retval = -ENOMEM;
|
||||
goto finished;
|
||||
}
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
/* pci setup */
|
||||
if (pci_enable_device(pdev) < 0) {
|
||||
kfree(dev);
|
||||
dev = NULL;
|
||||
retval = -ENODEV;
|
||||
goto finished;
|
||||
goto err_pcidev;
|
||||
}
|
||||
dev->active = 1;
|
||||
|
||||
|
@ -3246,28 +3242,22 @@ static int udc_pci_probe(
|
|||
|
||||
if (!request_mem_region(resource, len, name)) {
|
||||
dev_dbg(&pdev->dev, "pci device used already\n");
|
||||
kfree(dev);
|
||||
dev = NULL;
|
||||
retval = -EBUSY;
|
||||
goto finished;
|
||||
goto err_memreg;
|
||||
}
|
||||
dev->mem_region = 1;
|
||||
|
||||
dev->virt_addr = ioremap_nocache(resource, len);
|
||||
if (dev->virt_addr == NULL) {
|
||||
dev_dbg(&pdev->dev, "start address cannot be mapped\n");
|
||||
kfree(dev);
|
||||
dev = NULL;
|
||||
retval = -EFAULT;
|
||||
goto finished;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
if (!pdev->irq) {
|
||||
dev_err(&pdev->dev, "irq not set\n");
|
||||
kfree(dev);
|
||||
dev = NULL;
|
||||
retval = -ENODEV;
|
||||
goto finished;
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
spin_lock_init(&dev->lock);
|
||||
|
@ -3283,10 +3273,8 @@ static int udc_pci_probe(
|
|||
|
||||
if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) {
|
||||
dev_dbg(&pdev->dev, "request_irq(%d) fail\n", pdev->irq);
|
||||
kfree(dev);
|
||||
dev = NULL;
|
||||
retval = -EBUSY;
|
||||
goto finished;
|
||||
goto err_irq;
|
||||
}
|
||||
dev->irq_registered = 1;
|
||||
|
||||
|
@ -3314,8 +3302,17 @@ static int udc_pci_probe(
|
|||
return 0;
|
||||
|
||||
finished:
|
||||
if (dev)
|
||||
udc_pci_remove(pdev);
|
||||
udc_pci_remove(pdev);
|
||||
return retval;
|
||||
|
||||
err_irq:
|
||||
iounmap(dev->virt_addr);
|
||||
err_ioremap:
|
||||
release_mem_region(resource, len);
|
||||
err_memreg:
|
||||
pci_disable_device(pdev);
|
||||
err_pcidev:
|
||||
kfree(dev);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
@ -2002,6 +2002,17 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
|||
ep->udc = udc;
|
||||
INIT_LIST_HEAD(&ep->queue);
|
||||
|
||||
if (ep->index == 0) {
|
||||
ep->ep.caps.type_control = true;
|
||||
} else {
|
||||
ep->ep.caps.type_iso = ep->can_isoc;
|
||||
ep->ep.caps.type_bulk = true;
|
||||
ep->ep.caps.type_int = true;
|
||||
}
|
||||
|
||||
ep->ep.caps.dir_in = true;
|
||||
ep->ep.caps.dir_out = true;
|
||||
|
||||
if (i)
|
||||
list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
|
||||
|
||||
|
|
|
@ -324,8 +324,7 @@ static void bdc_mem_free(struct bdc *bdc)
|
|||
bdc->scratchpad.buff, bdc->scratchpad.sp_dma);
|
||||
|
||||
/* Destroy the dma pools */
|
||||
if (bdc->bd_table_pool)
|
||||
dma_pool_destroy(bdc->bd_table_pool);
|
||||
dma_pool_destroy(bdc->bd_table_pool);
|
||||
|
||||
/* Free the bdc_ep array */
|
||||
kfree(bdc->bdc_ep_array);
|
||||
|
|
|
@ -1348,6 +1348,7 @@ static int transfer(struct dummy_hcd *dum_hcd, struct urb *urb,
|
|||
{
|
||||
struct dummy *dum = dum_hcd->dum;
|
||||
struct dummy_request *req;
|
||||
int sent = 0;
|
||||
|
||||
top:
|
||||
/* if there's no request queued, the device is NAKing; return */
|
||||
|
@ -1385,12 +1386,15 @@ top:
|
|||
if (len == 0)
|
||||
break;
|
||||
|
||||
/* use an extra pass for the final short packet */
|
||||
if (len > ep->ep.maxpacket) {
|
||||
rescan = 1;
|
||||
len -= (len % ep->ep.maxpacket);
|
||||
/* send multiple of maxpacket first, then remainder */
|
||||
if (len >= ep->ep.maxpacket) {
|
||||
is_short = 0;
|
||||
if (len % ep->ep.maxpacket)
|
||||
rescan = 1;
|
||||
len -= len % ep->ep.maxpacket;
|
||||
} else {
|
||||
is_short = 1;
|
||||
}
|
||||
is_short = (len % ep->ep.maxpacket) != 0;
|
||||
|
||||
len = dummy_perform_transfer(urb, req, len);
|
||||
|
||||
|
@ -1399,6 +1403,7 @@ top:
|
|||
req->req.status = len;
|
||||
} else {
|
||||
limit -= len;
|
||||
sent += len;
|
||||
urb->actual_length += len;
|
||||
req->req.actual += len;
|
||||
}
|
||||
|
@ -1421,7 +1426,7 @@ top:
|
|||
*status = -EOVERFLOW;
|
||||
else
|
||||
*status = 0;
|
||||
} else if (!to_host) {
|
||||
} else {
|
||||
*status = 0;
|
||||
if (host_len > dev_len)
|
||||
req->req.status = -EOVERFLOW;
|
||||
|
@ -1429,15 +1434,24 @@ top:
|
|||
req->req.status = 0;
|
||||
}
|
||||
|
||||
/* many requests terminate without a short packet */
|
||||
/*
|
||||
* many requests terminate without a short packet.
|
||||
* send a zlp if demanded by flags.
|
||||
*/
|
||||
} else {
|
||||
if (req->req.length == req->req.actual
|
||||
&& !req->req.zero)
|
||||
req->req.status = 0;
|
||||
if (urb->transfer_buffer_length == urb->actual_length
|
||||
&& !(urb->transfer_flags
|
||||
& URB_ZERO_PACKET))
|
||||
*status = 0;
|
||||
if (req->req.length == req->req.actual) {
|
||||
if (req->req.zero && to_host)
|
||||
rescan = 1;
|
||||
else
|
||||
req->req.status = 0;
|
||||
}
|
||||
if (urb->transfer_buffer_length == urb->actual_length) {
|
||||
if (urb->transfer_flags & URB_ZERO_PACKET &&
|
||||
!to_host)
|
||||
rescan = 1;
|
||||
else
|
||||
*status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* device side completion --> continuable */
|
||||
|
@ -1460,7 +1474,7 @@ top:
|
|||
if (rescan)
|
||||
goto top;
|
||||
}
|
||||
return limit;
|
||||
return sent;
|
||||
}
|
||||
|
||||
static int periodic_bytes(struct dummy *dum, struct dummy_ep *ep)
|
||||
|
@ -1890,7 +1904,7 @@ restart:
|
|||
default:
|
||||
treat_control_like_bulk:
|
||||
ep->last_io = jiffies;
|
||||
total = transfer(dum_hcd, urb, ep, limit, &status);
|
||||
total -= transfer(dum_hcd, urb, ep, limit, &status);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -2117,8 +2117,7 @@ static int gr_remove(struct platform_device *pdev)
|
|||
return -EBUSY;
|
||||
|
||||
gr_dfs_delete(dev);
|
||||
if (dev->desc_pool)
|
||||
dma_pool_destroy(dev->desc_pool);
|
||||
dma_pool_destroy(dev->desc_pool);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
gr_free_request(&dev->epi[0].ep, &dev->ep0reqi->req);
|
||||
|
|
|
@ -1767,8 +1767,7 @@ static int mv_u3d_remove(struct platform_device *dev)
|
|||
usb_del_gadget_udc(&u3d->gadget);
|
||||
|
||||
/* free memory allocated in probe */
|
||||
if (u3d->trb_pool)
|
||||
dma_pool_destroy(u3d->trb_pool);
|
||||
dma_pool_destroy(u3d->trb_pool);
|
||||
|
||||
if (u3d->ep_context)
|
||||
dma_free_coherent(&dev->dev, u3d->ep_context_size,
|
||||
|
|
|
@ -2100,8 +2100,7 @@ static int mv_udc_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* free memory allocated in probe */
|
||||
if (udc->dtd_pool)
|
||||
dma_pool_destroy(udc->dtd_pool);
|
||||
dma_pool_destroy(udc->dtd_pool);
|
||||
|
||||
if (udc->ep_dqh)
|
||||
dma_free_coherent(&pdev->dev, udc->ep_dqh_size,
|
||||
|
|
|
@ -1498,10 +1498,10 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
|
|||
* use Event Data TRBs, and we don't chain in a link TRB on short
|
||||
* transfers, we're basically dividing by 1.
|
||||
*
|
||||
* xHCI 1.0 specification indicates that the Average TRB Length should
|
||||
* be set to 8 for control endpoints.
|
||||
* xHCI 1.0 and 1.1 specification indicates that the Average TRB Length
|
||||
* should be set to 8 for control endpoints.
|
||||
*/
|
||||
if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version == 0x100)
|
||||
if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version >= 0x100)
|
||||
ep_ctx->tx_info |= cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(8));
|
||||
else
|
||||
ep_ctx->tx_info |=
|
||||
|
@ -1792,8 +1792,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
|
|||
int size;
|
||||
int i, j, num_ports;
|
||||
|
||||
if (timer_pending(&xhci->cmd_timer))
|
||||
del_timer_sync(&xhci->cmd_timer);
|
||||
del_timer_sync(&xhci->cmd_timer);
|
||||
|
||||
/* Free the Event Ring Segment Table and the actual Event Ring */
|
||||
size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries);
|
||||
|
@ -2321,6 +2320,10 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
|
||||
INIT_LIST_HEAD(&xhci->cmd_list);
|
||||
|
||||
/* init command timeout timer */
|
||||
setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout,
|
||||
(unsigned long)xhci);
|
||||
|
||||
page_size = readl(&xhci->op_regs->page_size);
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"Supported page size register = 0x%x", page_size);
|
||||
|
@ -2505,10 +2508,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
|
|||
"Wrote ERST address to ir_set 0.");
|
||||
xhci_print_ir_set(xhci, 0);
|
||||
|
||||
/* init command timeout timer */
|
||||
setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout,
|
||||
(unsigned long)xhci);
|
||||
|
||||
/*
|
||||
* XXX: Might need to set the Interrupter Moderation Register to
|
||||
* something other than the default (~1ms minimum between interrupts).
|
||||
|
|
|
@ -180,51 +180,6 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
|||
"QUIRK: Resetting on resume");
|
||||
}
|
||||
|
||||
/*
|
||||
* In some Intel xHCI controllers, in order to get D3 working,
|
||||
* through a vendor specific SSIC CONFIG register at offset 0x883c,
|
||||
* SSIC PORT need to be marked as "unused" before putting xHCI
|
||||
* into D3. After D3 exit, the SSIC port need to be marked as "used".
|
||||
* Without this change, xHCI might not enter D3 state.
|
||||
* Make sure PME works on some Intel xHCI controllers by writing 1 to clear
|
||||
* the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4
|
||||
*/
|
||||
static void xhci_pme_quirk(struct usb_hcd *hcd, bool suspend)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
u32 val;
|
||||
void __iomem *reg;
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) {
|
||||
|
||||
reg = (void __iomem *) xhci->cap_regs + PORT2_SSIC_CONFIG_REG2;
|
||||
|
||||
/* Notify SSIC that SSIC profile programming is not done */
|
||||
val = readl(reg) & ~PROG_DONE;
|
||||
writel(val, reg);
|
||||
|
||||
/* Mark SSIC port as unused(suspend) or used(resume) */
|
||||
val = readl(reg);
|
||||
if (suspend)
|
||||
val |= SSIC_PORT_UNUSED;
|
||||
else
|
||||
val &= ~SSIC_PORT_UNUSED;
|
||||
writel(val, reg);
|
||||
|
||||
/* Notify SSIC that SSIC profile programming is done */
|
||||
val = readl(reg) | PROG_DONE;
|
||||
writel(val, reg);
|
||||
readl(reg);
|
||||
}
|
||||
|
||||
reg = (void __iomem *) xhci->cap_regs + 0x80a4;
|
||||
val = readl(reg);
|
||||
writel(val | BIT(28), reg);
|
||||
readl(reg);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev)
|
||||
{
|
||||
|
@ -345,6 +300,51 @@ static void xhci_pci_remove(struct pci_dev *dev)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/*
|
||||
* In some Intel xHCI controllers, in order to get D3 working,
|
||||
* through a vendor specific SSIC CONFIG register at offset 0x883c,
|
||||
* SSIC PORT need to be marked as "unused" before putting xHCI
|
||||
* into D3. After D3 exit, the SSIC port need to be marked as "used".
|
||||
* Without this change, xHCI might not enter D3 state.
|
||||
* Make sure PME works on some Intel xHCI controllers by writing 1 to clear
|
||||
* the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4
|
||||
*/
|
||||
static void xhci_pme_quirk(struct usb_hcd *hcd, bool suspend)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
|
||||
u32 val;
|
||||
void __iomem *reg;
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) {
|
||||
|
||||
reg = (void __iomem *) xhci->cap_regs + PORT2_SSIC_CONFIG_REG2;
|
||||
|
||||
/* Notify SSIC that SSIC profile programming is not done */
|
||||
val = readl(reg) & ~PROG_DONE;
|
||||
writel(val, reg);
|
||||
|
||||
/* Mark SSIC port as unused(suspend) or used(resume) */
|
||||
val = readl(reg);
|
||||
if (suspend)
|
||||
val |= SSIC_PORT_UNUSED;
|
||||
else
|
||||
val &= ~SSIC_PORT_UNUSED;
|
||||
writel(val, reg);
|
||||
|
||||
/* Notify SSIC that SSIC profile programming is done */
|
||||
val = readl(reg) | PROG_DONE;
|
||||
writel(val, reg);
|
||||
readl(reg);
|
||||
}
|
||||
|
||||
reg = (void __iomem *) xhci->cap_regs + 0x80a4;
|
||||
val = readl(reg);
|
||||
writel(val | BIT(28), reg);
|
||||
readl(reg);
|
||||
}
|
||||
|
||||
static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
|
|
|
@ -302,6 +302,15 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
|
|||
ret = xhci_handshake(&xhci->op_regs->cmd_ring,
|
||||
CMD_RING_RUNNING, 0, 5 * 1000 * 1000);
|
||||
if (ret < 0) {
|
||||
/* we are about to kill xhci, give it one more chance */
|
||||
xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
|
||||
&xhci->op_regs->cmd_ring);
|
||||
udelay(1000);
|
||||
ret = xhci_handshake(&xhci->op_regs->cmd_ring,
|
||||
CMD_RING_RUNNING, 0, 3 * 1000 * 1000);
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
|
||||
xhci_err(xhci, "Stopped the command ring failed, "
|
||||
"maybe the host is dead\n");
|
||||
xhci->xhc_state |= XHCI_STATE_DYING;
|
||||
|
@ -3461,8 +3470,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||
if (start_cycle == 0)
|
||||
field |= 0x1;
|
||||
|
||||
/* xHCI 1.0 6.4.1.2.1: Transfer Type field */
|
||||
if (xhci->hci_version == 0x100) {
|
||||
/* xHCI 1.0/1.1 6.4.1.2.1: Transfer Type field */
|
||||
if (xhci->hci_version >= 0x100) {
|
||||
if (urb->transfer_buffer_length > 0) {
|
||||
if (setup->bRequestType & USB_DIR_IN)
|
||||
field |= TRB_TX_TYPE(TRB_DATA_IN);
|
||||
|
|
|
@ -146,7 +146,8 @@ static int xhci_start(struct xhci_hcd *xhci)
|
|||
"waited %u microseconds.\n",
|
||||
XHCI_MAX_HALT_USEC);
|
||||
if (!ret)
|
||||
xhci->xhc_state &= ~XHCI_STATE_HALTED;
|
||||
xhci->xhc_state &= ~(XHCI_STATE_HALTED | XHCI_STATE_DYING);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -654,15 +655,6 @@ int xhci_run(struct usb_hcd *hcd)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(xhci_run);
|
||||
|
||||
static void xhci_only_stop_hcd(struct usb_hcd *hcd)
|
||||
{
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
|
||||
spin_lock_irq(&xhci->lock);
|
||||
xhci_halt(xhci);
|
||||
spin_unlock_irq(&xhci->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop xHCI driver.
|
||||
*
|
||||
|
@ -677,12 +669,14 @@ void xhci_stop(struct usb_hcd *hcd)
|
|||
u32 temp;
|
||||
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
|
||||
|
||||
if (!usb_hcd_is_primary_hcd(hcd)) {
|
||||
xhci_only_stop_hcd(xhci->shared_hcd);
|
||||
if (xhci->xhc_state & XHCI_STATE_HALTED)
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&xhci->mutex);
|
||||
spin_lock_irq(&xhci->lock);
|
||||
xhci->xhc_state |= XHCI_STATE_HALTED;
|
||||
xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
|
||||
|
||||
/* Make sure the xHC is halted for a USB3 roothub
|
||||
* (xhci_stop() could be called as part of failed init).
|
||||
*/
|
||||
|
@ -717,6 +711,7 @@ void xhci_stop(struct usb_hcd *hcd)
|
|||
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
|
||||
"xhci_stop completed - status = %x",
|
||||
readl(&xhci->op_regs->status));
|
||||
mutex_unlock(&xhci->mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3793,6 +3788,9 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
|
|||
|
||||
mutex_lock(&xhci->mutex);
|
||||
|
||||
if (xhci->xhc_state) /* dying or halted */
|
||||
goto out;
|
||||
|
||||
if (!udev->slot_id) {
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_address,
|
||||
"Bad Slot ID %d", udev->slot_id);
|
||||
|
|
|
@ -1051,6 +1051,7 @@ void musb_start(struct musb *musb)
|
|||
* (c) peripheral initiates, using SRP
|
||||
*/
|
||||
if (musb->port_mode != MUSB_PORT_MODE_HOST &&
|
||||
musb->xceiv->otg->state != OTG_STATE_A_WAIT_BCON &&
|
||||
(devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) {
|
||||
musb->is_active = 1;
|
||||
} else {
|
||||
|
@ -2448,6 +2449,9 @@ static int musb_suspend(struct device *dev)
|
|||
struct musb *musb = dev_to_musb(dev);
|
||||
unsigned long flags;
|
||||
|
||||
musb_platform_disable(musb);
|
||||
musb_generic_disable(musb);
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
|
||||
if (is_peripheral_active(musb)) {
|
||||
|
@ -2501,6 +2505,9 @@ static int musb_resume(struct device *dev)
|
|||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
musb_start(musb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -551,6 +551,9 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel)
|
|||
} else {
|
||||
cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREQ_NONE);
|
||||
|
||||
/* delay to drain to cppi dma pipeline for isoch */
|
||||
udelay(250);
|
||||
|
||||
csr = musb_readw(epio, MUSB_RXCSR);
|
||||
csr &= ~(MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_DMAENAB);
|
||||
musb_writew(epio, MUSB_RXCSR, csr);
|
||||
|
|
|
@ -225,8 +225,11 @@ static void dsps_musb_enable(struct musb *musb)
|
|||
|
||||
dsps_writel(reg_base, wrp->epintr_set, epmask);
|
||||
dsps_writel(reg_base, wrp->coreintr_set, coremask);
|
||||
/* start polling for ID change. */
|
||||
mod_timer(&glue->timer, jiffies + msecs_to_jiffies(wrp->poll_timeout));
|
||||
/* start polling for ID change in dual-role idle mode */
|
||||
if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
|
||||
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
|
||||
mod_timer(&glue->timer, jiffies +
|
||||
msecs_to_jiffies(wrp->poll_timeout));
|
||||
dsps_musb_try_idle(musb, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -379,6 +379,8 @@ static const struct of_device_id ux500_match[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, ux500_match);
|
||||
|
||||
static struct platform_driver ux500_driver = {
|
||||
.probe = ux500_probe,
|
||||
.remove = ux500_remove,
|
||||
|
|
|
@ -155,7 +155,7 @@ config USB_MSM_OTG
|
|||
config USB_QCOM_8X16_PHY
|
||||
tristate "Qualcomm APQ8016/MSM8916 on-chip USB PHY controller support"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
depends on RESET_CONTROLLER
|
||||
depends on RESET_CONTROLLER && EXTCON
|
||||
select USB_PHY
|
||||
select USB_ULPI_VIEWPORT
|
||||
help
|
||||
|
|
|
@ -232,7 +232,8 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
|
|||
clk_rate = pdata->clk_rate;
|
||||
needs_vcc = pdata->needs_vcc;
|
||||
if (gpio_is_valid(pdata->gpio_reset)) {
|
||||
err = devm_gpio_request_one(dev, pdata->gpio_reset, 0,
|
||||
err = devm_gpio_request_one(dev, pdata->gpio_reset,
|
||||
GPIOF_ACTIVE_LOW,
|
||||
dev_name(dev));
|
||||
if (!err)
|
||||
nop->gpiod_reset =
|
||||
|
|
|
@ -31,6 +31,7 @@ static const struct i2c_device_id isp1301_id[] = {
|
|||
{ "isp1301", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, isp1301_id);
|
||||
|
||||
static struct i2c_client *isp1301_i2c_client;
|
||||
|
||||
|
|
|
@ -278,6 +278,10 @@ static void option_instat_callback(struct urb *urb);
|
|||
#define ZTE_PRODUCT_MF622 0x0001
|
||||
#define ZTE_PRODUCT_MF628 0x0015
|
||||
#define ZTE_PRODUCT_MF626 0x0031
|
||||
#define ZTE_PRODUCT_ZM8620_X 0x0396
|
||||
#define ZTE_PRODUCT_ME3620_MBIM 0x0426
|
||||
#define ZTE_PRODUCT_ME3620_X 0x1432
|
||||
#define ZTE_PRODUCT_ME3620_L 0x1433
|
||||
#define ZTE_PRODUCT_AC2726 0xfff1
|
||||
#define ZTE_PRODUCT_MG880 0xfffd
|
||||
#define ZTE_PRODUCT_CDMA_TECH 0xfffe
|
||||
|
@ -544,6 +548,18 @@ static const struct option_blacklist_info zte_mc2716_z_blacklist = {
|
|||
.sendsetup = BIT(1) | BIT(2) | BIT(3),
|
||||
};
|
||||
|
||||
static const struct option_blacklist_info zte_me3620_mbim_blacklist = {
|
||||
.reserved = BIT(2) | BIT(3) | BIT(4),
|
||||
};
|
||||
|
||||
static const struct option_blacklist_info zte_me3620_xl_blacklist = {
|
||||
.reserved = BIT(3) | BIT(4) | BIT(5),
|
||||
};
|
||||
|
||||
static const struct option_blacklist_info zte_zm8620_x_blacklist = {
|
||||
.reserved = BIT(3) | BIT(4) | BIT(5),
|
||||
};
|
||||
|
||||
static const struct option_blacklist_info huawei_cdc12_blacklist = {
|
||||
.reserved = BIT(1) | BIT(2),
|
||||
};
|
||||
|
@ -1591,6 +1607,14 @@ static const struct usb_device_id option_ids[] = {
|
|||
.driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist },
|
||||
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff),
|
||||
.driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist },
|
||||
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_L),
|
||||
.driver_info = (kernel_ulong_t)&zte_me3620_xl_blacklist },
|
||||
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_MBIM),
|
||||
.driver_info = (kernel_ulong_t)&zte_me3620_mbim_blacklist },
|
||||
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_X),
|
||||
.driver_info = (kernel_ulong_t)&zte_me3620_xl_blacklist },
|
||||
{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ZM8620_X),
|
||||
.driver_info = (kernel_ulong_t)&zte_zm8620_x_blacklist },
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) },
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) },
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) },
|
||||
|
|
|
@ -80,6 +80,8 @@ static int whiteheat_firmware_download(struct usb_serial *serial,
|
|||
static int whiteheat_firmware_attach(struct usb_serial *serial);
|
||||
|
||||
/* function prototypes for the Connect Tech WhiteHEAT serial converter */
|
||||
static int whiteheat_probe(struct usb_serial *serial,
|
||||
const struct usb_device_id *id);
|
||||
static int whiteheat_attach(struct usb_serial *serial);
|
||||
static void whiteheat_release(struct usb_serial *serial);
|
||||
static int whiteheat_port_probe(struct usb_serial_port *port);
|
||||
|
@ -116,6 +118,7 @@ static struct usb_serial_driver whiteheat_device = {
|
|||
.description = "Connect Tech - WhiteHEAT",
|
||||
.id_table = id_table_std,
|
||||
.num_ports = 4,
|
||||
.probe = whiteheat_probe,
|
||||
.attach = whiteheat_attach,
|
||||
.release = whiteheat_release,
|
||||
.port_probe = whiteheat_port_probe,
|
||||
|
@ -217,6 +220,34 @@ static int whiteheat_firmware_attach(struct usb_serial *serial)
|
|||
/*****************************************************************************
|
||||
* Connect Tech's White Heat serial driver functions
|
||||
*****************************************************************************/
|
||||
|
||||
static int whiteheat_probe(struct usb_serial *serial,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_host_interface *iface_desc;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
size_t num_bulk_in = 0;
|
||||
size_t num_bulk_out = 0;
|
||||
size_t min_num_bulk;
|
||||
unsigned int i;
|
||||
|
||||
iface_desc = serial->interface->cur_altsetting;
|
||||
|
||||
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
|
||||
endpoint = &iface_desc->endpoint[i].desc;
|
||||
if (usb_endpoint_is_bulk_in(endpoint))
|
||||
++num_bulk_in;
|
||||
if (usb_endpoint_is_bulk_out(endpoint))
|
||||
++num_bulk_out;
|
||||
}
|
||||
|
||||
min_num_bulk = COMMAND_PORT + 1;
|
||||
if (num_bulk_in < min_num_bulk || num_bulk_out < min_num_bulk)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int whiteheat_attach(struct usb_serial *serial)
|
||||
{
|
||||
struct usb_serial_port *command_port;
|
||||
|
|
Загрузка…
Ссылка в новой задаче