📄 ub.c
字号:
break; cmd->error = rc; cmd->state = UB_CMDST_DONE; } else { if (!ub_is_completed(&sc->work_done)) break; del_timer(&sc->work_timer); ub_scsi_urb_compl(sc, cmd); } }}static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd){ struct urb *urb = &sc->work_urb; struct bulk_cs_wrap *bcs; int len; int rc; if (atomic_read(&sc->poison)) { ub_state_done(sc, cmd, -ENODEV); return; } if (cmd->state == UB_CMDST_CLEAR) { if (urb->status == -EPIPE) { /* * STALL while clearning STALL. * The control pipe clears itself - nothing to do. */ 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_sense(sc, cmd); } else if (cmd->state == UB_CMDST_CLR2STS) { if (urb->status == -EPIPE) { 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(sc, cmd); } else if (cmd->state == UB_CMDST_CLRRS) { if (urb->status == -EPIPE) { 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) { switch (urb->status) { case 0: break; case -EOVERFLOW: goto Bad_End; case -EPIPE: rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe); if (rc != 0) { printk(KERN_NOTICE "%s: " "unable to submit clear (%d)\n", sc->name, rc); /* * This is typically ENOMEM or some other such shit. * Retrying is pointless. Just do Bad End on it... */ ub_state_done(sc, cmd, rc); return; } cmd->state = UB_CMDST_CLEAR; return; case -ESHUTDOWN: /* unplug */ case -EILSEQ: /* unplug timeout on uhci */ ub_state_done(sc, cmd, -ENODEV); return; default: goto Bad_End; } if (urb->actual_length != US_BULK_CB_WRAP_LEN) { goto Bad_End; } if (cmd->dir == UB_DIR_NONE || cmd->nsg < 1) { ub_state_stat(sc, cmd); return; } // udelay(125); // usb-storage has this ub_data_start(sc, cmd); } else if (cmd->state == UB_CMDST_DATA) { if (urb->status == -EPIPE) { rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe); if (rc != 0) { printk(KERN_NOTICE "%s: " "unable to submit clear (%d)\n", sc->name, rc); ub_state_done(sc, cmd, rc); return; } cmd->state = UB_CMDST_CLR2STS; return; } if (urb->status == -EOVERFLOW) { /* * A babble? Failure, but we must transfer CSW now. */ cmd->error = -EOVERFLOW; /* A cheap trick... */ ub_state_stat(sc, cmd); return; } if (cmd->dir == UB_DIR_WRITE) { /* * Do not continue writes in case of a failure. * Doing so would cause sectors to be mixed up, * which is worse than sectors lost. * * We must try to read the CSW, or many devices * get confused. */ len = urb->actual_length; if (urb->status != 0 || len != cmd->sgv[cmd->current_sg].length) { cmd->act_len += len; cmd->error = -EIO; ub_state_stat(sc, cmd); return; } } else { /* * If an error occurs on read, we record it, and * continue to fetch data in order to avoid bubble. * * As a small shortcut, we stop if we detect that * a CSW mixed into data. */ if (urb->status != 0) cmd->error = -EIO; len = urb->actual_length; if (urb->status != 0 || len != cmd->sgv[cmd->current_sg].length) { if ((len & 0x1FF) == US_BULK_CS_WRAP_LEN) goto Bad_End; } } cmd->act_len += urb->actual_length; if (++cmd->current_sg < cmd->nsg) { ub_data_start(sc, cmd); return; } ub_state_stat(sc, cmd); } else if (cmd->state == UB_CMDST_STAT) { if (urb->status == -EPIPE) { rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe); if (rc != 0) { printk(KERN_NOTICE "%s: " "unable to submit clear (%d)\n", sc->name, rc); ub_state_done(sc, cmd, rc); return; } /* * 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; return; } /* Catch everything, including -EOVERFLOW and other nasties. */ if (urb->status != 0) goto Bad_End; if (urb->actual_length == 0) { ub_state_stat_counted(sc, cmd); return; } /* * Check the returned Bulk protocol status. * The status block has to be validated first. */ bcs = &sc->work_bcs; if (sc->signature == cpu_to_le32(0)) { /* * This is the first reply, so do not perform the check. * Instead, remember the signature the device uses * for future checks. But do not allow a nul. */ sc->signature = bcs->Signature; if (sc->signature == cpu_to_le32(0)) { ub_state_stat_counted(sc, cmd); return; } } else { if (bcs->Signature != sc->signature) { ub_state_stat_counted(sc, cmd); return; } } if (bcs->Tag != cmd->tag) { /* * This usually happens when we disagree with the * device's microcode about something. For instance, * a few of them throw this after timeouts. They buffer * commands and reply at commands we timed out before. * Without flushing these replies we loop forever. */ ub_state_stat_counted(sc, cmd); return; } len = le32_to_cpu(bcs->Residue); if (len != 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. */ goto Bad_End; } switch (bcs->Status) { case US_BULK_STAT_OK: break; case US_BULK_STAT_FAIL: ub_state_sense(sc, cmd); return; case US_BULK_STAT_PHASE: goto Bad_End; default: printk(KERN_INFO "%s: unknown CSW status 0x%x\n", sc->name, bcs->Status); ub_state_done(sc, cmd, -EINVAL); return; } /* Not zeroing error to preserve a babble indicator */ if (cmd->error != 0) { ub_state_sense(sc, cmd); return; } cmd->state = UB_CMDST_DONE; ub_cmdq_pop(sc); (*cmd->done)(sc, cmd); } else if (cmd->state == UB_CMDST_SENSE) { ub_state_done(sc, cmd, -EIO); } else { printk(KERN_WARNING "%s: " "wrong command state %d\n", sc->name, cmd->state); ub_state_done(sc, cmd, -EINVAL); return; } return;Bad_End: /* Little Excel is dead */ ub_state_done(sc, cmd, -EIO);}/* * Factorization helper for the command state machine: * Initiate a data segment transfer. */static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd){ struct scatterlist *sg = &cmd->sgv[cmd->current_sg]; int pipe; int rc; UB_INIT_COMPLETION(sc->work_done); if (cmd->dir == UB_DIR_READ) pipe = sc->recv_bulk_pipe; else pipe = sc->send_bulk_pipe; sc->last_pipe = pipe; usb_fill_bulk_urb(&sc->work_urb, sc->dev, pipe, sg_virt(sg), sg->length, ub_urb_complete, sc); sc->work_urb.actual_length = 0; sc->work_urb.error_count = 0; sc->work_urb.status = 0; if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { /* XXX Clear stalls */ ub_complete(&sc->work_done); ub_state_done(sc, cmd, rc); return; } sc->work_timer.expires = jiffies + UB_DATA_TIMEOUT; add_timer(&sc->work_timer); cmd->state = UB_CMDST_DATA;}/* * Factorization helper for the command state machine: * Finish the command. */static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc){ cmd->error = rc; cmd->state = UB_CMDST_DONE; ub_cmdq_pop(sc); (*cmd->done)(sc, cmd);}/* * Factorization helper for the command state machine: * Submit a CSW read. */static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd){ int rc; UB_INIT_COMPLETION(sc->work_done); sc->last_pipe = sc->recv_bulk_pipe; usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->recv_bulk_pipe, &sc->work_bcs, US_BULK_CS_WRAP_LEN, ub_urb_complete, sc); sc->work_urb.actual_length = 0; sc->work_urb.error_count = 0; sc->work_urb.status = 0; if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { /* XXX Clear stalls */ ub_complete(&sc->work_done); ub_state_done(sc, cmd, rc); return -1; } sc->work_timer.expires = jiffies + UB_STAT_TIMEOUT; add_timer(&sc->work_timer); return 0;}/* * Factorization helper for the command state machine: * Submit a CSW read and go to STAT state. */static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd){ if (__ub_state_stat(sc, cmd) != 0) return; cmd->stat_count = 0; cmd->state = UB_CMDST_STAT;}/* * 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;}/* * Factorization helper for the command state machine: * Submit a REQUEST SENSE and go to SENSE state. */static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd){ struct ub_scsi_cmd *scmd; struct scatterlist *sg; int rc; if (cmd->cdb[0] == REQUEST_SENSE) { rc = -EPIPE; goto error; } scmd = &sc->top_rqs_cmd; memset(scmd, 0, sizeof(struct ub_scsi_cmd)); scmd->cdb[0] = REQUEST_SENSE; scmd->cdb[4] = UB_SENSE_SIZE; scmd->cdb_len = 6; scmd->dir = UB_DIR_READ; scmd->state = UB_CMDST_INIT; scmd->nsg = 1; sg = &scmd->sgv[0]; sg_init_table(sg, UB_MAX_REQ_SG); sg_set_page(sg, virt_to_page(sc->top_sense), UB_SENSE_SIZE, (unsigned long)sc->top_sense & (PAGE_SIZE-1)); scmd->len = UB_SENSE_SIZE; scmd->lun = cmd->lun; scmd->done = ub_top_sense_done; scmd->back = cmd; scmd->tag = sc->tagcnt++; cmd->state = UB_CMDST_SENSE; ub_cmdq_insert(sc, scmd); return;error: ub_state_done(sc, cmd, rc);}/* * A helper for the command's state machine: * Submit a stall clear. */static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int stalled_pipe){ int endp; struct usb_ctrlrequest *cr; int rc; endp = usb_pipeendpoint(stalled_pipe); if (usb_pipein (stalled_pipe)) endp |= USB_DIR_IN; cr = &sc->work_cr; cr->bRequestType = USB_RECIP_ENDPOINT; cr->bRequest = USB_REQ_CLEAR_FEATURE; cr->wValue = cpu_to_le16(USB_ENDPOINT_HALT); cr->wIndex = cpu_to_le16(endp); cr->wLength = cpu_to_le16(0); UB_INIT_COMPLETION(sc->work_done); usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe, (unsigned char*) cr, NULL, 0, ub_urb_complete, sc); sc->work_urb.actual_length = 0; sc->work_urb.error_count = 0; sc->work_urb.status = 0; if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) { ub_complete(&sc->work_done); return rc; } sc->work_timer.expires = jiffies + UB_CTRL_TIMEOUT; add_timer(&sc->work_timer); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -