Commit 1872bceb authored by Pete Zaitcev's avatar Pete Zaitcev Committed by Linus Torvalds

[PATCH] ub: fix for blank CDs

This patch fixes a microcode lockup in my CD-ROM adapters when a blank CD
is inserted.  However, do not try to burn CDs yet!  I'm pretty sure that
trying it will end in coasters.

 - Fix a few cases where we were unable to resynchronize with replies
   for previous commands. The main thing is to keep reading replies
   in case of a stall. This is done with the new state CLRRS.
 - Since I am forgetting the basic state machine already, document it.
 - Move counter increments in the looping path in its own function.
 - Fix a harmless buglet in case CSW read fails to submit: do not
   override state.
 - Implement the Alan Stern's idea for adaptive signature checking.
Signed-off-by: default avatarPete Zaitcev <zaitcev@yahoo.com>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 90c5029e
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* -- Exterminate P3 printks * -- Exterminate P3 printks
* -- Resove XXX's * -- Resove XXX's
* -- Redo "benh's retries", perhaps have spin-up code to handle them. V:D=? * -- Redo "benh's retries", perhaps have spin-up code to handle them. V:D=?
* -- CLEAR, CLR2STS, CLRRS seem to be ripe for refactoring.
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -37,6 +38,73 @@ ...@@ -37,6 +38,73 @@
#define UB_MAJOR 180 #define UB_MAJOR 180
/*
* The command state machine is the key model for understanding of this driver.
*
* The general rule is that all transitions are done towards the bottom
* of the diagram, thus preventing any loops.
*
* An exception to that is how the STAT state is handled. A counter allows it
* to be re-entered along the path marked with [C].
*
* +--------+
* ! INIT !
* +--------+
* !
* ub_scsi_cmd_start fails ->--------------------------------------\
* ! !
* V !
* +--------+ !
* ! CMD ! !
* +--------+ !
* ! +--------+ !
* was -EPIPE -->-------------------------------->! CLEAR ! !
* ! +--------+ !
* ! ! !
* was error -->------------------------------------- ! --------->\
* ! ! !
* /--<-- cmd->dir == NONE ? ! !
* ! ! ! !
* ! V ! !
* ! +--------+ ! !
* ! ! DATA ! ! !
* ! +--------+ ! !
* ! ! +---------+ ! !
* ! was -EPIPE -->--------------->! CLR2STS ! ! !
* ! ! +---------+ ! !
* ! ! ! ! !
* ! ! was error -->---- ! --------->\
* ! was error -->--------------------- ! ------------- ! --------->\
* ! ! ! ! !
* ! V ! ! !
* \--->+--------+ ! ! !
* ! STAT !<--------------------------/ ! !
* /--->+--------+ ! !
* ! ! ! !
* [C] was -EPIPE -->-----------\ ! !
* ! ! ! ! !
* +<---- len == 0 ! ! !
* ! ! ! ! !
* ! was error -->--------------------------------------!---------->\
* ! ! ! ! !
* +<---- bad CSW ! ! !
* +<---- bad tag ! ! !
* ! ! V ! !
* ! ! +--------+ ! !
* ! ! ! CLRRS ! ! !
* ! ! +--------+ ! !
* ! ! ! ! !
* \------- ! --------------------[C]--------\ ! !
* ! ! ! !
* cmd->error---\ +--------+ ! !
* ! +--------------->! SENSE !<----------/ !
* STAT_FAIL----/ +--------+ !
* ! ! V
* ! V +--------+
* \--------------------------------\--------------------->! DONE !
* +--------+
*/
/* /*
* Definitions which have to be scattered once we understand the layout better. * Definitions which have to be scattered once we understand the layout better.
*/ */
...@@ -91,8 +159,6 @@ struct bulk_cs_wrap { ...@@ -91,8 +159,6 @@ struct bulk_cs_wrap {
#define US_BULK_CS_WRAP_LEN 13 #define US_BULK_CS_WRAP_LEN 13
#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */ #define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */
/* This is for Olympus Camedia digital cameras */
#define US_BULK_CS_OLYMPUS_SIGN 0x55425355 /* spells out 'USBU' */
#define US_BULK_STAT_OK 0 #define US_BULK_STAT_OK 0
#define US_BULK_STAT_FAIL 1 #define US_BULK_STAT_FAIL 1
#define US_BULK_STAT_PHASE 2 #define US_BULK_STAT_PHASE 2
...@@ -135,6 +201,7 @@ enum ub_scsi_cmd_state { ...@@ -135,6 +201,7 @@ enum ub_scsi_cmd_state {
UB_CMDST_CLR2STS, /* Clearing before requesting status */ UB_CMDST_CLR2STS, /* Clearing before requesting status */
UB_CMDST_STAT, /* Status phase */ UB_CMDST_STAT, /* Status phase */
UB_CMDST_CLEAR, /* Clearing a stall (halt, actually) */ UB_CMDST_CLEAR, /* Clearing a stall (halt, actually) */
UB_CMDST_CLRRS, /* Clearing before retrying status */
UB_CMDST_SENSE, /* Sending Request Sense */ UB_CMDST_SENSE, /* Sending Request Sense */
UB_CMDST_DONE /* Final state */ UB_CMDST_DONE /* Final state */
}; };
...@@ -146,6 +213,7 @@ static char *ub_scsi_cmd_stname[] = { ...@@ -146,6 +213,7 @@ static char *ub_scsi_cmd_stname[] = {
"c2s", "c2s",
"sts", "sts",
"clr", "clr",
"crs",
"Sen", "Sen",
"fin" "fin"
}; };
...@@ -316,6 +384,7 @@ struct ub_dev { ...@@ -316,6 +384,7 @@ struct ub_dev {
struct urb work_urb; struct urb work_urb;
struct timer_list work_timer; struct timer_list work_timer;
int last_pipe; /* What might need clearing */ int last_pipe; /* What might need clearing */
__le32 signature; /* Learned signature */
struct bulk_cb_wrap work_bcb; struct bulk_cb_wrap work_bcb;
struct bulk_cs_wrap work_bcs; struct bulk_cs_wrap work_bcs;
struct usb_ctrlrequest work_cr; struct usb_ctrlrequest work_cr;
...@@ -339,8 +408,9 @@ static void ub_scsi_action(unsigned long _dev); ...@@ -339,8 +408,9 @@ static void ub_scsi_action(unsigned long _dev);
static void ub_scsi_dispatch(struct ub_dev *sc); static void ub_scsi_dispatch(struct ub_dev *sc);
static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd); static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc); static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc);
static void __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd); static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd); static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
static void ub_state_stat_counted(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd); static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd, static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
int stalled_pipe); int stalled_pipe);
...@@ -1085,6 +1155,28 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ...@@ -1085,6 +1155,28 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
ub_state_stat(sc, cmd); ub_state_stat(sc, cmd);
} else if (cmd->state == UB_CMDST_CLRRS) {
if (urb->status == -EPIPE) {
/*
* STALL while clearning STALL.
* The control pipe clears itself - nothing to do.
* XXX Might try to reset the device here and retry.
*/
printk(KERN_NOTICE "%s: stall on control pipe\n",
sc->name);
goto Bad_End;
}
/*
* We ignore the result for the halt clear.
*/
/* reset the endpoint toggle */
usb_settoggle(sc->dev, usb_pipeendpoint(sc->last_pipe),
usb_pipeout(sc->last_pipe), 0);
ub_state_stat_counted(sc, cmd);
} else if (cmd->state == UB_CMDST_CMD) { } else if (cmd->state == UB_CMDST_CMD) {
if (urb->status == -EPIPE) { if (urb->status == -EPIPE) {
rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe); rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
...@@ -1190,52 +1282,57 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ...@@ -1190,52 +1282,57 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
*/ */
goto Bad_End; goto Bad_End;
} }
cmd->state = UB_CMDST_CLEAR;
/*
* Having a stall when getting CSW is an error, so
* make sure uppper levels are not oblivious to it.
*/
cmd->error = -EIO; /* A cheap trick... */
cmd->state = UB_CMDST_CLRRS;
ub_cmdtr_state(sc, cmd); ub_cmdtr_state(sc, cmd);
return; return;
} }
if (urb->status != 0) if (urb->status == -EOVERFLOW) {
goto Bad_End;
if (urb->actual_length == 0) {
/* /*
* Some broken devices add unnecessary zero-length * XXX We are screwed here. Retrying is pointless,
* packets to the end of their data transfers. * because the pipelined data will not get in until
* Such packets show up as 0-length CSWs. If we * we read with a big enough buffer. We must reset XXX.
* encounter such a thing, try to read the CSW again.
*/ */
if (++cmd->stat_count >= 4) {
printk(KERN_NOTICE "%s: unable to get CSW\n",
sc->name);
goto Bad_End; goto Bad_End;
} }
__ub_state_stat(sc, cmd); if (urb->status != 0)
goto Bad_End;
if (urb->actual_length == 0) {
ub_state_stat_counted(sc, cmd);
return; return;
} }
/* /*
* Check the returned Bulk protocol status. * Check the returned Bulk protocol status.
* The status block has to be validated first.
*/ */
bcs = &sc->work_bcs; bcs = &sc->work_bcs;
rc = le32_to_cpu(bcs->Residue);
if (rc != cmd->len - cmd->act_len) { if (sc->signature == cpu_to_le32(0)) {
/* /*
* It is all right to transfer less, the caller has * This is the first reply, so do not perform the check.
* to check. But it's not all right if the device * Instead, remember the signature the device uses
* counts disagree with our counts. * for future checks. But do not allow a nul.
*/ */
/* P3 */ printk("%s: resid %d len %d act %d\n", sc->signature = bcs->Signature;
sc->name, rc, cmd->len, cmd->act_len); if (sc->signature == cpu_to_le32(0)) {
goto Bad_End; ub_state_stat_counted(sc, cmd);
return;
}
} else {
if (bcs->Signature != sc->signature) {
ub_state_stat_counted(sc, cmd);
return;
} }
#if 0
if (bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN) &&
bcs->Signature != cpu_to_le32(US_BULK_CS_OLYMPUS_SIGN)) {
/* Windows ignores signatures, so do we. */
} }
#endif
if (bcs->Tag != cmd->tag) { if (bcs->Tag != cmd->tag) {
/* /*
...@@ -1245,16 +1342,22 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ...@@ -1245,16 +1342,22 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
* commands and reply at commands we timed out before. * commands and reply at commands we timed out before.
* Without flushing these replies we loop forever. * Without flushing these replies we loop forever.
*/ */
if (++cmd->stat_count >= 4) { ub_state_stat_counted(sc, cmd);
printk(KERN_NOTICE "%s: "
"tag mismatch orig 0x%x reply 0x%x\n",
sc->name, cmd->tag, bcs->Tag);
goto Bad_End;
}
__ub_state_stat(sc, cmd);
return; return;
} }
rc = le32_to_cpu(bcs->Residue);
if (rc != cmd->len - cmd->act_len) {
/*
* It is all right to transfer less, the caller has
* to check. But it's not all right if the device
* counts disagree with our counts.
*/
/* P3 */ printk("%s: resid %d len %d act %d\n",
sc->name, rc, cmd->len, cmd->act_len);
goto Bad_End;
}
switch (bcs->Status) { switch (bcs->Status) {
case US_BULK_STAT_OK: case US_BULK_STAT_OK:
break; break;
...@@ -1272,6 +1375,10 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ...@@ -1272,6 +1375,10 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
} }
/* Not zeroing error to preserve a babble indicator */ /* Not zeroing error to preserve a babble indicator */
if (cmd->error != 0) {
ub_state_sense(sc, cmd);
return;
}
cmd->state = UB_CMDST_DONE; cmd->state = UB_CMDST_DONE;
ub_cmdtr_state(sc, cmd); ub_cmdtr_state(sc, cmd);
ub_cmdq_pop(sc); ub_cmdq_pop(sc);
...@@ -1310,7 +1417,7 @@ static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc) ...@@ -1310,7 +1417,7 @@ static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc)
* Factorization helper for the command state machine: * Factorization helper for the command state machine:
* Submit a CSW read. * Submit a CSW read.
*/ */
static void __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd) static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
{ {
int rc; int rc;
...@@ -1328,11 +1435,12 @@ static void __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ...@@ -1328,11 +1435,12 @@ static void __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
/* XXX Clear stalls */ /* XXX Clear stalls */
ub_complete(&sc->work_done); ub_complete(&sc->work_done);
ub_state_done(sc, cmd, rc); ub_state_done(sc, cmd, rc);
return; return -1;
} }
sc->work_timer.expires = jiffies + UB_STAT_TIMEOUT; sc->work_timer.expires = jiffies + UB_STAT_TIMEOUT;
add_timer(&sc->work_timer); add_timer(&sc->work_timer);
return 0;
} }
/* /*
...@@ -1341,13 +1449,34 @@ static void __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ...@@ -1341,13 +1449,34 @@ static void __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
*/ */
static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd) static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
{ {
__ub_state_stat(sc, cmd);
if (__ub_state_stat(sc, cmd) != 0)
return;
cmd->stat_count = 0; cmd->stat_count = 0;
cmd->state = UB_CMDST_STAT; cmd->state = UB_CMDST_STAT;
ub_cmdtr_state(sc, cmd); ub_cmdtr_state(sc, cmd);
} }
/*
* Factorization helper for the command state machine:
* Submit a CSW read and go to STAT state with counter (along [C] path).
*/
static void ub_state_stat_counted(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
{
if (++cmd->stat_count >= 4) {
ub_state_sense(sc, cmd);
return;
}
if (__ub_state_stat(sc, cmd) != 0)
return;
cmd->state = UB_CMDST_STAT;
ub_cmdtr_state(sc, cmd);
}
/* /*
* Factorization helper for the command state machine: * Factorization helper for the command state machine:
* Submit a REQUEST SENSE and go to SENSE state. * Submit a REQUEST SENSE and go to SENSE state.
......
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