internal static List <ReadyToTransferPDU> GetReadyToTransferPDUs(SCSICommandPDU command, ConnectionParameters connection, out List <SCSICommandPDU> commandsToExecute) { // We return either SCSIResponsePDU or List<SCSIDataInPDU> List <ReadyToTransferPDU> responseList = new List <ReadyToTransferPDU>(); commandsToExecute = new List <SCSICommandPDU>(); ISCSISession session = connection.Session; if (command.Write && command.DataSegmentLength < command.ExpectedDataTransferLength) { uint transferTag = session.GetNextTransferTag(); // Create buffer for next segments (we only execute the command after receiving all of its data) Array.Resize <byte>(ref command.Data, (int)command.ExpectedDataTransferLength); // Send R2Ts: uint bytesLeft = command.ExpectedDataTransferLength - command.DataSegmentLength; uint nextOffset = command.DataSegmentLength; if (!session.InitialR2T) { uint firstDataPDULength = Math.Min((uint)session.FirstBurstLength, command.ExpectedDataTransferLength) - command.DataSegmentLength; bytesLeft -= firstDataPDULength; nextOffset += firstDataPDULength; } int totalR2Ts = (int)Math.Ceiling((double)bytesLeft / connection.TargetMaxRecvDataSegmentLength); int outgoingR2Ts = Math.Min(session.MaxOutstandingR2T, totalR2Ts); for (uint index = 0; index < outgoingR2Ts; index++) { ReadyToTransferPDU response = new ReadyToTransferPDU(); response.InitiatorTaskTag = command.InitiatorTaskTag; response.R2TSN = index; // R2Ts are sequenced per command and must start with 0 for each new command; response.TargetTransferTag = transferTag; response.BufferOffset = nextOffset; response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, command.ExpectedDataTransferLength - response.BufferOffset); responseList.Add(response); nextOffset += (uint)connection.TargetMaxRecvDataSegmentLength; } connection.AddTransfer(transferTag, command, (uint)outgoingR2Ts, nextOffset, (uint)totalR2Ts); session.CommandsInTransfer.Add(command.CmdSN); return(responseList); } if (session.IsPrecedingCommandPending(command.CmdSN)) { session.DelayedCommands.Add(command); } else { commandsToExecute.Add(command); } return(responseList); }
internal static List <ISCSIPDU> GetSCSIResponsePDU(SCSICommandPDU command, ISCSITarget target, SessionParameters session, ConnectionParameters connection) { // We return either SCSIResponsePDU or List<SCSIDataInPDU> List <ISCSIPDU> responseList = new List <ISCSIPDU>(); string connectionIdentifier = StateObject.GetConnectionIdentifier(session, connection); if (command.Write && command.DataSegmentLength < command.ExpectedDataTransferLength) { uint transferTag = session.GetNextTransferTag(); // Store segment (we only execute the command after receiving all of its data) byte[] commandDataBuffer = new byte[command.ExpectedDataTransferLength]; Array.Copy(command.Data, 0, commandDataBuffer, 0, command.DataSegmentLength); // Send R2T ReadyToTransferPDU response = new ReadyToTransferPDU(); response.InitiatorTaskTag = command.InitiatorTaskTag; response.R2TSN = 0; // R2Ts are sequenced per command and must start with 0 for each new command; response.TargetTransferTag = transferTag; response.BufferOffset = command.DataSegmentLength; response.DesiredDataTransferLength = Math.Min((uint)connection.TargetMaxRecvDataSegmentLength, command.ExpectedDataTransferLength - response.BufferOffset); connection.AddTransfer(transferTag, command.CommandDescriptorBlock, commandDataBuffer, 1); responseList.Add(response); return(responseList); } byte[] scsiResponse; SCSIStatusCodeName status = target.ExecuteCommand(command.CommandDescriptorBlock, command.LUN, command.Data, out scsiResponse); if (!command.Read || status != SCSIStatusCodeName.Good) { // RFC 3720: if the command is completed with an error, then the response and sense data MUST be sent in a SCSI Response PDU SCSIResponsePDU response = new SCSIResponsePDU(); response.InitiatorTaskTag = command.InitiatorTaskTag; response.Status = status; response.Data = scsiResponse; if (command.Read) { EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength); } responseList.Add(response); } else if (scsiResponse.Length <= connection.InitiatorMaxRecvDataSegmentLength) { SCSIDataInPDU response = new SCSIDataInPDU(); response.InitiatorTaskTag = command.InitiatorTaskTag; response.Status = status; response.StatusPresent = true; response.Final = true; response.Data = scsiResponse; EnforceExpectedDataTransferLength(response, command.ExpectedDataTransferLength); responseList.Add(response); } else // we have to split the response to multiple Data-In PDUs { int bytesLeftToSend = scsiResponse.Length; uint dataSN = 0; while (bytesLeftToSend > 0) { int dataSegmentLength = Math.Min(connection.InitiatorMaxRecvDataSegmentLength, bytesLeftToSend); int dataOffset = scsiResponse.Length - bytesLeftToSend; SCSIDataInPDU response = new SCSIDataInPDU(); response.InitiatorTaskTag = command.InitiatorTaskTag; if (bytesLeftToSend == dataSegmentLength) { // last Data-In PDU response.Status = status; response.StatusPresent = true; response.Final = true; } response.BufferOffset = (uint)dataOffset; response.DataSN = dataSN; dataSN++; response.Data = new byte[dataSegmentLength]; Array.Copy(scsiResponse, dataOffset, response.Data, 0, dataSegmentLength); responseList.Add(response); bytesLeftToSend -= dataSegmentLength; } } return(responseList); }