usb: fixes for v4.9-rc2
First set of fixes for v4.9-rc cycle. Nothing major this time around. The most important patches are the 3 fixes in dwc3 (dma_free_coheren() size fix, queued request accounting fix and Isochronous StartTransfer fix) and the 3 reverts on dwc2. We're also including the late atmel UDC fix which didn't make it into v4.8-final. -----BEGIN PGP SIGNATURE----- iQI6BAABCAAkBQJYB2ioHRxmZWxpcGUuYmFsYmlAbGludXguaW50ZWwuY29tAAoJ EMy+uJnhGpkGmM4QAIacUFUyPPVie+cZm7RlaYdS2mZeaZUp85fjleT73UVHlhHx rjIxAGv1fYvloLa9P6JbN8QgU37sY/0LDXsjZ9JKR9iXdaJL08nEcU7oHU1R9Adc Irv+SRQv6j0+GqWjBV+bebiAEkOgI0nx6mfI51NpvK7wyaxHignlOgLBa/XbN1SS NzOG+TRxI41IBdP3RdYRlo5i0Jlzmj8Zo5p3JCzwikHW/CoEfYgqGQLX9yip6aP9 E40zuQcON4UFv2N9JsML8Znzr1CHjGFveHOEfr9VjAWQIDzMD2LXFFoApAGFRaZ6 kOBDy4qwWnuYkdZzBHIPYc7G3mtCiA6coxeEvpyDlmqofxpLtw0ruZBDkvRFRIXY 3FX3yRp8NpKsq2bIwbBBCYPI/TYr7cYBch4NFpES4xXpXbMs4ONns/d7NsygktXO O7MqB4/DhLK88XiDSqZXrLDMl6X+2z1krqBrr71WI6/HUCQSPSutxBI9zhc4gJOC H+OiNaW4D9IYvPKeFJCi9BatLVyrbwyaLv7ssb1+lrP+mwr0H+YKe6WWgvCB8UMX lJn0Rz+1vCFQ8g5q9UfyZawJ9TvNStyows9nsLpqw7jcz0AB42DPp81B/NvHME1I m+8AKOAiYCNfHqxSa8C6aeoqOgYB2hvTWlnKu3YZr/NAmcCKX8YWH4oNK/tY =Kepq -----END PGP SIGNATURE----- Merge tag 'fixes-for-v4.9-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-linus Felipe writes: usb: fixes for v4.9-rc2 First set of fixes for v4.9-rc cycle. Nothing major this time around. The most important patches are the 3 fixes in dwc3 (dma_free_coheren() size fix, queued request accounting fix and Isochronous StartTransfer fix) and the 3 reverts on dwc2. We're also including the late atmel UDC fix which didn't make it into v4.8-final.
This commit is contained in:
commit
733cbe0698
|
@ -28,10 +28,7 @@ Refer to phy/phy-bindings.txt for generic phy consumer properties
|
||||||
- g-use-dma: enable dma usage in gadget driver.
|
- g-use-dma: enable dma usage in gadget driver.
|
||||||
- g-rx-fifo-size: size of rx fifo size in gadget mode.
|
- g-rx-fifo-size: size of rx fifo size in gadget mode.
|
||||||
- g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode.
|
- g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode.
|
||||||
|
- g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0) in gadget mode.
|
||||||
Deprecated properties:
|
|
||||||
- g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0)
|
|
||||||
in gadget mode.
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|
|
@ -463,9 +463,18 @@ static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
|
||||||
*/
|
*/
|
||||||
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
|
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
|
||||||
{
|
{
|
||||||
|
bool ret;
|
||||||
|
|
||||||
switch (hsotg->dr_mode) {
|
switch (hsotg->dr_mode) {
|
||||||
case USB_DR_MODE_HOST:
|
case USB_DR_MODE_HOST:
|
||||||
dwc2_force_mode(hsotg, true);
|
ret = dwc2_force_mode(hsotg, true);
|
||||||
|
/*
|
||||||
|
* NOTE: This is required for some rockchip soc based
|
||||||
|
* platforms on their host-only dwc2.
|
||||||
|
*/
|
||||||
|
if (!ret)
|
||||||
|
msleep(50);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case USB_DR_MODE_PERIPHERAL:
|
case USB_DR_MODE_PERIPHERAL:
|
||||||
dwc2_force_mode(hsotg, false);
|
dwc2_force_mode(hsotg, false);
|
||||||
|
|
|
@ -259,6 +259,13 @@ enum dwc2_lx_state {
|
||||||
DWC2_L3, /* Off state */
|
DWC2_L3, /* Off state */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gadget periodic tx fifo sizes as used by legacy driver
|
||||||
|
* EP0 is not included
|
||||||
|
*/
|
||||||
|
#define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \
|
||||||
|
768, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
|
||||||
/* Gadget ep0 states */
|
/* Gadget ep0 states */
|
||||||
enum dwc2_ep0_state {
|
enum dwc2_ep0_state {
|
||||||
DWC2_EP0_SETUP,
|
DWC2_EP0_SETUP,
|
||||||
|
|
|
@ -186,10 +186,9 @@ static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg,
|
||||||
*/
|
*/
|
||||||
static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
|
static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
|
||||||
{
|
{
|
||||||
unsigned int fifo;
|
unsigned int ep;
|
||||||
unsigned int addr;
|
unsigned int addr;
|
||||||
int timeout;
|
int timeout;
|
||||||
u32 dptxfsizn;
|
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
/* Reset fifo map if not correctly cleared during previous session */
|
/* Reset fifo map if not correctly cleared during previous session */
|
||||||
|
@ -217,16 +216,16 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
|
||||||
* them to endpoints dynamically according to maxpacket size value of
|
* them to endpoints dynamically according to maxpacket size value of
|
||||||
* given endpoint.
|
* given endpoint.
|
||||||
*/
|
*/
|
||||||
for (fifo = 1; fifo < MAX_EPS_CHANNELS; fifo++) {
|
for (ep = 1; ep < MAX_EPS_CHANNELS; ep++) {
|
||||||
dptxfsizn = dwc2_readl(hsotg->regs + DPTXFSIZN(fifo));
|
if (!hsotg->g_tx_fifo_sz[ep])
|
||||||
|
continue;
|
||||||
|
val = addr;
|
||||||
|
val |= hsotg->g_tx_fifo_sz[ep] << FIFOSIZE_DEPTH_SHIFT;
|
||||||
|
WARN_ONCE(addr + hsotg->g_tx_fifo_sz[ep] > hsotg->fifo_mem,
|
||||||
|
"insufficient fifo memory");
|
||||||
|
addr += hsotg->g_tx_fifo_sz[ep];
|
||||||
|
|
||||||
val = (dptxfsizn & FIFOSIZE_DEPTH_MASK) | addr;
|
dwc2_writel(val, hsotg->regs + DPTXFSIZN(ep));
|
||||||
addr += dptxfsizn >> FIFOSIZE_DEPTH_SHIFT;
|
|
||||||
|
|
||||||
if (addr > hsotg->fifo_mem)
|
|
||||||
break;
|
|
||||||
|
|
||||||
dwc2_writel(val, hsotg->regs + DPTXFSIZN(fifo));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3807,10 +3806,36 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg)
|
||||||
static void dwc2_hsotg_of_probe(struct dwc2_hsotg *hsotg)
|
static void dwc2_hsotg_of_probe(struct dwc2_hsotg *hsotg)
|
||||||
{
|
{
|
||||||
struct device_node *np = hsotg->dev->of_node;
|
struct device_node *np = hsotg->dev->of_node;
|
||||||
|
u32 len = 0;
|
||||||
|
u32 i = 0;
|
||||||
|
|
||||||
/* Enable dma if requested in device tree */
|
/* Enable dma if requested in device tree */
|
||||||
hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma");
|
hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register TX periodic fifo size per endpoint.
|
||||||
|
* EP0 is excluded since it has no fifo configuration.
|
||||||
|
*/
|
||||||
|
if (!of_find_property(np, "g-tx-fifo-size", &len))
|
||||||
|
goto rx_fifo;
|
||||||
|
|
||||||
|
len /= sizeof(u32);
|
||||||
|
|
||||||
|
/* Read tx fifo sizes other than ep0 */
|
||||||
|
if (of_property_read_u32_array(np, "g-tx-fifo-size",
|
||||||
|
&hsotg->g_tx_fifo_sz[1], len))
|
||||||
|
goto rx_fifo;
|
||||||
|
|
||||||
|
/* Add ep0 */
|
||||||
|
len++;
|
||||||
|
|
||||||
|
/* Make remaining TX fifos unavailable */
|
||||||
|
if (len < MAX_EPS_CHANNELS) {
|
||||||
|
for (i = len; i < MAX_EPS_CHANNELS; i++)
|
||||||
|
hsotg->g_tx_fifo_sz[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rx_fifo:
|
||||||
/* Register RX fifo size */
|
/* Register RX fifo size */
|
||||||
of_property_read_u32(np, "g-rx-fifo-size", &hsotg->g_rx_fifo_sz);
|
of_property_read_u32(np, "g-rx-fifo-size", &hsotg->g_rx_fifo_sz);
|
||||||
|
|
||||||
|
@ -3832,10 +3857,13 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
|
||||||
struct device *dev = hsotg->dev;
|
struct device *dev = hsotg->dev;
|
||||||
int epnum;
|
int epnum;
|
||||||
int ret;
|
int ret;
|
||||||
|
int i;
|
||||||
|
u32 p_tx_fifo[] = DWC2_G_P_LEGACY_TX_FIFO_SIZE;
|
||||||
|
|
||||||
/* Initialize to legacy fifo configuration values */
|
/* Initialize to legacy fifo configuration values */
|
||||||
hsotg->g_rx_fifo_sz = 2048;
|
hsotg->g_rx_fifo_sz = 2048;
|
||||||
hsotg->g_np_g_tx_fifo_sz = 1024;
|
hsotg->g_np_g_tx_fifo_sz = 1024;
|
||||||
|
memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo));
|
||||||
/* Device tree specific probe */
|
/* Device tree specific probe */
|
||||||
dwc2_hsotg_of_probe(hsotg);
|
dwc2_hsotg_of_probe(hsotg);
|
||||||
|
|
||||||
|
@ -3853,6 +3881,9 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
|
||||||
dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
|
dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
|
||||||
hsotg->g_np_g_tx_fifo_sz);
|
hsotg->g_np_g_tx_fifo_sz);
|
||||||
dev_dbg(dev, "RXFIFO size: %d\n", hsotg->g_rx_fifo_sz);
|
dev_dbg(dev, "RXFIFO size: %d\n", hsotg->g_rx_fifo_sz);
|
||||||
|
for (i = 0; i < MAX_EPS_CHANNELS; i++)
|
||||||
|
dev_dbg(dev, "Periodic TXFIFO%2d size: %d\n", i,
|
||||||
|
hsotg->g_tx_fifo_sz[i]);
|
||||||
|
|
||||||
hsotg->gadget.max_speed = USB_SPEED_HIGH;
|
hsotg->gadget.max_speed = USB_SPEED_HIGH;
|
||||||
hsotg->gadget.ops = &dwc2_hsotg_gadget_ops;
|
hsotg->gadget.ops = &dwc2_hsotg_gadget_ops;
|
||||||
|
|
|
@ -783,6 +783,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||||
req->trb = trb;
|
req->trb = trb;
|
||||||
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
|
req->trb_dma = dwc3_trb_dma_offset(dep, trb);
|
||||||
req->first_trb_index = dep->trb_enqueue;
|
req->first_trb_index = dep->trb_enqueue;
|
||||||
|
dep->queued_requests++;
|
||||||
}
|
}
|
||||||
|
|
||||||
dwc3_ep_inc_enq(dep);
|
dwc3_ep_inc_enq(dep);
|
||||||
|
@ -833,8 +834,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
|
||||||
|
|
||||||
trb->ctrl |= DWC3_TRB_CTRL_HWO;
|
trb->ctrl |= DWC3_TRB_CTRL_HWO;
|
||||||
|
|
||||||
dep->queued_requests++;
|
|
||||||
|
|
||||||
trace_dwc3_prepare_trb(dep, trb);
|
trace_dwc3_prepare_trb(dep, trb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1074,9 +1073,17 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
||||||
|
|
||||||
list_add_tail(&req->list, &dep->pending_list);
|
list_add_tail(&req->list, &dep->pending_list);
|
||||||
|
|
||||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
|
/*
|
||||||
dep->flags & DWC3_EP_PENDING_REQUEST) {
|
* NOTICE: Isochronous endpoints should NEVER be prestarted. We must
|
||||||
if (list_empty(&dep->started_list)) {
|
* wait for a XferNotReady event so we will know what's the current
|
||||||
|
* (micro-)frame number.
|
||||||
|
*
|
||||||
|
* Without this trick, we are very, very likely gonna get Bus Expiry
|
||||||
|
* errors which will force us issue EndTransfer command.
|
||||||
|
*/
|
||||||
|
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||||
|
if ((dep->flags & DWC3_EP_PENDING_REQUEST) &&
|
||||||
|
list_empty(&dep->started_list)) {
|
||||||
dwc3_stop_active_transfer(dwc, dep->number, true);
|
dwc3_stop_active_transfer(dwc, dep->number, true);
|
||||||
dep->flags = DWC3_EP_ENABLED;
|
dep->flags = DWC3_EP_ENABLED;
|
||||||
}
|
}
|
||||||
|
@ -1861,8 +1868,11 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep,
|
||||||
unsigned int s_pkt = 0;
|
unsigned int s_pkt = 0;
|
||||||
unsigned int trb_status;
|
unsigned int trb_status;
|
||||||
|
|
||||||
dep->queued_requests--;
|
|
||||||
dwc3_ep_inc_deq(dep);
|
dwc3_ep_inc_deq(dep);
|
||||||
|
|
||||||
|
if (req->trb == trb)
|
||||||
|
dep->queued_requests--;
|
||||||
|
|
||||||
trace_dwc3_complete_trb(dep, trb);
|
trace_dwc3_complete_trb(dep, trb);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2980,7 +2990,7 @@ err3:
|
||||||
kfree(dwc->setup_buf);
|
kfree(dwc->setup_buf);
|
||||||
|
|
||||||
err2:
|
err2:
|
||||||
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
|
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
|
||||||
dwc->ep0_trb, dwc->ep0_trb_addr);
|
dwc->ep0_trb, dwc->ep0_trb_addr);
|
||||||
|
|
||||||
err1:
|
err1:
|
||||||
|
@ -3005,7 +3015,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||||
kfree(dwc->setup_buf);
|
kfree(dwc->setup_buf);
|
||||||
kfree(dwc->zlp_buf);
|
kfree(dwc->zlp_buf);
|
||||||
|
|
||||||
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb),
|
dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb) * 2,
|
||||||
dwc->ep0_trb, dwc->ep0_trb_addr);
|
dwc->ep0_trb, dwc->ep0_trb_addr);
|
||||||
|
|
||||||
dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
|
dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req),
|
||||||
|
|
|
@ -136,8 +136,60 @@ struct ffs_epfile {
|
||||||
/*
|
/*
|
||||||
* Buffer for holding data from partial reads which may happen since
|
* Buffer for holding data from partial reads which may happen since
|
||||||
* we’re rounding user read requests to a multiple of a max packet size.
|
* we’re rounding user read requests to a multiple of a max packet size.
|
||||||
|
*
|
||||||
|
* The pointer is initialised with NULL value and may be set by
|
||||||
|
* __ffs_epfile_read_data function to point to a temporary buffer.
|
||||||
|
*
|
||||||
|
* In normal operation, calls to __ffs_epfile_read_buffered will consume
|
||||||
|
* data from said buffer and eventually free it. Importantly, while the
|
||||||
|
* function is using the buffer, it sets the pointer to NULL. This is
|
||||||
|
* all right since __ffs_epfile_read_data and __ffs_epfile_read_buffered
|
||||||
|
* can never run concurrently (they are synchronised by epfile->mutex)
|
||||||
|
* so the latter will not assign a new value to the pointer.
|
||||||
|
*
|
||||||
|
* Meanwhile ffs_func_eps_disable frees the buffer (if the pointer is
|
||||||
|
* valid) and sets the pointer to READ_BUFFER_DROP value. This special
|
||||||
|
* value is crux of the synchronisation between ffs_func_eps_disable and
|
||||||
|
* __ffs_epfile_read_data.
|
||||||
|
*
|
||||||
|
* Once __ffs_epfile_read_data is about to finish it will try to set the
|
||||||
|
* pointer back to its old value (as described above), but seeing as the
|
||||||
|
* pointer is not-NULL (namely READ_BUFFER_DROP) it will instead free
|
||||||
|
* the buffer.
|
||||||
|
*
|
||||||
|
* == State transitions ==
|
||||||
|
*
|
||||||
|
* • ptr == NULL: (initial state)
|
||||||
|
* ◦ __ffs_epfile_read_buffer_free: go to ptr == DROP
|
||||||
|
* ◦ __ffs_epfile_read_buffered: nop
|
||||||
|
* ◦ __ffs_epfile_read_data allocates temp buffer: go to ptr == buf
|
||||||
|
* ◦ reading finishes: n/a, not in ‘and reading’ state
|
||||||
|
* • ptr == DROP:
|
||||||
|
* ◦ __ffs_epfile_read_buffer_free: nop
|
||||||
|
* ◦ __ffs_epfile_read_buffered: go to ptr == NULL
|
||||||
|
* ◦ __ffs_epfile_read_data allocates temp buffer: free buf, nop
|
||||||
|
* ◦ reading finishes: n/a, not in ‘and reading’ state
|
||||||
|
* • ptr == buf:
|
||||||
|
* ◦ __ffs_epfile_read_buffer_free: free buf, go to ptr == DROP
|
||||||
|
* ◦ __ffs_epfile_read_buffered: go to ptr == NULL and reading
|
||||||
|
* ◦ __ffs_epfile_read_data: n/a, __ffs_epfile_read_buffered
|
||||||
|
* is always called first
|
||||||
|
* ◦ reading finishes: n/a, not in ‘and reading’ state
|
||||||
|
* • ptr == NULL and reading:
|
||||||
|
* ◦ __ffs_epfile_read_buffer_free: go to ptr == DROP and reading
|
||||||
|
* ◦ __ffs_epfile_read_buffered: n/a, mutex is held
|
||||||
|
* ◦ __ffs_epfile_read_data: n/a, mutex is held
|
||||||
|
* ◦ reading finishes and …
|
||||||
|
* … all data read: free buf, go to ptr == NULL
|
||||||
|
* … otherwise: go to ptr == buf and reading
|
||||||
|
* • ptr == DROP and reading:
|
||||||
|
* ◦ __ffs_epfile_read_buffer_free: nop
|
||||||
|
* ◦ __ffs_epfile_read_buffered: n/a, mutex is held
|
||||||
|
* ◦ __ffs_epfile_read_data: n/a, mutex is held
|
||||||
|
* ◦ reading finishes: free buf, go to ptr == DROP
|
||||||
*/
|
*/
|
||||||
struct ffs_buffer *read_buffer; /* P: epfile->mutex */
|
struct ffs_buffer *read_buffer;
|
||||||
|
#define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN))
|
||||||
|
|
||||||
char name[5];
|
char name[5];
|
||||||
|
|
||||||
|
@ -736,25 +788,47 @@ static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
|
||||||
schedule_work(&io_data->work);
|
schedule_work(&io_data->work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __ffs_epfile_read_buffer_free(struct ffs_epfile *epfile)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* See comment in struct ffs_epfile for full read_buffer pointer
|
||||||
|
* synchronisation story.
|
||||||
|
*/
|
||||||
|
struct ffs_buffer *buf = xchg(&epfile->read_buffer, READ_BUFFER_DROP);
|
||||||
|
if (buf && buf != READ_BUFFER_DROP)
|
||||||
|
kfree(buf);
|
||||||
|
}
|
||||||
|
|
||||||
/* Assumes epfile->mutex is held. */
|
/* Assumes epfile->mutex is held. */
|
||||||
static ssize_t __ffs_epfile_read_buffered(struct ffs_epfile *epfile,
|
static ssize_t __ffs_epfile_read_buffered(struct ffs_epfile *epfile,
|
||||||
struct iov_iter *iter)
|
struct iov_iter *iter)
|
||||||
{
|
{
|
||||||
struct ffs_buffer *buf = epfile->read_buffer;
|
/*
|
||||||
|
* Null out epfile->read_buffer so ffs_func_eps_disable does not free
|
||||||
|
* the buffer while we are using it. See comment in struct ffs_epfile
|
||||||
|
* for full read_buffer pointer synchronisation story.
|
||||||
|
*/
|
||||||
|
struct ffs_buffer *buf = xchg(&epfile->read_buffer, NULL);
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
if (!buf)
|
if (!buf || buf == READ_BUFFER_DROP)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = copy_to_iter(buf->data, buf->length, iter);
|
ret = copy_to_iter(buf->data, buf->length, iter);
|
||||||
if (buf->length == ret) {
|
if (buf->length == ret) {
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
epfile->read_buffer = NULL;
|
return ret;
|
||||||
} else if (unlikely(iov_iter_count(iter))) {
|
}
|
||||||
|
|
||||||
|
if (unlikely(iov_iter_count(iter))) {
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
} else {
|
} else {
|
||||||
buf->length -= ret;
|
buf->length -= ret;
|
||||||
buf->data += ret;
|
buf->data += ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cmpxchg(&epfile->read_buffer, NULL, buf))
|
||||||
|
kfree(buf);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -783,7 +857,15 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
|
||||||
buf->length = data_len;
|
buf->length = data_len;
|
||||||
buf->data = buf->storage;
|
buf->data = buf->storage;
|
||||||
memcpy(buf->storage, data + ret, data_len);
|
memcpy(buf->storage, data + ret, data_len);
|
||||||
epfile->read_buffer = buf;
|
|
||||||
|
/*
|
||||||
|
* At this point read_buffer is NULL or READ_BUFFER_DROP (if
|
||||||
|
* ffs_func_eps_disable has been called in the meanwhile). See comment
|
||||||
|
* in struct ffs_epfile for full read_buffer pointer synchronisation
|
||||||
|
* story.
|
||||||
|
*/
|
||||||
|
if (unlikely(cmpxchg(&epfile->read_buffer, NULL, buf)))
|
||||||
|
kfree(buf);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1097,8 +1179,7 @@ ffs_epfile_release(struct inode *inode, struct file *file)
|
||||||
|
|
||||||
ENTER();
|
ENTER();
|
||||||
|
|
||||||
kfree(epfile->read_buffer);
|
__ffs_epfile_read_buffer_free(epfile);
|
||||||
epfile->read_buffer = NULL;
|
|
||||||
ffs_data_closed(epfile->ffs);
|
ffs_data_closed(epfile->ffs);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1724,24 +1805,20 @@ static void ffs_func_eps_disable(struct ffs_function *func)
|
||||||
unsigned count = func->ffs->eps_count;
|
unsigned count = func->ffs->eps_count;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&func->ffs->eps_lock, flags);
|
||||||
do {
|
do {
|
||||||
if (epfile)
|
|
||||||
mutex_lock(&epfile->mutex);
|
|
||||||
spin_lock_irqsave(&func->ffs->eps_lock, flags);
|
|
||||||
/* pending requests get nuked */
|
/* pending requests get nuked */
|
||||||
if (likely(ep->ep))
|
if (likely(ep->ep))
|
||||||
usb_ep_disable(ep->ep);
|
usb_ep_disable(ep->ep);
|
||||||
++ep;
|
++ep;
|
||||||
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
|
|
||||||
|
|
||||||
if (epfile) {
|
if (epfile) {
|
||||||
epfile->ep = NULL;
|
epfile->ep = NULL;
|
||||||
kfree(epfile->read_buffer);
|
__ffs_epfile_read_buffer_free(epfile);
|
||||||
epfile->read_buffer = NULL;
|
|
||||||
mutex_unlock(&epfile->mutex);
|
|
||||||
++epfile;
|
++epfile;
|
||||||
}
|
}
|
||||||
} while (--count);
|
} while (--count);
|
||||||
|
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ffs_func_eps_enable(struct ffs_function *func)
|
static int ffs_func_eps_enable(struct ffs_function *func)
|
||||||
|
|
|
@ -590,8 +590,9 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||||
|
|
||||||
/* throttle high/super speed IRQ rate back slightly */
|
/* throttle high/super speed IRQ rate back slightly */
|
||||||
if (gadget_is_dualspeed(dev->gadget))
|
if (gadget_is_dualspeed(dev->gadget))
|
||||||
req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
|
req->no_interrupt = (((dev->gadget->speed == USB_SPEED_HIGH ||
|
||||||
dev->gadget->speed == USB_SPEED_SUPER)
|
dev->gadget->speed == USB_SPEED_SUPER)) &&
|
||||||
|
!list_empty(&dev->tx_reqs))
|
||||||
? ((atomic_read(&dev->tx_qlen) % dev->qmult) != 0)
|
? ((atomic_read(&dev->tx_qlen) % dev->qmult) != 0)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
|
|
|
@ -1978,7 +1978,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
|
||||||
dev_err(&pdev->dev, "of_probe: name error(%d)\n", ret);
|
dev_err(&pdev->dev, "of_probe: name error(%d)\n", ret);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
ep->ep.name = name;
|
ep->ep.name = kasprintf(GFP_KERNEL, "ep%d", ep->index);
|
||||||
|
|
||||||
ep->ep_regs = udc->regs + USBA_EPT_BASE(i);
|
ep->ep_regs = udc->regs + USBA_EPT_BASE(i);
|
||||||
ep->dma_regs = udc->regs + USBA_DMA_BASE(i);
|
ep->dma_regs = udc->regs + USBA_DMA_BASE(i);
|
||||||
|
|
Loading…
Reference in New Issue