diff options
author | David Vrabel <david.vrabel@csr.com> | 2009-06-24 13:26:40 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-23 09:46:21 -0400 |
commit | 831baa4915de465357b25c471bbb9b36472024df (patch) | |
tree | b28e8365e77defb84ba1437993b64be753274ead /drivers/usb | |
parent | 586dfc8cafc25cf785332fdfe9530f392e26f30d (diff) |
USB: whci-hcd: make endpoint_reset method async
usb_hcd_endpoint_reset() may be called in atomic context and must not
sleep. So make whci-hcd's endpoint_reset() asynchronous. URBs
submitted while the reset is in progress will be queued (on the std
list) and transfers will resume once the reset is complete.
Signed-off-by: David Vrabel <david.vrabel@csr.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/whci/asl.c | 12 | ||||
-rw-r--r-- | drivers/usb/host/whci/hcd.c | 8 | ||||
-rw-r--r-- | drivers/usb/host/whci/pzl.c | 12 | ||||
-rw-r--r-- | drivers/usb/host/whci/qset.c | 4 | ||||
-rw-r--r-- | drivers/usb/host/whci/whci-hc.h | 1 |
5 files changed, 31 insertions, 6 deletions
diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c index c2050785a819..c632437c7649 100644 --- a/drivers/usb/host/whci/asl.c +++ b/drivers/usb/host/whci/asl.c | |||
@@ -227,11 +227,21 @@ void scan_async_work(struct work_struct *work) | |||
227 | /* | 227 | /* |
228 | * Now that the ASL is updated, complete the removal of any | 228 | * Now that the ASL is updated, complete the removal of any |
229 | * removed qsets. | 229 | * removed qsets. |
230 | * | ||
231 | * If the qset was to be reset, do so and reinsert it into the | ||
232 | * ASL if it has pending transfers. | ||
230 | */ | 233 | */ |
231 | spin_lock_irq(&whc->lock); | 234 | spin_lock_irq(&whc->lock); |
232 | 235 | ||
233 | list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) { | 236 | list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) { |
234 | qset_remove_complete(whc, qset); | 237 | qset_remove_complete(whc, qset); |
238 | if (qset->reset) { | ||
239 | qset_reset(whc, qset); | ||
240 | if (!list_empty(&qset->stds)) { | ||
241 | asl_qset_insert_begin(whc, qset); | ||
242 | queue_work(whc->workqueue, &whc->async_work); | ||
243 | } | ||
244 | } | ||
235 | } | 245 | } |
236 | 246 | ||
237 | spin_unlock_irq(&whc->lock); | 247 | spin_unlock_irq(&whc->lock); |
@@ -267,7 +277,7 @@ int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) | |||
267 | else | 277 | else |
268 | err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); | 278 | err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); |
269 | if (!err) { | 279 | if (!err) { |
270 | if (!qset->in_sw_list) | 280 | if (!qset->in_sw_list && !qset->remove) |
271 | asl_qset_insert_begin(whc, qset); | 281 | asl_qset_insert_begin(whc, qset); |
272 | } else | 282 | } else |
273 | usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); | 283 | usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); |
diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c index e019a5058ab8..687b622a1612 100644 --- a/drivers/usb/host/whci/hcd.c +++ b/drivers/usb/host/whci/hcd.c | |||
@@ -192,19 +192,23 @@ static void whc_endpoint_reset(struct usb_hcd *usb_hcd, | |||
192 | struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); | 192 | struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); |
193 | struct whc *whc = wusbhc_to_whc(wusbhc); | 193 | struct whc *whc = wusbhc_to_whc(wusbhc); |
194 | struct whc_qset *qset; | 194 | struct whc_qset *qset; |
195 | unsigned long flags; | ||
196 | |||
197 | spin_lock_irqsave(&whc->lock, flags); | ||
195 | 198 | ||
196 | qset = ep->hcpriv; | 199 | qset = ep->hcpriv; |
197 | if (qset) { | 200 | if (qset) { |
198 | qset->remove = 1; | 201 | qset->remove = 1; |
202 | qset->reset = 1; | ||
199 | 203 | ||
200 | if (usb_endpoint_xfer_bulk(&ep->desc) | 204 | if (usb_endpoint_xfer_bulk(&ep->desc) |
201 | || usb_endpoint_xfer_control(&ep->desc)) | 205 | || usb_endpoint_xfer_control(&ep->desc)) |
202 | queue_work(whc->workqueue, &whc->async_work); | 206 | queue_work(whc->workqueue, &whc->async_work); |
203 | else | 207 | else |
204 | queue_work(whc->workqueue, &whc->periodic_work); | 208 | queue_work(whc->workqueue, &whc->periodic_work); |
205 | |||
206 | qset_reset(whc, qset); | ||
207 | } | 209 | } |
210 | |||
211 | spin_unlock_irqrestore(&whc->lock, flags); | ||
208 | } | 212 | } |
209 | 213 | ||
210 | 214 | ||
diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c index ff4ef9e910d9..a9e05bac6646 100644 --- a/drivers/usb/host/whci/pzl.c +++ b/drivers/usb/host/whci/pzl.c | |||
@@ -255,11 +255,21 @@ void scan_periodic_work(struct work_struct *work) | |||
255 | /* | 255 | /* |
256 | * Now that the PZL is updated, complete the removal of any | 256 | * Now that the PZL is updated, complete the removal of any |
257 | * removed qsets. | 257 | * removed qsets. |
258 | * | ||
259 | * If the qset was to be reset, do so and reinsert it into the | ||
260 | * PZL if it has pending transfers. | ||
258 | */ | 261 | */ |
259 | spin_lock_irq(&whc->lock); | 262 | spin_lock_irq(&whc->lock); |
260 | 263 | ||
261 | list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { | 264 | list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { |
262 | qset_remove_complete(whc, qset); | 265 | qset_remove_complete(whc, qset); |
266 | if (qset->reset) { | ||
267 | qset_reset(whc, qset); | ||
268 | if (!list_empty(&qset->stds)) { | ||
269 | qset_insert_in_sw_list(whc, qset); | ||
270 | queue_work(whc->workqueue, &whc->periodic_work); | ||
271 | } | ||
272 | } | ||
263 | } | 273 | } |
264 | 274 | ||
265 | spin_unlock_irq(&whc->lock); | 275 | spin_unlock_irq(&whc->lock); |
@@ -295,7 +305,7 @@ int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) | |||
295 | else | 305 | else |
296 | err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); | 306 | err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); |
297 | if (!err) { | 307 | if (!err) { |
298 | if (!qset->in_sw_list) | 308 | if (!qset->in_sw_list && !qset->remove) |
299 | qset_insert_in_sw_list(whc, qset); | 309 | qset_insert_in_sw_list(whc, qset); |
300 | } else | 310 | } else |
301 | usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); | 311 | usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb); |
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index 640b38fbd051..1b9dc1571570 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c | |||
@@ -103,7 +103,6 @@ static void qset_fill_qh(struct whc_qset *qset, struct urb *urb) | |||
103 | void qset_clear(struct whc *whc, struct whc_qset *qset) | 103 | void qset_clear(struct whc *whc, struct whc_qset *qset) |
104 | { | 104 | { |
105 | qset->td_start = qset->td_end = qset->ntds = 0; | 105 | qset->td_start = qset->td_end = qset->ntds = 0; |
106 | qset->remove = 0; | ||
107 | 106 | ||
108 | qset->qh.link = cpu_to_le32(QH_LINK_NTDS(8) | QH_LINK_T); | 107 | qset->qh.link = cpu_to_le32(QH_LINK_NTDS(8) | QH_LINK_T); |
109 | qset->qh.status = qset->qh.status & QH_STATUS_SEQ_MASK; | 108 | qset->qh.status = qset->qh.status & QH_STATUS_SEQ_MASK; |
@@ -125,7 +124,7 @@ void qset_clear(struct whc *whc, struct whc_qset *qset) | |||
125 | */ | 124 | */ |
126 | void qset_reset(struct whc *whc, struct whc_qset *qset) | 125 | void qset_reset(struct whc *whc, struct whc_qset *qset) |
127 | { | 126 | { |
128 | wait_for_completion(&qset->remove_complete); | 127 | qset->reset = 0; |
129 | 128 | ||
130 | qset->qh.status &= ~QH_STATUS_SEQ_MASK; | 129 | qset->qh.status &= ~QH_STATUS_SEQ_MASK; |
131 | qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1); | 130 | qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1); |
@@ -156,6 +155,7 @@ struct whc_qset *get_qset(struct whc *whc, struct urb *urb, | |||
156 | 155 | ||
157 | void qset_remove_complete(struct whc *whc, struct whc_qset *qset) | 156 | void qset_remove_complete(struct whc *whc, struct whc_qset *qset) |
158 | { | 157 | { |
158 | qset->remove = 0; | ||
159 | list_del_init(&qset->list_node); | 159 | list_del_init(&qset->list_node); |
160 | complete(&qset->remove_complete); | 160 | complete(&qset->remove_complete); |
161 | } | 161 | } |
diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h index 794dba0d0f0a..e8d0001605be 100644 --- a/drivers/usb/host/whci/whci-hc.h +++ b/drivers/usb/host/whci/whci-hc.h | |||
@@ -264,6 +264,7 @@ struct whc_qset { | |||
264 | unsigned in_sw_list:1; | 264 | unsigned in_sw_list:1; |
265 | unsigned in_hw_list:1; | 265 | unsigned in_hw_list:1; |
266 | unsigned remove:1; | 266 | unsigned remove:1; |
267 | unsigned reset:1; | ||
267 | struct urb *pause_after_urb; | 268 | struct urb *pause_after_urb; |
268 | struct completion remove_complete; | 269 | struct completion remove_complete; |
269 | int max_burst; | 270 | int max_burst; |