📄 isd200.c
字号:
US_DEBUGP("usb_stor_bulk_msg() returned %d xferred %d/%d\n", result, partial, length); /* if we stall, we need to clear it before we go on */ if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); usb_stor_clear_halt(us->pusb_dev, pipe); } /* did we send all the data? */ if (partial == length) { US_DEBUGP("isd200_transfer_partial(): transfer complete\n"); return ISD200_TRANSPORT_GOOD; } /* uh oh... we have an error code, so something went wrong. */ if (result) { /* NAK - that means we've retried a few times already */ if (result == -ETIMEDOUT) { US_DEBUGP("isd200_transfer_partial(): device NAKed\n"); return ISD200_TRANSPORT_FAILED; } /* -ENOENT -- we canceled this transfer */ if (result == -ENOENT) { US_DEBUGP("isd200_transfer_partial(): transfer aborted\n"); return ISD200_TRANSPORT_ABORTED; } /* the catch-all case */ US_DEBUGP("isd200_transfer_partial(): unknown error\n"); return ISD200_TRANSPORT_FAILED; } /* no error code, so we must have transferred some data, * just not all of it */ return ISD200_TRANSPORT_SHORT;}/************************************************************************** * Transfer an entire SCSI command's worth of data payload over the bulk * pipe. * * Note that this uses us_transfer_partial to achieve it's goals -- this * function simply determines if we're going to use scatter-gather or not, * and acts appropriately. For now, it also re-interprets the error codes. */static void isd200_transfer( struct us_data *us, Scsi_Cmnd *srb ){ int i; int result = -1; struct scatterlist *sg; unsigned int total_transferred = 0; unsigned int transfer_amount; /* calculate how much we want to transfer */ int dir = srb->sc_data_direction; srb->sc_data_direction = SCSI_DATA_WRITE; transfer_amount = usb_stor_transfer_length(srb); srb->sc_data_direction = dir; /* was someone foolish enough to request more data than available * buffer space? */ if (transfer_amount > srb->request_bufflen) transfer_amount = srb->request_bufflen; /* are we scatter-gathering? */ if (srb->use_sg) { /* loop over all the scatter gather structures and * make the appropriate requests for each, until done */ sg = (struct scatterlist *) srb->request_buffer; for (i = 0; i < srb->use_sg; i++) { /* transfer the lesser of the next buffer or the * remaining data */ if (transfer_amount - total_transferred >= sg[i].length) { result = isd200_transfer_partial(us, srb->sc_data_direction, sg[i].address, sg[i].length); total_transferred += sg[i].length; } else result = isd200_transfer_partial(us, srb->sc_data_direction, sg[i].address, transfer_amount - total_transferred); /* if we get an error, end the loop here */ if (result) break; } } else /* no scatter-gather, just make the request */ result = isd200_transfer_partial(us, srb->sc_data_direction, srb->request_buffer, transfer_amount); /* return the result in the data structure itself */ srb->result = result;}/*********************************************************************** * Transport routines ***********************************************************************//************************************************************************** * ISD200 Bulk Transport * * Note: This routine was copied from the usb_stor_Bulk_transport routine * located in the transport.c source file. The scsi command is limited to * only 12 bytes while the CDB for the ISD200 must be 16 bytes. */int isd200_Bulk_transport( struct us_data *us, Scsi_Cmnd *srb, union ata_cdb *AtaCdb, unsigned char AtaCdbLength ){ struct bulk_cb_wrap bcb; struct bulk_cs_wrap bcs; int result; int pipe; int partial; unsigned int transfer_amount; int dir = srb->sc_data_direction; srb->sc_data_direction = SCSI_DATA_WRITE; transfer_amount = usb_stor_transfer_length(srb); srb->sc_data_direction = dir; /* set up the command wrapper */ bcb.Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb.DataTransferLength = cpu_to_le32(transfer_amount); bcb.Flags = srb->sc_data_direction == SCSI_DATA_READ ? 1 << 7 : 0; bcb.Tag = srb->serial_number; bcb.Lun = srb->cmnd[1] >> 5; if (us->flags & US_FL_SCM_MULT_TARG) bcb.Lun |= srb->target << 4; bcb.Length = AtaCdbLength; /* construct the pipe handle */ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); /* copy the command payload */ memset(bcb.CDB, 0, sizeof(bcb.CDB)); memcpy(bcb.CDB, AtaCdb, bcb.Length); /* send it to out endpoint */ US_DEBUGP("Bulk command S 0x%x T 0x%x Trg %d LUN %d L %d F %d CL %d\n", le32_to_cpu(bcb.Signature), bcb.Tag, (bcb.Lun >> 4), (bcb.Lun & 0xFF), bcb.DataTransferLength, bcb.Flags, bcb.Length); result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN, &partial); US_DEBUGP("Bulk command transfer result=%d\n", result); if (result == -ENOENT) return ISD200_TRANSPORT_ABORTED; else if (result == -EPIPE) { /* if we stall, we need to clear it before we go on */ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); usb_stor_clear_halt(us->pusb_dev, pipe); } else if (result) return ISD200_TRANSPORT_ERROR; /* if the command transfered well, then we go to the data stage */ if (!result && bcb.DataTransferLength) { isd200_transfer(us, srb); US_DEBUGP("Bulk data transfer result 0x%x\n", srb->result); if (srb->result == ISD200_TRANSPORT_ABORTED) return ISD200_TRANSPORT_ABORTED; } /* See flow chart on pg 15 of the Bulk Only Transport spec for * an explanation of how this code works. */ /* construct the pipe handle */ pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); /* get CSW for device status */ US_DEBUGP("Attempting to get CSW...\n"); result = usb_stor_bulk_msg(us, &bcs, pipe, US_BULK_CS_WRAP_LEN, &partial); if (result == -ENOENT) return ISD200_TRANSPORT_ABORTED; /* did the attempt to read the CSW fail? */ if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); usb_stor_clear_halt(us->pusb_dev, pipe); /* get the status again */ US_DEBUGP("Attempting to get CSW (2nd try)...\n"); result = usb_stor_bulk_msg(us, &bcs, pipe, US_BULK_CS_WRAP_LEN, &partial); /* if the command was aborted, indicate that */ if (result == -ENOENT) return ISD200_TRANSPORT_ABORTED; /* if it fails again, we need a reset and return an error*/ if (result == -EPIPE) { US_DEBUGP("clearing halt for pipe 0x%x\n", pipe); usb_stor_clear_halt(us->pusb_dev, pipe); return ISD200_TRANSPORT_ERROR; } } /* if we still have a failure at this point, we're in trouble */ US_DEBUGP("Bulk status result = %d\n", result); if (result) return ISD200_TRANSPORT_ERROR; /* check bulk status */ US_DEBUGP("Bulk status Sig 0x%x T 0x%x R %d Stat 0x%x\n", le32_to_cpu(bcs.Signature), bcs.Tag, bcs.Residue, bcs.Status); if (bcs.Signature != cpu_to_le32(US_BULK_CS_SIGN) || bcs.Tag != bcb.Tag || bcs.Status > US_BULK_STAT_PHASE || partial != 13) { US_DEBUGP("Bulk logical error\n"); return ISD200_TRANSPORT_ERROR; } /* based on the status code, we report good or bad */ switch (bcs.Status) { case US_BULK_STAT_OK: /* command good -- note that we could be short on data */ return ISD200_TRANSPORT_GOOD; case US_BULK_STAT_FAIL: /* command failed */ return ISD200_TRANSPORT_FAILED; case US_BULK_STAT_PHASE: /* phase error */ usb_stor_Bulk_reset(us); return ISD200_TRANSPORT_ERROR; } /* we should never get here, but if we do, we're in trouble */ return ISD200_TRANSPORT_ERROR;}/************************************************************************** * isd200_action * * Routine for sending commands to the isd200 * * RETURNS: * ISD status code */static int isd200_action( struct us_data *us, int action, void* pointer, int value ){ union ata_cdb ata; struct scsi_cmnd srb; struct isd200_info *info = (struct isd200_info *)us->extra; int status; memset(&ata, 0, sizeof(ata)); memset(&srb, 0, sizeof(srb)); ata.generic.SignatureByte0 = info->ConfigData.ATAMajorCommand; ata.generic.SignatureByte1 = info->ConfigData.ATAMinorCommand; ata.generic.TransferBlockSize = 1; switch ( action ) { case ACTION_READ_STATUS: US_DEBUGP(" isd200_action(READ_STATUS)\n"); ata.generic.ActionSelect = ACTION_SELECT_0|ACTION_SELECT_2; ata.read.SelectStatus = 1; ata.read.SelectError = 1; ata.read.SelectCylinderHigh = 1; ata.read.SelectCylinderLow = 1; srb.sc_data_direction = SCSI_DATA_READ; srb.request_buffer = pointer; srb.request_bufflen = value; break; case ACTION_ENUM: US_DEBUGP(" isd200_action(ENUM,0x%02x)\n",value); ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2| ACTION_SELECT_3|ACTION_SELECT_4| ACTION_SELECT_5; ata.write.SelectDeviceHead = 1; ata.write.DeviceHeadByte = value; srb.sc_data_direction = SCSI_DATA_NONE; break; case ACTION_RESET: US_DEBUGP(" isd200_action(RESET)\n"); ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2| ACTION_SELECT_3|ACTION_SELECT_4; ata.write.SelectDeviceControl = 1; ata.write.DeviceControlByte = ATA_DC_RESET_CONTROLLER; srb.sc_data_direction = SCSI_DATA_NONE; break; case ACTION_REENABLE: US_DEBUGP(" isd200_action(REENABLE)\n"); ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_2| ACTION_SELECT_3|ACTION_SELECT_4; ata.write.SelectDeviceControl = 1; ata.write.DeviceControlByte = ATA_DC_REENABLE_CONTROLLER; srb.sc_data_direction = SCSI_DATA_NONE; break; case ACTION_SOFT_RESET: US_DEBUGP(" isd200_action(SOFT_RESET)\n"); ata.generic.ActionSelect = ACTION_SELECT_1|ACTION_SELECT_5; ata.write.SelectDeviceHead = 1; ata.write.DeviceHeadByte = info->DeviceHead; ata.write.SelectCommand = 1; ata.write.CommandByte = WIN_SRST; srb.sc_data_direction = SCSI_DATA_NONE; break; case ACTION_IDENTIFY: US_DEBUGP(" isd200_action(IDENTIFY)\n"); ata.write.SelectCommand = 1; ata.write.CommandByte = WIN_IDENTIFY; srb.sc_data_direction = SCSI_DATA_READ; srb.request_buffer = (void *)&info->drive; srb.request_bufflen = sizeof(struct hd_driveid); break; default: US_DEBUGP("Error: Undefined action %d\n",action); break; } status = isd200_Bulk_transport(us, &srb, &ata, sizeof(ata.generic)); if (status != ISD200_TRANSPORT_GOOD) { US_DEBUGP(" isd200_action(0x%02x) error: %d\n",action,status); status = ISD200_ERROR; /* need to reset device here */ } return status;}/************************************************************************** * isd200_read_regs * * Read ATA Registers * * RETURNS: * ISD status code */ int isd200_read_regs( struct us_data *us ){ struct isd200_info *info = (struct isd200_info *)us->extra; int retStatus = ISD200_GOOD; int transferStatus; US_DEBUGP("Entering isd200_IssueATAReadRegs\n"); transferStatus = isd200_action( us, ACTION_READ_STATUS, info->ATARegs, sizeof(info->ATARegs) ); if (transferStatus != ISD200_TRANSPORT_GOOD) { US_DEBUGP(" Error reading ATA registers\n"); retStatus = ISD200_ERROR; } else { US_DEBUGP(" Got ATA Register[IDE_ERROR_OFFSET] = 0x%x\n", info->ATARegs[IDE_ERROR_OFFSET]); } return retStatus;}/************************************************************************** * 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 recieve the response. */void isd200_invoke_transport( struct us_data *us, Scsi_Cmnd *srb, union ata_cdb *ataCdb ){ int need_auto_sense = 0; int transferStatus; /* send the command to the transport layer */ transferStatus = isd200_Bulk_transport(us, srb, ataCdb, sizeof(ataCdb->generic)); switch (transferStatus) { case ISD200_TRANSPORT_GOOD: /* Indicate a good result */ srb->result = GOOD; break; case ISD200_TRANSPORT_ABORTED: /* if the command gets aborted by the higher layers, we need to * short-circuit all other processing */ US_DEBUGP("-- transport indicates command was aborted\n"); srb->result = DID_ABORT << 16; break; case ISD200_TRANSPORT_FAILED: US_DEBUGP("-- transport indicates command failure\n"); need_auto_sense = 1; break; case ISD200_TRANSPORT_ERROR: US_DEBUGP("-- transport indicates transport failure\n"); srb->result = DID_ERROR << 16; break; case ISD200_TRANSPORT_SHORT: if (!((srb->cmnd[0] == REQUEST_SENSE) || (srb->cmnd[0] == INQUIRY) || (srb->cmnd[0] == MODE_SENSE) || (srb->cmnd[0] == LOG_SENSE) || (srb->cmnd[0] == MODE_SENSE_10))) { US_DEBUGP("-- unexpectedly short transfer\n"); need_auto_sense = 1; } break; default: US_DEBUGP("-- transport indicates unknown failure\n"); srb->result = DID_ERROR << 16; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -