[prev in list] [next in list] [prev in thread] [next in thread] 

List:       linux-usb-devel
Subject:    [linux-usb-devel] PATCH: usb-storage: code consolidation
From:       Matthew Dharm <mdharm-usb () one-eyed-alien ! net>
Date:       2002-03-31 3:38:09
[Download RAW message or body]

[Attachment #2 (multipart/mixed)]


Attached is a patch to usb-storage against 2.4.19-pre5.  Greg, please send
to Marcello for inclusion.

This patch consolidates a great deal of common code in the URB submission
paths, and in the reset paths.  This is in preparation for the new
error-handling state machine.

These changes are courtsey of Rowland Stern.  I've tested them, and they
look good.  More changes will follow shortly.

Matt

-- 
Matthew Dharm                              Home: mdharm-usb@one-eyed-alien.net 
Maintainer, Linux USB Mass Storage Driver

Why am I talking to a toilet brush?
					-- CEO
User Friendly, 4/30/1998

["patch20020330" (text/plain)]

diff -u -X dontdiff linux-2.4.19-pre5/drivers/usb/storage.old/isd200.c \
                linux-2.4.19-pre5/drivers/usb/storage/isd200.c
--- linux-2.4.19-pre5/drivers/usb/storage.old/isd200.c	Sat Mar 30 16:35:49 2002
+++ linux-2.4.19-pre5/drivers/usb/storage/isd200.c	Sat Mar 30 16:40:00 2002
@@ -1,6 +1,6 @@
 /* Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC
  *
- * $Id: isd200.c,v 1.14 2002/02/25 00:40:13 mdharm Exp $
+ * $Id: isd200.c,v 1.15 2002/03/31 00:40:00 mdharm Exp $
  *
  * Current development and maintenance:
  *   (C) 2001-2002 Björn Stenberg (bjorn@haxx.se)
diff -u -X dontdiff linux-2.4.19-pre5/drivers/usb/storage.old/transport.c \
                linux-2.4.19-pre5/drivers/usb/storage/transport.c
--- linux-2.4.19-pre5/drivers/usb/storage.old/transport.c	Sat Mar 30 16:35:49 2002
+++ linux-2.4.19-pre5/drivers/usb/storage/transport.c	Sat Mar 30 15:47:28 2002
@@ -1,6 +1,6 @@
 /* Driver for USB Mass Storage compliant devices
  *
- * $Id: transport.c,v 1.44 2002/02/25 00:43:41 mdharm Exp $
+ * $Id: transport.c,v 1.45 2002/03/30 23:47:28 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -353,39 +353,19 @@
 	complete(urb_done_ptr);
 }
 
-/* This is our function to emulate usb_control_msg() but give us enough
- * access to make aborts/resets work
+/* This is the common part of the URB message submission code
+ * This function expects the current_urb_sem to be held upon entry.
  */
-int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
-			 u8 request, u8 requesttype, u16 value, u16 index, 
-			 void *data, u16 size)
+static int usb_stor_msg_common(struct us_data *us)
 {
 	struct completion urb_done;
 	int status;
-	devrequest *dr;
-
-	/* allocate the device request structure */
-	dr = kmalloc(sizeof(devrequest), GFP_NOIO);
-	if (!dr)
-		return -ENOMEM;
-
-	/* fill in the structure */
-	dr->requesttype = requesttype;
-	dr->request = request;
-	dr->value = cpu_to_le16(value);
-	dr->index = cpu_to_le16(index);
-	dr->length = cpu_to_le16(size);
 
 	/* set up data structures for the wakeup system */
 	init_completion(&urb_done);
 
-	/* lock the URB */
-	down(&(us->current_urb_sem));
-
-	/* fill the URB */
-	FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, 
-			 (unsigned char*) dr, data, size, 
-			 usb_stor_blocking_completion, &urb_done);
+	/* fill the common fields in the URB */
+	us->current_urb->context = &urb_done;
 	us->current_urb->actual_length = 0;
 	us->current_urb->error_count = 0;
 	us->current_urb->transfer_flags = USB_ASYNC_UNLINK;
@@ -394,8 +374,6 @@
 	status = usb_submit_urb(us->current_urb);
 	if (status) {
 		/* something went wrong */
-		up(&(us->current_urb_sem));
-		kfree(dr);
 		return status;
 	}
 
@@ -404,58 +382,74 @@
 	wait_for_completion(&urb_done);
 	down(&(us->current_urb_sem));
 
+	/* return the URB status */
+	return us->current_urb->status;
+}
+
+/* This is our function to emulate usb_control_msg() with enough control
+ * to make aborts/resets/timeouts work
+ *
+ * FIXME: devrequest is allocated on the kernel stack, which is not always
+ * DMA-capable memory.
+ */
+int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
+			 u8 request, u8 requesttype, u16 value, u16 index, 
+			 void *data, u16 size)
+{
+	int status;
+	devrequest		dr;
+
+	/* fill in the devrequest structure */
+	dr.requesttype = requesttype;
+	dr.request = request;
+	dr.value = cpu_to_le16(value);
+	dr.index = cpu_to_le16(index);
+	dr.length = cpu_to_le16(size);
+
+	/* lock the URB */
+	down(&(us->current_urb_sem));
+
+	/* fill the URB */
+	FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, 
+			 (unsigned char*) &dr, data, size, 
+			 usb_stor_blocking_completion, NULL);
+
+	/* submit the URB */
+	status = usb_stor_msg_common(us);
+
 	/* return the actual length of the data transferred if no error*/
-	status = us->current_urb->status;
 	if (status >= 0)
 		status = us->current_urb->actual_length;
 
 	/* release the lock and return status */
 	up(&(us->current_urb_sem));
-	kfree(dr);
-  	return status;
+	return status;
 }
 
-/* This is our function to emulate usb_bulk_msg() but give us enough
- * access to make aborts/resets work
+/* This is our function to emulate usb_bulk_msg() with enough control
+ * to make aborts/resets/timeouts work
  */
 int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
 		      unsigned int len, unsigned int *act_len)
 {
-	struct completion urb_done;
 	int status;
 
-	/* set up data structures for the wakeup system */
-	init_completion(&urb_done);
-
 	/* lock the URB */
 	down(&(us->current_urb_sem));
 
 	/* fill the URB */
 	FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len,
-		      usb_stor_blocking_completion, &urb_done);
-	us->current_urb->actual_length = 0;
-	us->current_urb->error_count = 0;
-	us->current_urb->transfer_flags = USB_ASYNC_UNLINK;
+		      usb_stor_blocking_completion, NULL);
 
 	/* submit the URB */
-	status = usb_submit_urb(us->current_urb);
-	if (status) {
-		/* something went wrong */
-		up(&(us->current_urb_sem));
-		return status;
-	}
-
-	/* wait for the completion of the URB */
-	up(&(us->current_urb_sem));
-	wait_for_completion(&urb_done);
-	down(&(us->current_urb_sem));
+	status = usb_stor_msg_common(us);
 
 	/* return the actual length of the data transferred */
 	*act_len = us->current_urb->actual_length;
 
 	/* release the lock and return status */
 	up(&(us->current_urb_sem));
-	return us->current_urb->status;
+	return status;
 }
 
 /* This is a version of usb_clear_halt() that doesn't read the status from
@@ -616,7 +610,7 @@
 /* Invoke the transport and basic error-handling/recovery methods
  *
  * This is used by the protocol layers to actually send the message to
- * the device and receive the response.
+ * the device and recieve the response.
  */
 void usb_stor_invoke_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
@@ -1073,8 +1067,6 @@
 	return 0;
 }
 
-int usb_stor_Bulk_reset(struct us_data *us);
-
 int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
 	struct bulk_cb_wrap bcb;
@@ -1229,12 +1221,108 @@
  * Reset routines
  ***********************************************************************/
 
+struct us_timeout {
+	struct us_data *us;
+	spinlock_t timer_lock;
+};
+
+/* The timeout event handler
+ */
+static void usb_stor_timeout_handler(unsigned long to__)
+{
+	struct us_timeout *to = (struct us_timeout *) to__;
+	struct us_data *us = to->us;
+
+	US_DEBUGP("Timeout occurred\n");
+
+	/* abort the current request */
+	if (us->current_urb->status == -EINPROGRESS)
+		usb_unlink_urb(us->current_urb);
+
+	/* let the reset routine know we have finished */
+	spin_unlock(&to->timer_lock);
+}
+
+/* This is the common part of the device reset code.
+ *
+ * It's handy that every transport mechanism uses the control endpoint for
+ * resets.
+ *
+ * Basically, we send a reset with a 20-second timeout, so we don't get
+ * jammed attempting to do the reset.
+ */
+void usb_stor_reset_common(struct us_data *us, u8 request, u8 requesttype,
+		u16 value, u16 index, void *data, u16 size)
+{
+	int result;
+	struct us_timeout timeout_data = {us, SPIN_LOCK_UNLOCKED};
+	struct timer_list timeout_list;
+
+	/* prepare the timeout handler */
+	spin_lock(&timeout_data.timer_lock);
+	init_timer(&timeout_list);
+
+	/* A 20-second timeout may seem rather long, but a LaCie
+	 *  StudioDrive USB2 device takes 16+ seconds to get going
+	 *  following a powerup or USB attach event. */
+
+	timeout_list.expires = jiffies + 20 * HZ;
+	timeout_list.data = (unsigned long) &timeout_data;
+	timeout_list.function = usb_stor_timeout_handler;
+	add_timer(&timeout_list);
+
+	result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0),
+			 request, requesttype, value, index, data, size);
+	if (result < 0)
+		goto Done;
+
+	/* long wait for reset */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(HZ*6);
+	set_current_state(TASK_RUNNING);
+
+	US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n");
+	result = usb_stor_clear_halt(us,
+		usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+	if (result < 0)
+		goto Done;
+
+	US_DEBUGP("Soft reset: clearing bulk-out endpoint halt\n");
+	result = usb_stor_clear_halt(us,
+		usb_sndbulkpipe(us->pusb_dev, us->ep_out));
+
+	Done:
+
+	/* prevent the timer from coming back to haunt us */
+	if (!del_timer(&timeout_list)) {
+		/* the handler has already started; wait for it to finish */
+		spin_lock(&timeout_data.timer_lock);
+		/* change the abort into a timeout */
+		if (result == -ENOENT)
+			result = -ETIMEDOUT;
+	}
+
+	/* return a result code based on the result of the control message */
+	if (result >= 0)
+		US_DEBUGP("Soft reset done\n");
+	else
+		US_DEBUGP("Soft reset failed: %d\n", result);
+
+	if (result == -ETIMEDOUT)
+		us->srb->result = DID_TIME_OUT << 16;
+	else if (result == -ENOENT)
+		us->srb->result = DID_ABORT << 16;
+	else if (result < 0)
+		us->srb->result = DID_ERROR << 16;
+	else
+		us->srb->result = GOOD << 1;
+}
+
 /* This issues a CB[I] Reset to the device in question
  */
 int usb_stor_CB_reset(struct us_data *us)
 {
 	unsigned char cmd[12];
-	int result;
 
 	US_DEBUGP("CB_reset() called\n");
 
@@ -1245,30 +1333,10 @@
 	memset(cmd, 0xFF, sizeof(cmd));
 	cmd[0] = SEND_DIAGNOSTIC;
 	cmd[1] = 4;
-	result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
-				 US_CBI_ADSC, 
+	usb_stor_reset_common(us, US_CBI_ADSC, 
 				 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-				 0, us->ifnum, cmd, sizeof(cmd), HZ*5);
-
-	if (result < 0) {
-		US_DEBUGP("CB[I] soft reset failed %d\n", result);
-		return FAILED;
-	}
-
-	/* long wait for reset */
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(HZ*6);
-	set_current_state(TASK_RUNNING);
-
-	US_DEBUGP("CB_reset: clearing endpoint halt\n");
-	usb_stor_clear_halt(us,
-			usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
-	usb_stor_clear_halt(us,
-			usb_sndbulkpipe(us->pusb_dev, us->ep_out));
-
-	US_DEBUGP("CB_reset done\n");
-	/* return a result code based on the result of the control message */
-	return SUCCESS;
+				 0, us->ifnum, cmd, sizeof(cmd));
+	return (us->srb->result == GOOD << 1 ? SUCCESS : FAILED);
 }
 
 /* This issues a Bulk-only Reset to the device in question, including
@@ -1276,34 +1344,14 @@
  */
 int usb_stor_Bulk_reset(struct us_data *us)
 {
-	int result;
-
 	US_DEBUGP("Bulk reset requested\n");
 
 	/* if the device was removed, then we're already reset */
 	if (!us->pusb_dev)
 		return SUCCESS;
 
-	result = usb_control_msg(us->pusb_dev, 
-				 usb_sndctrlpipe(us->pusb_dev,0), 
-				 US_BULK_RESET_REQUEST, 
+	usb_stor_reset_common(us, US_BULK_RESET_REQUEST, 
 				 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-				 0, us->ifnum, NULL, 0, HZ*5);
-
-	if (result < 0) {
-		US_DEBUGP("Bulk soft reset failed %d\n", result);
-		return FAILED;
-	}
-
-	/* long wait for reset */
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(HZ*6);
-	set_current_state(TASK_RUNNING);
-
-	usb_stor_clear_halt(us,
-			usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
-	usb_stor_clear_halt(us,
-			usb_sndbulkpipe(us->pusb_dev, us->ep_out));
-	US_DEBUGP("Bulk soft reset completed\n");
-	return SUCCESS;
+				 0, us->ifnum, NULL, 0);
+	return (us->srb->result == GOOD << 1 ? SUCCESS : FAILED);
 }
diff -u -X dontdiff linux-2.4.19-pre5/drivers/usb/storage.old/usb.h \
                linux-2.4.19-pre5/drivers/usb/storage/usb.h
--- linux-2.4.19-pre5/drivers/usb/storage.old/usb.h	Thu Nov 22 11:49:34 2001
+++ linux-2.4.19-pre5/drivers/usb/storage/usb.h	Sat Mar 30 17:02:59 2002
@@ -1,7 +1,7 @@
 /* Driver for USB Mass Storage compliant devices
  * Main Header File
  *
- * $Id: usb.h,v 1.18 2001/07/30 00:27:59 mdharm Exp $
+ * $Id: usb.h,v 1.20 2002/03/31 00:40:00 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -188,4 +188,5 @@
 /* Function to fill an inquiry response. See usb.c for details */
 extern void fill_inquiry_response(struct us_data *us,
 	unsigned char *data, unsigned int data_len);
+
 #endif


[Attachment #6 (application/pgp-signature)]
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic