📄 ub.c
字号:
bcb->Lun = (cmd->lun != NULL) ? cmd->lun->num : 0; bcb->Length = cmd->cdb_len; /* copy the command payload */ memcpy(bcb->CDB, cmd->cdb, UB_MAX_CDB_SIZE); UB_INIT_COMPLETION(sc->work_done); sc->last_pipe = sc->send_bulk_pipe; usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->send_bulk_pipe, bcb, US_BULK_CB_WRAP_LEN, ub_urb_complete, sc); /* Fill what we shouldn't be filling, because usb-storage did so. */ 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); return rc; } sc->work_timer.expires = jiffies + UB_URB_TIMEOUT; add_timer(&sc->work_timer); cmd->state = UB_CMDST_CMD; ub_cmdtr_state(sc, cmd); return 0;}/* * Timeout handler. */static void ub_urb_timeout(unsigned long arg){ struct ub_dev *sc = (struct ub_dev *) arg; unsigned long flags; spin_lock_irqsave(&sc->lock, flags); usb_unlink_urb(&sc->work_urb); spin_unlock_irqrestore(&sc->lock, flags);}/* * Completion routine for the work URB. * * This can be called directly from usb_submit_urb (while we have * the sc->lock taken) and from an interrupt (while we do NOT have * the sc->lock taken). Therefore, bounce this off to a tasklet. */static void ub_urb_complete(struct urb *urb, struct pt_regs *pt){ struct ub_dev *sc = urb->context; ub_complete(&sc->work_done); tasklet_schedule(&sc->tasklet);}static void ub_scsi_action(unsigned long _dev){ struct ub_dev *sc = (struct ub_dev *) _dev; unsigned long flags; spin_lock_irqsave(&sc->lock, flags); del_timer(&sc->work_timer); ub_scsi_dispatch(sc); spin_unlock_irqrestore(&sc->lock, flags);}static void ub_scsi_dispatch(struct ub_dev *sc){ struct ub_scsi_cmd *cmd; int rc; while ((cmd = ub_cmdq_peek(sc)) != NULL) { if (cmd->state == UB_CMDST_DONE) { ub_cmdq_pop(sc); (*cmd->done)(sc, cmd); } else if (cmd->state == UB_CMDST_INIT) { ub_cmdtr_new(sc, cmd); if ((rc = ub_scsi_cmd_start(sc, cmd)) == 0) break; cmd->error = rc; cmd->state = UB_CMDST_DONE; ub_cmdtr_state(sc, cmd); } else { if (!ub_is_completed(&sc->work_done)) break; 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 rc; if (atomic_read(&sc->poison)) { /* A little too simplistic, I feel... */ goto Bad_End; } if (cmd->state == UB_CMDST_CLEAR) { 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_sense(sc, cmd); } else if (cmd->state == UB_CMDST_CLR2STS) { 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(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) { 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); /* * This is typically ENOMEM or some other such shit. * Retrying is pointless. Just do Bad End on it... */ goto Bad_End; } cmd->state = UB_CMDST_CLEAR; ub_cmdtr_state(sc, cmd); return; } if (urb->status != 0) { goto Bad_End; } if (urb->actual_length != US_BULK_CB_WRAP_LEN) { /* XXX Must do reset here to unconfuse the device */ 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); /* * This is typically ENOMEM or some other such shit. * Retrying is pointless. Just do Bad End on it... */ goto Bad_End; } cmd->state = UB_CMDST_CLR2STS; ub_cmdtr_state(sc, cmd); return; } if (urb->status == -EOVERFLOW) { /* * A babble? Failure, but we must transfer CSW now. * XXX This is going to end in perpetual babble. Reset. */ cmd->error = -EOVERFLOW; /* A cheap trick... */ ub_state_stat(sc, cmd); return; } if (urb->status != 0) goto Bad_End; cmd->act_len += urb->actual_length; ub_cmdtr_act_len(sc, cmd); 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); /* * This is typically ENOMEM or some other such shit. * Retrying is pointless. Just do Bad End on it... */ goto Bad_End; } /* * 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); return; } if (urb->status == -EOVERFLOW) { /* * XXX We are screwed here. Retrying is pointless, * because the pipelined data will not get in until * we read with a big enough buffer. We must reset XXX. */ goto Bad_End; } 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; } 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) { case US_BULK_STAT_OK: break; case US_BULK_STAT_FAIL: ub_state_sense(sc, cmd); return; case US_BULK_STAT_PHASE: /* XXX We must reset the transport here */ /* P3 */ printk("%s: status PHASE\n", sc->name); goto Bad_End; default: printk(KERN_INFO "%s: unknown CSW status 0x%x\n", sc->name, bcs->Status); goto Bad_End; } /* Not zeroing error to preserve a babble indicator */ if (cmd->error != 0) { ub_state_sense(sc, cmd); return; } cmd->state = UB_CMDST_DONE; ub_cmdtr_state(sc, cmd); 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); goto Bad_End; } 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, page_address(sg->page) + sg->offset, 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; ub_cmdtr_state(sc, cmd);}/* * 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_cmdtr_state(sc, cmd); 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; 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: * 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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -