Commit cc28983c authored by Christian Gromm's avatar Christian Gromm Committed by Greg Kroah-Hartman

staging: most: hdm-usb: fix clear halt processing

This patch is needed to ensure that submitted URBs get unlinked before
the driver calls usb_clear_halt(). Since the halt condition of an USB
endpoint is channel related, the work_struct is moved from a buffer
basis to a channel basis.
Signed-off-by: default avatarChristian Gromm <christian.gromm@microchip.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9ed745ff
...@@ -65,19 +65,15 @@ ...@@ -65,19 +65,15 @@
/** /**
* struct buf_anchor - used to create a list of pending URBs * struct buf_anchor - used to create a list of pending URBs
* @urb: pointer to USB request block * @urb: pointer to USB request block
* @clear_work_obj:
* @list: linked list * @list: linked list
* @urb_completion: * @urb_completion:
*/ */
struct buf_anchor { struct buf_anchor {
struct urb *urb; struct urb *urb;
struct work_struct clear_work_obj;
struct list_head list; struct list_head list;
struct completion urb_compl; struct completion urb_compl;
}; };
#define to_buf_anchor(w) container_of(w, struct buf_anchor, clear_work_obj)
/** /**
* struct most_dci_obj - Direct Communication Interface * struct most_dci_obj - Direct Communication Interface
* @kobj:position in sysfs * @kobj:position in sysfs
...@@ -90,6 +86,17 @@ struct most_dci_obj { ...@@ -90,6 +86,17 @@ struct most_dci_obj {
#define to_dci_obj(p) container_of(p, struct most_dci_obj, kobj) #define to_dci_obj(p) container_of(p, struct most_dci_obj, kobj)
struct most_dev;
struct clear_hold_work {
struct work_struct ws;
struct most_dev *mdev;
unsigned int channel;
int pipe;
};
#define to_clear_hold_work(w) container_of(w, struct clear_hold_work, ws)
/** /**
* struct most_dev - holds all usb interface specific stuff * struct most_dev - holds all usb interface specific stuff
* @parent: parent object in sysfs * @parent: parent object in sysfs
...@@ -126,6 +133,7 @@ struct most_dev { ...@@ -126,6 +133,7 @@ struct most_dev {
spinlock_t anchor_list_lock[MAX_NUM_ENDPOINTS]; spinlock_t anchor_list_lock[MAX_NUM_ENDPOINTS];
bool padding_active[MAX_NUM_ENDPOINTS]; bool padding_active[MAX_NUM_ENDPOINTS];
bool is_channel_healthy[MAX_NUM_ENDPOINTS]; bool is_channel_healthy[MAX_NUM_ENDPOINTS];
struct clear_hold_work clear_work[MAX_NUM_ENDPOINTS];
struct list_head *anchor_list; struct list_head *anchor_list;
struct mutex io_mutex; struct mutex io_mutex;
struct timer_list link_stat_timer; struct timer_list link_stat_timer;
...@@ -222,7 +230,6 @@ static void free_anchored_buffers(struct most_dev *mdev, unsigned int channel, ...@@ -222,7 +230,6 @@ static void free_anchored_buffers(struct most_dev *mdev, unsigned int channel,
} }
spin_lock_irqsave(&mdev->anchor_list_lock[channel], flags); spin_lock_irqsave(&mdev->anchor_list_lock[channel], flags);
list_del(&anchor->list); list_del(&anchor->list);
cancel_work_sync(&anchor->clear_work_obj);
kfree(anchor); kfree(anchor);
} }
spin_unlock_irqrestore(&mdev->anchor_list_lock[channel], flags); spin_unlock_irqrestore(&mdev->anchor_list_lock[channel], flags);
...@@ -289,6 +296,8 @@ static int hdm_poison_channel(struct most_interface *iface, int channel) ...@@ -289,6 +296,8 @@ static int hdm_poison_channel(struct most_interface *iface, int channel)
mdev->is_channel_healthy[channel] = false; mdev->is_channel_healthy[channel] = false;
cancel_work_sync(&mdev->clear_work[channel].ws);
mutex_lock(&mdev->io_mutex); mutex_lock(&mdev->io_mutex);
free_anchored_buffers(mdev, channel, MBO_E_CLOSE); free_anchored_buffers(mdev, channel, MBO_E_CLOSE);
if (mdev->padding_active[channel]) if (mdev->padding_active[channel])
...@@ -409,9 +418,8 @@ static void hdm_write_completion(struct urb *urb) ...@@ -409,9 +418,8 @@ static void hdm_write_completion(struct urb *urb)
dev_warn(dev, "Broken OUT pipe detected\n"); dev_warn(dev, "Broken OUT pipe detected\n");
most_stop_enqueue(&mdev->iface, channel); most_stop_enqueue(&mdev->iface, channel);
mbo->status = MBO_E_INVAL; mbo->status = MBO_E_INVAL;
usb_unlink_urb(urb); mdev->clear_work[channel].pipe = urb->pipe;
INIT_WORK(&anchor->clear_work_obj, wq_clear_halt); schedule_work(&mdev->clear_work[channel].ws);
schedule_work(&anchor->clear_work_obj);
return; return;
case -ENODEV: case -ENODEV:
case -EPROTO: case -EPROTO:
...@@ -573,9 +581,8 @@ static void hdm_read_completion(struct urb *urb) ...@@ -573,9 +581,8 @@ static void hdm_read_completion(struct urb *urb)
case -EPIPE: case -EPIPE:
dev_warn(dev, "Broken IN pipe detected\n"); dev_warn(dev, "Broken IN pipe detected\n");
mbo->status = MBO_E_INVAL; mbo->status = MBO_E_INVAL;
usb_unlink_urb(urb); mdev->clear_work[channel].pipe = urb->pipe;
INIT_WORK(&anchor->clear_work_obj, wq_clear_halt); schedule_work(&mdev->clear_work[channel].ws);
schedule_work(&anchor->clear_work_obj);
return; return;
case -ENODEV: case -ENODEV:
case -EPROTO: case -EPROTO:
...@@ -735,6 +742,9 @@ static int hdm_configure_channel(struct most_interface *iface, int channel, ...@@ -735,6 +742,9 @@ static int hdm_configure_channel(struct most_interface *iface, int channel,
mdev = to_mdev(iface); mdev = to_mdev(iface);
mdev->is_channel_healthy[channel] = true; mdev->is_channel_healthy[channel] = true;
mdev->clear_work[channel].channel = channel;
mdev->clear_work[channel].mdev = mdev;
INIT_WORK(&mdev->clear_work[channel].ws, wq_clear_halt);
dev = &mdev->usb_device->dev; dev = &mdev->usb_device->dev;
if (unlikely(!iface || !conf)) { if (unlikely(!iface || !conf)) {
...@@ -916,33 +926,19 @@ static void wq_netinfo(struct work_struct *wq_obj) ...@@ -916,33 +926,19 @@ static void wq_netinfo(struct work_struct *wq_obj)
*/ */
static void wq_clear_halt(struct work_struct *wq_obj) static void wq_clear_halt(struct work_struct *wq_obj)
{ {
struct buf_anchor *anchor; struct clear_hold_work *clear_work = to_clear_hold_work(wq_obj);
struct most_dev *mdev; struct most_dev *mdev = clear_work->mdev;
struct mbo *mbo; unsigned int channel = clear_work->channel;
struct urb *urb; int pipe = clear_work->pipe;
unsigned int channel;
unsigned long flags;
anchor = to_buf_anchor(wq_obj); mutex_lock(&mdev->io_mutex);
urb = anchor->urb; free_anchored_buffers(mdev, channel, MBO_E_INVAL);
mbo = urb->context; if (usb_clear_halt(mdev->usb_device, pipe))
mdev = to_mdev(mbo->ifp);
channel = mbo->hdm_channel_id;
if (usb_clear_halt(urb->dev, urb->pipe))
dev_warn(&mdev->usb_device->dev, "Failed to reset endpoint.\n"); dev_warn(&mdev->usb_device->dev, "Failed to reset endpoint.\n");
usb_free_urb(urb);
spin_lock_irqsave(&mdev->anchor_list_lock[channel], flags);
list_del(&anchor->list);
spin_unlock_irqrestore(&mdev->anchor_list_lock[channel], flags);
if (likely(mbo->complete))
mbo->complete(mbo);
if (mdev->conf[channel].direction & MOST_CH_TX) if (mdev->conf[channel].direction & MOST_CH_TX)
most_resume_enqueue(&mdev->iface, channel); most_resume_enqueue(&mdev->iface, channel);
mutex_unlock(&mdev->io_mutex);
kfree(anchor);
} }
/** /**
......
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