public UsbCbiCommandResult ExecuteCommand(byte[] command, byte[] dataBuffer, int dataIndex, int dataLength, UsbDataDirection dataDirection)
        {
            adsc.wLength = (short)command.Length;
            var commandTransferResult = host.ExecuteControlTransfer(adsc, command, 0, deviceAddress);

            if (commandTransferResult.IsErrorButNotStall)
            {
                return(new UsbCbiCommandResult(commandTransferResult.TransactionResult, 0, null));
            }

            UsbCbiCommandResult ResultOnError(int transferredLength)
            {
                if (command[0] == requestSenseCommand[0])
                {
                    return(new UsbCbiCommandResult(commandTransferResult.TransactionResult, transferredLength, null));
                }

                var requestSenseResult = ExecuteRequestSense();

                if (requestSenseResult.IsErrorResult)
                {
                    return(new UsbCbiCommandResult(commandTransferResult.TransactionResult, transferredLength, null));
                }
                else
                {
                    return(new UsbCbiCommandResult(UsbPacketResult.Ok, transferredLength, requestSenseResult.ToByteArray()));
                }
            }

            if (commandTransferResult.IsError)
            {
                return(ResultOnError(0));
            }

            var transferredDataCount = 0;

            if (dataLength != 0)
            {
                var dataTransferResult = dataDirection == UsbDataDirection.IN ?
                                         host.ExecuteDataInTransfer(dataBuffer, dataIndex, dataLength, deviceAddress, bulkInEndpoint.Number) :
                                         host.ExecuteDataOutTransfer(dataBuffer, dataIndex, dataLength, deviceAddress, bulkOutEndpoint.Number);

                transferredDataCount = dataTransferResult.TransferredDataCount;

                if (dataTransferResult.IsErrorButNotStall)
                {
                    return(new UsbCbiCommandResult(dataTransferResult.TransactionResult, transferredDataCount, null));
                }
                else if (dataTransferResult.IsError)
                {
                    ClearEndpointHalt(dataDirection == UsbDataDirection.IN ? bulkInEndpoint.Number : bulkOutEndpoint.Number);
                    return(ResultOnError(transferredDataCount));
                }
            }

            var intTransferResult = host.ExecuteDataInTransfer(senseCodeBuffer, 0, 2, deviceAddress, interruptEndpoint.Number);

            if (intTransferResult.IsErrorButNotStall)
            {
                return(new UsbCbiCommandResult(intTransferResult.TransactionResult, transferredDataCount, null));
            }
            else if (intTransferResult.IsError)
            {
                ClearEndpointHalt(interruptEndpoint.Number);
                return(ResultOnError(transferredDataCount));
            }
            else if (senseCodeBuffer[0] != 0)
            {
                return(ResultOnError(transferredDataCount)); //Request Sense needed to clear the error condition
            }
            return(new UsbCbiCommandResult(commandTransferResult.TransactionResult, transferredDataCount, senseCodeBuffer.ToArray()));
        }
 public static UsbCbiCommandResult ExecuteCommandWithRetry(this IUsbCbiTransport cbi, byte[] command, byte[] dataBuffer, int dataIndex, int dataLength, UsbDataDirection dataDirection, bool retryOnMediaChanged = true)
 {
     return(ExecuteCommandWithRetry(cbi, command, dataBuffer, dataIndex, dataLength, dataDirection, out bool dummyMediaChanged, retryOnMediaChanged));
 }
        private UsbTransferResult ExecuteDataTransfer(byte[] dataBuffer, int dataBufferIndex, int dataLength, int deviceAddress, int endpointNumber, UsbDataDirection dataDirection)
        {
            if (deviceAddress != UsbDeviceAddress)
            {
                throw new InvalidOperationException($"There's no device connected with address {deviceAddress}");
            }

            if (!connectedDevice.EndpointsByNumber.ContainsKey((byte)endpointNumber))
            {
                throw new InvalidOperationException($"The device with address {deviceAddress} doesn't have an endpoint with number 0x{endpointNumber:X} in any of the interfaces of the current configuration");
            }

            if ((UsbDataDirection)(endpointNumber & 0x80) != dataDirection)
            {
                throw new InvalidOperationException($"Can't perform {dataDirection} transfers on endpoint 0x{endpointNumber:X}, it's not an {dataDirection} endpoint");
            }

            var endpointType = connectedDevice.EndpointsByNumber[(byte)endpointNumber].Type;

            if (endpointType != UsbEndpointType.Bulk && endpointType != UsbEndpointType.Interrupt)
            {
                throw new InvalidOperationException($"Can't perform data transfers on endpoint 0x{endpointNumber:X}, it's a {endpointType} endpoint");
            }

            if (dataDirection == UsbDataDirection.OUT && endpointType == UsbEndpointType.Interrupt)
            {
                throw new InvalidOperationException($"Can't perform OUT data transfers on endpoint 0x{endpointNumber:X}, it's an Interrupt endpoint");
            }

            var endpoint = connectedDevice.EndpointsByNumber[(byte)endpointNumber];

            var result = dataDirection == UsbDataDirection.IN ?
                         hw.ExecuteDataInTransfer(dataBuffer, dataBufferIndex, dataLength, deviceAddress, endpointNumber, endpoint.MaxPacketSize, endpoint.ToggleBit) :
                         hw.ExecuteDataOutTransfer(dataBuffer, dataBufferIndex, dataLength, deviceAddress, endpointNumber, endpoint.MaxPacketSize, endpoint.ToggleBit);

            endpoint.ToggleBit = result.NextTogleBit;

            return(result);
        }
        public static UsbCbiCommandResult ExecuteCommandWithRetry(this IUsbCbiTransport cbi, byte[] command, byte[] dataBuffer, int dataIndex, int dataLength, UsbDataDirection dataDirection, out bool mediaChanged, bool retryOnMediaChanged = true)
        {
            UsbCbiCommandResult result;

            mediaChanged = false;

            while (true)
            {
                result = cbi.ExecuteCommand(command, dataBuffer, dataIndex, dataLength, dataDirection);
                if (!result.IsError || result.SenseData == null)
                {
                    return(result);
                }

                if (result.SenseData == null)
                {
                    Debug.WriteLine("!!! No sense data on int endpoint!");
                }
                else
                {
                    Debug.WriteLine($"!!! ASC: {result.SenseData[0]:X2}h, ASCQ: {result.SenseData[1]:X2}h");
                }

                cbi.ExecuteCommand(requestSenseCommand, requestSenseBuffer, 0, 1, UsbDataDirection.IN);

                var asc  = result.SenseData[0];
                var ascq = result.SenseData[1];

                //4,1 = LOGICAL DRIVE NOT READY - BECOMING READY
                //4,FF = LOGICAL DRIVE NOT READY - DEVICE IS BUSY
                //28 = NOT READY TO READY TRANSITION - MEDIA CHANGED
                //28..2F = UNIT ATTENTION
                if ((asc < 0x28 || asc > 0x2F) && !(asc == 4 && (ascq == 1 || ascq == 0xFF)))
                {
                    return(result);
                }

                if (asc == 0x28)
                {
                    mediaChanged = true;
                    if (!retryOnMediaChanged)
                    {
                        return(result);
                    }
                }
            }
        }