From 0fc7f96b5be344f7a5499e29b30f245fb857f4a2 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 15 Apr 2010 15:04:07 -0400 Subject: [PATCH] USB: gadget: f_mass_storage: Defer handling interface changes until it is safe. Pulling in some code from file_storage.c, we now handle interface changes in do_set_config(), which is now not called until any pending requests have been successfully completed or cancelled. This fixes a race condition that resulted in usb_ep_free_request() being called while the request is still busy. Signed-off-by: Mike Lockwood --- drivers/usb/gadget/f_mass_storage.c | 39 +++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index f4622b36baf7..ca85cc01e9f8 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -2261,18 +2261,19 @@ static int do_set_config(struct fsg_dev *fsg, u8 new_config) { int rc = 0; - if (new_config == fsg->config) - return rc; - /* Disable the single interface */ if (fsg->config != 0) { DBG(fsg, "reset config\n"); fsg->config = 0; + rc = do_set_interface(fsg, -1); } /* Enable the interface */ - if (new_config != 0) + if (new_config != 0) { fsg->config = new_config; + if ((rc = do_set_interface(fsg, 0)) != 0) + fsg->config = 0; // Reset on errors + } switch_set_state(&fsg->sdev, new_config); adjust_wake_lock(fsg); @@ -2287,6 +2288,7 @@ static void handle_exception(struct fsg_dev *fsg) siginfo_t info; int sig; int i; + int num_active; struct fsg_buffhd *bh; enum fsg_state old_state; u8 new_config; @@ -2308,6 +2310,28 @@ static void handle_exception(struct fsg_dev *fsg) } } + /* Cancel all the pending transfers */ + for (i = 0; i < NUM_BUFFERS; ++i) { + bh = &fsg->buffhds[i]; + if (bh->inreq_busy) + usb_ep_dequeue(fsg->bulk_in, bh->inreq); + if (bh->outreq_busy) + usb_ep_dequeue(fsg->bulk_out, bh->outreq); + } + + /* Wait until everything is idle */ + for (;;) { + num_active = 0; + for (i = 0; i < NUM_BUFFERS; ++i) { + bh = &fsg->buffhds[i]; + num_active += bh->outreq_busy; + } + if (num_active == 0) + break; + if (sleep_thread(fsg)) + return; + } + /* * Do NOT flush the fifo after set_interface() * Otherwise, it results in some data being lost @@ -2378,10 +2402,6 @@ static void handle_exception(struct fsg_dev *fsg) case FSG_STATE_EXIT: case FSG_STATE_TERMINATED: - if (new_config) { - fsg->new_config = 0; - do_set_interface(fsg, -1); - } do_set_config(fsg, 0); /* Free resources */ spin_lock_irqsave(&fsg->lock, flags); fsg->state = FSG_STATE_TERMINATED; /* Stop the thread */ @@ -2885,7 +2905,6 @@ static int fsg_function_set_alt(struct usb_function *f, struct fsg_dev *fsg = func_to_dev(f); DBG(fsg, "fsg_function_set_alt intf: %d alt: %d\n", intf, alt); fsg->new_config = 1; - do_set_interface(fsg, 0); raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); return 0; } @@ -2894,8 +2913,6 @@ static void fsg_function_disable(struct usb_function *f) { struct fsg_dev *fsg = func_to_dev(f); DBG(fsg, "fsg_function_disable\n"); - if (fsg->new_config) - do_set_interface(fsg, -1); fsg->new_config = 0; raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); } -- 2.34.1