Commit e988f306 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'fixes-for-v3.15-rc3' of...

Merge tag 'fixes-for-v3.15-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-linus

Felipe writes:

usb: fixes for v3.15-rc3

Quite a few fixes this time since I lost v3.15-rc2
window.

Most fixes are MUSB which learned to remove its debugfs directories
properly, got a fix for PHY handling and now knows that it should
make sure its clocks aren't gated before trying to access registers.

ffs got a race fix between ffs_epfile_io() and ffs_func_eps_disable().

dwc3 got a fix for system suspend/resume and now only iterates over
valid endpoints when trying to resize TX fifos.

usb_get_phy() now will properly return an error if try_module_get() fails.

We also have a revert for a NAPI conversion on the ethernet gadget which
was causing a kernel BUG.
Signed-of-by: default avatarFelipe Balbi <balbi@ti.com>
parents a798c10f a31a942a
...@@ -821,6 +821,7 @@ static void dwc3_complete(struct device *dev) ...@@ -821,6 +821,7 @@ static void dwc3_complete(struct device *dev)
spin_lock_irqsave(&dwc->lock, flags); spin_lock_irqsave(&dwc->lock, flags);
dwc3_event_buffers_setup(dwc);
switch (dwc->dr_mode) { switch (dwc->dr_mode) {
case USB_DR_MODE_PERIPHERAL: case USB_DR_MODE_PERIPHERAL:
case USB_DR_MODE_OTG: case USB_DR_MODE_OTG:
...@@ -828,7 +829,6 @@ static void dwc3_complete(struct device *dev) ...@@ -828,7 +829,6 @@ static void dwc3_complete(struct device *dev)
/* FALLTHROUGH */ /* FALLTHROUGH */
case USB_DR_MODE_HOST: case USB_DR_MODE_HOST:
default: default:
dwc3_event_buffers_setup(dwc);
break; break;
} }
......
...@@ -187,15 +187,12 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc) ...@@ -187,15 +187,12 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
* improve this algorithm so that we better use the internal * improve this algorithm so that we better use the internal
* FIFO space * FIFO space
*/ */
for (num = 0; num < DWC3_ENDPOINTS_NUM; num++) { for (num = 0; num < dwc->num_in_eps; num++) {
struct dwc3_ep *dep = dwc->eps[num]; /* bit0 indicates direction; 1 means IN ep */
int fifo_number = dep->number >> 1; struct dwc3_ep *dep = dwc->eps[(num << 1) | 1];
int mult = 1; int mult = 1;
int tmp; int tmp;
if (!(dep->number & 1))
continue;
if (!(dep->flags & DWC3_EP_ENABLED)) if (!(dep->flags & DWC3_EP_ENABLED))
continue; continue;
...@@ -224,8 +221,7 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc) ...@@ -224,8 +221,7 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
dev_vdbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n", dev_vdbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n",
dep->name, last_fifo_depth, fifo_size & 0xffff); dep->name, last_fifo_depth, fifo_size & 0xffff);
dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(fifo_number), dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
fifo_size);
last_fifo_depth += (fifo_size & 0xffff); last_fifo_depth += (fifo_size & 0xffff);
} }
......
...@@ -745,6 +745,12 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) ...@@ -745,6 +745,12 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
*/ */
struct usb_gadget *gadget = epfile->ffs->gadget; struct usb_gadget *gadget = epfile->ffs->gadget;
spin_lock_irq(&epfile->ffs->eps_lock);
/* In the meantime, endpoint got disabled or changed. */
if (epfile->ep != ep) {
spin_unlock_irq(&epfile->ffs->eps_lock);
return -ESHUTDOWN;
}
/* /*
* Controller may require buffer size to be aligned to * Controller may require buffer size to be aligned to
* maxpacketsize of an out endpoint. * maxpacketsize of an out endpoint.
...@@ -752,6 +758,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) ...@@ -752,6 +758,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
data_len = io_data->read ? data_len = io_data->read ?
usb_ep_align_maybe(gadget, ep->ep, io_data->len) : usb_ep_align_maybe(gadget, ep->ep, io_data->len) :
io_data->len; io_data->len;
spin_unlock_irq(&epfile->ffs->eps_lock);
data = kmalloc(data_len, GFP_KERNEL); data = kmalloc(data_len, GFP_KERNEL);
if (unlikely(!data)) if (unlikely(!data))
......
...@@ -377,7 +377,7 @@ static struct sk_buff *rndis_add_header(struct gether *port, ...@@ -377,7 +377,7 @@ static struct sk_buff *rndis_add_header(struct gether *port,
if (skb2) if (skb2)
rndis_add_hdr(skb2); rndis_add_hdr(skb2);
dev_kfree_skb_any(skb); dev_kfree_skb(skb);
return skb2; return skb2;
} }
......
...@@ -1219,6 +1219,10 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on) ...@@ -1219,6 +1219,10 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on)
struct fsl_udc *udc; struct fsl_udc *udc;
udc = container_of(gadget, struct fsl_udc, gadget); udc = container_of(gadget, struct fsl_udc, gadget);
if (!udc->vbus_active)
return -EOPNOTSUPP;
udc->softconnect = (is_on != 0); udc->softconnect = (is_on != 0);
if (can_pullup(udc)) if (can_pullup(udc))
fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
...@@ -2532,8 +2536,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) ...@@ -2532,8 +2536,8 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
if (!udc_controller) if (!udc_controller)
return -ENODEV; return -ENODEV;
usb_del_gadget_udc(&udc_controller->gadget);
udc_controller->done = &done; udc_controller->done = &done;
usb_del_gadget_udc(&udc_controller->gadget);
fsl_udc_clk_release(); fsl_udc_clk_release();
......
...@@ -2043,6 +2043,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent) ...@@ -2043,6 +2043,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
return -ESRCH; return -ESRCH;
/* fake probe to determine $CHIP */ /* fake probe to determine $CHIP */
CHIP = NULL;
usb_gadget_probe_driver(&probe_driver); usb_gadget_probe_driver(&probe_driver);
if (!CHIP) if (!CHIP)
return -ENODEV; return -ENODEV;
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include "u_rndis.h"
#undef VERBOSE_DEBUG #undef VERBOSE_DEBUG
......
...@@ -48,8 +48,6 @@ ...@@ -48,8 +48,6 @@
#define UETH__VERSION "29-May-2008" #define UETH__VERSION "29-May-2008"
#define GETHER_NAPI_WEIGHT 32
struct eth_dev { struct eth_dev {
/* lock is held while accessing port_usb /* lock is held while accessing port_usb
*/ */
...@@ -74,7 +72,6 @@ struct eth_dev { ...@@ -74,7 +72,6 @@ struct eth_dev {
struct sk_buff_head *list); struct sk_buff_head *list);
struct work_struct work; struct work_struct work;
struct napi_struct rx_napi;
unsigned long todo; unsigned long todo;
#define WORK_RX_MEMORY 0 #define WORK_RX_MEMORY 0
...@@ -256,16 +253,18 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags) ...@@ -256,16 +253,18 @@ rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
DBG(dev, "rx submit --> %d\n", retval); DBG(dev, "rx submit --> %d\n", retval);
if (skb) if (skb)
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
spin_lock_irqsave(&dev->req_lock, flags);
list_add(&req->list, &dev->rx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);
} }
return retval; return retval;
} }
static void rx_complete(struct usb_ep *ep, struct usb_request *req) static void rx_complete(struct usb_ep *ep, struct usb_request *req)
{ {
struct sk_buff *skb = req->context; struct sk_buff *skb = req->context, *skb2;
struct eth_dev *dev = ep->driver_data; struct eth_dev *dev = ep->driver_data;
int status = req->status; int status = req->status;
bool rx_queue = 0;
switch (status) { switch (status) {
...@@ -289,8 +288,30 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) ...@@ -289,8 +288,30 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
} else { } else {
skb_queue_tail(&dev->rx_frames, skb); skb_queue_tail(&dev->rx_frames, skb);
} }
if (!status) skb = NULL;
rx_queue = 1;
skb2 = skb_dequeue(&dev->rx_frames);
while (skb2) {
if (status < 0
|| ETH_HLEN > skb2->len
|| skb2->len > VLAN_ETH_FRAME_LEN) {
dev->net->stats.rx_errors++;
dev->net->stats.rx_length_errors++;
DBG(dev, "rx length %d\n", skb2->len);
dev_kfree_skb_any(skb2);
goto next_frame;
}
skb2->protocol = eth_type_trans(skb2, dev->net);
dev->net->stats.rx_packets++;
dev->net->stats.rx_bytes += skb2->len;
/* no buffer copies needed, unless hardware can't
* use skb buffers.
*/
status = netif_rx(skb2);
next_frame:
skb2 = skb_dequeue(&dev->rx_frames);
}
break; break;
/* software-driven interface shutdown */ /* software-driven interface shutdown */
...@@ -313,20 +334,22 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) ...@@ -313,20 +334,22 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
/* FALLTHROUGH */ /* FALLTHROUGH */
default: default:
rx_queue = 1;
dev_kfree_skb_any(skb);
dev->net->stats.rx_errors++; dev->net->stats.rx_errors++;
DBG(dev, "rx status %d\n", status); DBG(dev, "rx status %d\n", status);
break; break;
} }
if (skb)
dev_kfree_skb_any(skb);
if (!netif_running(dev->net)) {
clean: clean:
spin_lock(&dev->req_lock); spin_lock(&dev->req_lock);
list_add(&req->list, &dev->rx_reqs); list_add(&req->list, &dev->rx_reqs);
spin_unlock(&dev->req_lock); spin_unlock(&dev->req_lock);
req = NULL;
if (rx_queue && likely(napi_schedule_prep(&dev->rx_napi))) }
__napi_schedule(&dev->rx_napi); if (req)
rx_submit(dev, req, GFP_ATOMIC);
} }
static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n) static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
...@@ -391,24 +414,16 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) ...@@ -391,24 +414,16 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
{ {
struct usb_request *req; struct usb_request *req;
unsigned long flags; unsigned long flags;
int rx_counts = 0;
/* fill unused rxq slots with some skb */ /* fill unused rxq slots with some skb */
spin_lock_irqsave(&dev->req_lock, flags); spin_lock_irqsave(&dev->req_lock, flags);
while (!list_empty(&dev->rx_reqs)) { while (!list_empty(&dev->rx_reqs)) {
if (++rx_counts > qlen(dev->gadget, dev->qmult))
break;
req = container_of(dev->rx_reqs.next, req = container_of(dev->rx_reqs.next,
struct usb_request, list); struct usb_request, list);
list_del_init(&req->list); list_del_init(&req->list);
spin_unlock_irqrestore(&dev->req_lock, flags); spin_unlock_irqrestore(&dev->req_lock, flags);
if (rx_submit(dev, req, gfp_flags) < 0) { if (rx_submit(dev, req, gfp_flags) < 0) {
spin_lock_irqsave(&dev->req_lock, flags);
list_add(&req->list, &dev->rx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);
defer_kevent(dev, WORK_RX_MEMORY); defer_kevent(dev, WORK_RX_MEMORY);
return; return;
} }
...@@ -418,41 +433,6 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) ...@@ -418,41 +433,6 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
spin_unlock_irqrestore(&dev->req_lock, flags); spin_unlock_irqrestore(&dev->req_lock, flags);
} }
static int gether_poll(struct napi_struct *napi, int budget)
{
struct eth_dev *dev = container_of(napi, struct eth_dev, rx_napi);
struct sk_buff *skb;
unsigned int work_done = 0;
int status = 0;
while ((skb = skb_dequeue(&dev->rx_frames))) {
if (status < 0
|| ETH_HLEN > skb->len
|| skb->len > VLAN_ETH_FRAME_LEN) {
dev->net->stats.rx_errors++;
dev->net->stats.rx_length_errors++;
DBG(dev, "rx length %d\n", skb->len);
dev_kfree_skb_any(skb);
continue;
}
skb->protocol = eth_type_trans(skb, dev->net);
dev->net->stats.rx_packets++;
dev->net->stats.rx_bytes += skb->len;
status = netif_rx_ni(skb);
}
if (netif_running(dev->net)) {
rx_fill(dev, GFP_KERNEL);
work_done++;
}
if (work_done < budget)
napi_complete(&dev->rx_napi);
return work_done;
}
static void eth_work(struct work_struct *work) static void eth_work(struct work_struct *work)
{ {
struct eth_dev *dev = container_of(work, struct eth_dev, work); struct eth_dev *dev = container_of(work, struct eth_dev, work);
...@@ -645,7 +625,6 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags) ...@@ -645,7 +625,6 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
/* and open the tx floodgates */ /* and open the tx floodgates */
atomic_set(&dev->tx_qlen, 0); atomic_set(&dev->tx_qlen, 0);
netif_wake_queue(dev->net); netif_wake_queue(dev->net);
napi_enable(&dev->rx_napi);
} }
static int eth_open(struct net_device *net) static int eth_open(struct net_device *net)
...@@ -672,7 +651,6 @@ static int eth_stop(struct net_device *net) ...@@ -672,7 +651,6 @@ static int eth_stop(struct net_device *net)
unsigned long flags; unsigned long flags;
VDBG(dev, "%s\n", __func__); VDBG(dev, "%s\n", __func__);
napi_disable(&dev->rx_napi);
netif_stop_queue(net); netif_stop_queue(net);
DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
...@@ -790,7 +768,6 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, ...@@ -790,7 +768,6 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
dev = netdev_priv(net); dev = netdev_priv(net);
netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
spin_lock_init(&dev->lock); spin_lock_init(&dev->lock);
spin_lock_init(&dev->req_lock); spin_lock_init(&dev->req_lock);
INIT_WORK(&dev->work, eth_work); INIT_WORK(&dev->work, eth_work);
...@@ -853,7 +830,6 @@ struct net_device *gether_setup_name_default(const char *netname) ...@@ -853,7 +830,6 @@ struct net_device *gether_setup_name_default(const char *netname)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
dev = netdev_priv(net); dev = netdev_priv(net);
netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
spin_lock_init(&dev->lock); spin_lock_init(&dev->lock);
spin_lock_init(&dev->req_lock); spin_lock_init(&dev->req_lock);
INIT_WORK(&dev->work, eth_work); INIT_WORK(&dev->work, eth_work);
...@@ -1137,7 +1113,6 @@ void gether_disconnect(struct gether *link) ...@@ -1137,7 +1113,6 @@ void gether_disconnect(struct gether *link)
{ {
struct eth_dev *dev = link->ioport; struct eth_dev *dev = link->ioport;
struct usb_request *req; struct usb_request *req;
struct sk_buff *skb;
WARN_ON(!dev); WARN_ON(!dev);
if (!dev) if (!dev)
...@@ -1164,12 +1139,6 @@ void gether_disconnect(struct gether *link) ...@@ -1164,12 +1139,6 @@ void gether_disconnect(struct gether *link)
spin_lock(&dev->req_lock); spin_lock(&dev->req_lock);
} }
spin_unlock(&dev->req_lock); spin_unlock(&dev->req_lock);
spin_lock(&dev->rx_frames.lock);
while ((skb = __skb_dequeue(&dev->rx_frames)))
dev_kfree_skb_any(skb);
spin_unlock(&dev->rx_frames.lock);
link->in_ep->driver_data = NULL; link->in_ep->driver_data = NULL;
link->in_ep->desc = NULL; link->in_ep->desc = NULL;
......
...@@ -300,7 +300,7 @@ static int __init zero_bind(struct usb_composite_dev *cdev) ...@@ -300,7 +300,7 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
ss_opts->isoc_interval = gzero_options.isoc_interval; ss_opts->isoc_interval = gzero_options.isoc_interval;
ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket; ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket;
ss_opts->isoc_mult = gzero_options.isoc_mult; ss_opts->isoc_mult = gzero_options.isoc_mult;
ss_opts->isoc_maxburst = gzero_options.isoc_maxpacket; ss_opts->isoc_maxburst = gzero_options.isoc_maxburst;
ss_opts->bulk_buflen = gzero_options.bulk_buflen; ss_opts->bulk_buflen = gzero_options.bulk_buflen;
func_ss = usb_get_function(func_inst_ss); func_ss = usb_get_function(func_inst_ss);
......
...@@ -470,8 +470,9 @@ static int dsps_musb_exit(struct musb *musb) ...@@ -470,8 +470,9 @@ static int dsps_musb_exit(struct musb *musb)
struct dsps_glue *glue = dev_get_drvdata(dev->parent); struct dsps_glue *glue = dev_get_drvdata(dev->parent);
del_timer_sync(&glue->timer); del_timer_sync(&glue->timer);
usb_phy_shutdown(musb->xceiv); usb_phy_shutdown(musb->xceiv);
debugfs_remove_recursive(glue->dbgfs_root);
return 0; return 0;
} }
...@@ -708,8 +709,6 @@ static int dsps_remove(struct platform_device *pdev) ...@@ -708,8 +709,6 @@ static int dsps_remove(struct platform_device *pdev)
pm_runtime_put(&pdev->dev); pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
debugfs_remove_recursive(glue->dbgfs_root);
return 0; return 0;
} }
......
...@@ -316,7 +316,13 @@ static void omap_musb_mailbox_work(struct work_struct *mailbox_work) ...@@ -316,7 +316,13 @@ static void omap_musb_mailbox_work(struct work_struct *mailbox_work)
{ {
struct omap2430_glue *glue = container_of(mailbox_work, struct omap2430_glue *glue = container_of(mailbox_work,
struct omap2430_glue, omap_musb_mailbox_work); struct omap2430_glue, omap_musb_mailbox_work);
struct musb *musb = glue_to_musb(glue);
struct device *dev = musb->controller;
pm_runtime_get_sync(dev);
omap_musb_set_mailbox(glue); omap_musb_set_mailbox(glue);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
} }
static irqreturn_t omap2430_musb_interrupt(int irq, void *__hci) static irqreturn_t omap2430_musb_interrupt(int irq, void *__hci)
...@@ -416,6 +422,7 @@ static int omap2430_musb_init(struct musb *musb) ...@@ -416,6 +422,7 @@ static int omap2430_musb_init(struct musb *musb)
omap_musb_set_mailbox(glue); omap_musb_set_mailbox(glue);
phy_init(musb->phy); phy_init(musb->phy);
phy_power_on(musb->phy);
pm_runtime_put_noidle(musb->controller); pm_runtime_put_noidle(musb->controller);
return 0; return 0;
...@@ -478,6 +485,7 @@ static int omap2430_musb_exit(struct musb *musb) ...@@ -478,6 +485,7 @@ static int omap2430_musb_exit(struct musb *musb)
del_timer_sync(&musb_idle_timer); del_timer_sync(&musb_idle_timer);
omap2430_low_level_exit(musb); omap2430_low_level_exit(musb);
phy_power_off(musb->phy);
phy_exit(musb->phy); phy_exit(musb->phy);
return 0; return 0;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/delay.h>
#include "am35x-phy-control.h" #include "am35x-phy-control.h"
struct am335x_control_usb { struct am335x_control_usb {
...@@ -86,6 +87,14 @@ static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on) ...@@ -86,6 +87,14 @@ static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on)
} }
writel(val, usb_ctrl->phy_reg + reg); writel(val, usb_ctrl->phy_reg + reg);
/*
* Give the PHY ~1ms to complete the power up operation.
* Tests have shown unstable behaviour if other USB PHY related
* registers are written too shortly after such a transition.
*/
if (on)
mdelay(1);
} }
static const struct phy_control ctrl_am335x = { static const struct phy_control ctrl_am335x = {
......
...@@ -132,6 +132,9 @@ struct usb_phy *usb_get_phy(enum usb_phy_type type) ...@@ -132,6 +132,9 @@ struct usb_phy *usb_get_phy(enum usb_phy_type type)
if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) {
pr_debug("PHY: unable to find transceiver of type %s\n", pr_debug("PHY: unable to find transceiver of type %s\n",
usb_phy_type_string(type)); usb_phy_type_string(type));
if (!IS_ERR(phy))
phy = ERR_PTR(-ENODEV);
goto err0; goto err0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment