aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorMatthew Dharm <mdharm-usb@one-eyed-alien.net>2005-06-06 20:21:41 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2005-06-27 17:44:03 -0400
commit4d07ef762fc8d6d35ecc1511a3b953a733a61a5f (patch)
tree9ea2bbce922ed74bcef3d26dd1843afaf6f7a877 /drivers/usb
parent5203ad441310a4c2abd4fb79015a6bdadc2a5a4f (diff)
[PATCH] USB Storage: port reset on transport error
This patch causes a port reset whenever there's a transport error or abort. If that fails it reverts back to doing a mass-storage device reset. It started life as as497 and was rediffed by me. This makes error recovery a lot quicker and more reliable. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Matthew Dharm <mdharm-usb@one-eyed-alien.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/storage/scsiglue.c45
-rw-r--r--drivers/usb/storage/scsiglue.h1
-rw-r--r--drivers/usb/storage/transport.c99
-rw-r--r--drivers/usb/storage/transport.h1
4 files changed, 82 insertions, 64 deletions
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index e43eddc3d44b..da2bfa944b96 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -255,50 +255,23 @@ static int device_reset(struct scsi_cmnd *srb)
255 255
256 /* lock the device pointers and do the reset */ 256 /* lock the device pointers and do the reset */
257 down(&(us->dev_semaphore)); 257 down(&(us->dev_semaphore));
258 if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { 258 result = us->transport_reset(us);
259 result = FAILED;
260 US_DEBUGP("No reset during disconnect\n");
261 } else
262 result = us->transport_reset(us);
263 up(&(us->dev_semaphore)); 259 up(&(us->dev_semaphore));
264 260
265 return result; 261 return result < 0 ? FAILED : SUCCESS;
266} 262}
267 263
268/* This resets the device's USB port. */ 264/* Simulate a SCSI bus reset by resetting the device's USB port. */
269/* It refuses to work if there's more than one interface in
270 * the device, so that other users are not affected. */
271/* This is always called with scsi_lock(host) held */ 265/* This is always called with scsi_lock(host) held */
272static int bus_reset(struct scsi_cmnd *srb) 266static int bus_reset(struct scsi_cmnd *srb)
273{ 267{
274 struct us_data *us = host_to_us(srb->device->host); 268 struct us_data *us = host_to_us(srb->device->host);
275 int result, rc; 269 int result;
276 270
277 US_DEBUGP("%s called\n", __FUNCTION__); 271 US_DEBUGP("%s called\n", __FUNCTION__);
278 272
279 /* The USB subsystem doesn't handle synchronisation between
280 * a device's several drivers. Therefore we reset only devices
281 * with just one interface, which we of course own. */
282
283 down(&(us->dev_semaphore)); 273 down(&(us->dev_semaphore));
284 if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { 274 result = usb_stor_port_reset(us);
285 result = -EIO;
286 US_DEBUGP("No reset during disconnect\n");
287 } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) {
288 result = -EBUSY;
289 US_DEBUGP("Refusing to reset a multi-interface device\n");
290 } else {
291 rc = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
292 if (rc < 0) {
293 US_DEBUGP("unable to lock device for reset: %d\n", rc);
294 result = rc;
295 } else {
296 result = usb_reset_device(us->pusb_dev);
297 if (rc)
298 usb_unlock_device(us->pusb_dev);
299 US_DEBUGP("usb_reset_device returns %d\n", result);
300 }
301 }
302 up(&(us->dev_semaphore)); 275 up(&(us->dev_semaphore));
303 276
304 /* lock the host for the return */ 277 /* lock the host for the return */
@@ -320,6 +293,14 @@ void usb_stor_report_device_reset(struct us_data *us)
320 } 293 }
321} 294}
322 295
296/* Report a driver-initiated bus reset to the SCSI layer.
297 * Calling this for a SCSI-initiated reset is unnecessary but harmless.
298 * The caller must own the SCSI host lock. */
299void usb_stor_report_bus_reset(struct us_data *us)
300{
301 scsi_report_bus_reset(us_to_host(us), 0);
302}
303
323/*********************************************************************** 304/***********************************************************************
324 * /proc/scsi/ functions 305 * /proc/scsi/ functions
325 ***********************************************************************/ 306 ***********************************************************************/
diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h
index d0a49af026c4..737e4fa6045f 100644
--- a/drivers/usb/storage/scsiglue.h
+++ b/drivers/usb/storage/scsiglue.h
@@ -42,6 +42,7 @@
42#define _SCSIGLUE_H_ 42#define _SCSIGLUE_H_
43 43
44extern void usb_stor_report_device_reset(struct us_data *us); 44extern void usb_stor_report_device_reset(struct us_data *us);
45extern void usb_stor_report_bus_reset(struct us_data *us);
45 46
46extern unsigned char usb_stor_sense_invalidCDB[18]; 47extern unsigned char usb_stor_sense_invalidCDB[18];
47extern struct scsi_host_template usb_stor_host_template; 48extern struct scsi_host_template usb_stor_host_template;
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 419afb2216b9..e6b1c6cf07f2 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -541,15 +541,15 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
541 */ 541 */
542 if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { 542 if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
543 US_DEBUGP("-- command was aborted\n"); 543 US_DEBUGP("-- command was aborted\n");
544 goto Handle_Abort; 544 srb->result = DID_ABORT << 16;
545 goto Handle_Errors;
545 } 546 }
546 547
547 /* if there is a transport error, reset and don't auto-sense */ 548 /* if there is a transport error, reset and don't auto-sense */
548 if (result == USB_STOR_TRANSPORT_ERROR) { 549 if (result == USB_STOR_TRANSPORT_ERROR) {
549 US_DEBUGP("-- transport indicates error, resetting\n"); 550 US_DEBUGP("-- transport indicates error, resetting\n");
550 us->transport_reset(us);
551 srb->result = DID_ERROR << 16; 551 srb->result = DID_ERROR << 16;
552 return; 552 goto Handle_Errors;
553 } 553 }
554 554
555 /* if the transport provided its own sense data, don't auto-sense */ 555 /* if the transport provided its own sense data, don't auto-sense */
@@ -669,7 +669,8 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
669 669
670 if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { 670 if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
671 US_DEBUGP("-- auto-sense aborted\n"); 671 US_DEBUGP("-- auto-sense aborted\n");
672 goto Handle_Abort; 672 srb->result = DID_ABORT << 16;
673 goto Handle_Errors;
673 } 674 }
674 if (temp_result != USB_STOR_TRANSPORT_GOOD) { 675 if (temp_result != USB_STOR_TRANSPORT_GOOD) {
675 US_DEBUGP("-- auto-sense failure\n"); 676 US_DEBUGP("-- auto-sense failure\n");
@@ -678,9 +679,9 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
678 * multi-target device, since failure of an 679 * multi-target device, since failure of an
679 * auto-sense is perfectly valid 680 * auto-sense is perfectly valid
680 */ 681 */
681 if (!(us->flags & US_FL_SCM_MULT_TARG))
682 us->transport_reset(us);
683 srb->result = DID_ERROR << 16; 682 srb->result = DID_ERROR << 16;
683 if (!(us->flags & US_FL_SCM_MULT_TARG))
684 goto Handle_Errors;
684 return; 685 return;
685 } 686 }
686 687
@@ -721,12 +722,28 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
721 722
722 return; 723 return;
723 724
724 /* abort processing: the bulk-only transport requires a reset 725 /* Error and abort processing: try to resynchronize with the device
725 * following an abort */ 726 * by issuing a port reset. If that fails, try a class-specific
726 Handle_Abort: 727 * device reset. */
727 srb->result = DID_ABORT << 16; 728 Handle_Errors:
728 if (us->protocol == US_PR_BULK) 729
730 /* Let the SCSI layer know we are doing a reset, set the
731 * RESETTING bit, and clear the ABORTING bit so that the reset
732 * may proceed. */
733 scsi_lock(us_to_host(us));
734 usb_stor_report_bus_reset(us);
735 set_bit(US_FLIDX_RESETTING, &us->flags);
736 clear_bit(US_FLIDX_ABORTING, &us->flags);
737 scsi_unlock(us_to_host(us));
738
739 result = usb_stor_port_reset(us);
740 if (result < 0) {
741 scsi_lock(us_to_host(us));
742 usb_stor_report_device_reset(us);
743 scsi_unlock(us_to_host(us));
729 us->transport_reset(us); 744 us->transport_reset(us);
745 }
746 clear_bit(US_FLIDX_RESETTING, &us->flags);
730} 747}
731 748
732/* Stop the current URB transfer */ 749/* Stop the current URB transfer */
@@ -1134,24 +1151,18 @@ static int usb_stor_reset_common(struct us_data *us,
1134{ 1151{
1135 int result; 1152 int result;
1136 int result2; 1153 int result2;
1137 int rc = FAILED;
1138 1154
1139 /* Let the SCSI layer know we are doing a reset, set the 1155 if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
1140 * RESETTING bit, and clear the ABORTING bit so that the reset 1156 US_DEBUGP("No reset during disconnect\n");
1141 * may proceed. 1157 return -EIO;
1142 */ 1158 }
1143 scsi_lock(us_to_host(us));
1144 usb_stor_report_device_reset(us);
1145 set_bit(US_FLIDX_RESETTING, &us->flags);
1146 clear_bit(US_FLIDX_ABORTING, &us->flags);
1147 scsi_unlock(us_to_host(us));
1148 1159
1149 result = usb_stor_control_msg(us, us->send_ctrl_pipe, 1160 result = usb_stor_control_msg(us, us->send_ctrl_pipe,
1150 request, requesttype, value, index, data, size, 1161 request, requesttype, value, index, data, size,
1151 5*HZ); 1162 5*HZ);
1152 if (result < 0) { 1163 if (result < 0) {
1153 US_DEBUGP("Soft reset failed: %d\n", result); 1164 US_DEBUGP("Soft reset failed: %d\n", result);
1154 goto Done; 1165 return result;
1155 } 1166 }
1156 1167
1157 /* Give the device some time to recover from the reset, 1168 /* Give the device some time to recover from the reset,
@@ -1161,7 +1172,7 @@ static int usb_stor_reset_common(struct us_data *us,
1161 HZ*6); 1172 HZ*6);
1162 if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { 1173 if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
1163 US_DEBUGP("Reset interrupted by disconnect\n"); 1174 US_DEBUGP("Reset interrupted by disconnect\n");
1164 goto Done; 1175 return -EIO;
1165 } 1176 }
1166 1177
1167 US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n"); 1178 US_DEBUGP("Soft reset: clearing bulk-in endpoint halt\n");
@@ -1173,16 +1184,11 @@ static int usb_stor_reset_common(struct us_data *us,
1173 /* return a result code based on the result of the clear-halts */ 1184 /* return a result code based on the result of the clear-halts */
1174 if (result >= 0) 1185 if (result >= 0)
1175 result = result2; 1186 result = result2;
1176 if (result < 0) { 1187 if (result < 0)
1177 US_DEBUGP("Soft reset failed\n"); 1188 US_DEBUGP("Soft reset failed\n");
1178 goto Done; 1189 else
1179 } 1190 US_DEBUGP("Soft reset done\n");
1180 US_DEBUGP("Soft reset done\n"); 1191 return result;
1181 rc = SUCCESS;
1182
1183 Done:
1184 clear_bit(US_FLIDX_RESETTING, &us->flags);
1185 return rc;
1186} 1192}
1187 1193
1188/* This issues a CB[I] Reset to the device in question 1194/* This issues a CB[I] Reset to the device in question
@@ -1212,3 +1218,32 @@ int usb_stor_Bulk_reset(struct us_data *us)
1212 USB_TYPE_CLASS | USB_RECIP_INTERFACE, 1218 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
1213 0, us->ifnum, NULL, 0); 1219 0, us->ifnum, NULL, 0);
1214} 1220}
1221
1222/* Issue a USB port reset to the device. But don't do anything if
1223 * there's more than one interface in the device, so that other users
1224 * are not affected. */
1225int usb_stor_port_reset(struct us_data *us)
1226{
1227 int result, rc;
1228
1229 if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
1230 result = -EIO;
1231 US_DEBUGP("No reset during disconnect\n");
1232 } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) {
1233 result = -EBUSY;
1234 US_DEBUGP("Refusing to reset a multi-interface device\n");
1235 } else {
1236 result = rc =
1237 usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
1238 if (result < 0) {
1239 US_DEBUGP("unable to lock device for reset: %d\n",
1240 result);
1241 } else {
1242 result = usb_reset_device(us->pusb_dev);
1243 if (rc)
1244 usb_unlock_device(us->pusb_dev);
1245 US_DEBUGP("usb_reset_device returns %d\n", result);
1246 }
1247 }
1248 return result;
1249}
diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h
index e25f8d8fc741..8d9e0663f8fe 100644
--- a/drivers/usb/storage/transport.h
+++ b/drivers/usb/storage/transport.h
@@ -171,4 +171,5 @@ extern int usb_stor_bulk_transfer_buf(struct us_data *us, unsigned int pipe,
171extern int usb_stor_bulk_transfer_sg(struct us_data *us, unsigned int pipe, 171extern int usb_stor_bulk_transfer_sg(struct us_data *us, unsigned int pipe,
172 void *buf, unsigned int length, int use_sg, int *residual); 172 void *buf, unsigned int length, int use_sg, int *residual);
173 173
174extern int usb_stor_port_reset(struct us_data *us);
174#endif 175#endif