// NOTE: Here we assume that the communication is well-formed, i.e.,
        // the controller does not send two setup packets in a row (without waiting for a response),
        // or a device does not start to respond by itself (without the request from the master).
        // There are some checks verifying it and printing errors, but there is no mechanism enforcing it.
        private void SetupPacketHandler(SetupPacket packet, byte[] additionalData, Action <byte[]> resultCallback)
        {
            this.Log(LogLevel.Noisy, "Received setup packet: {0}", packet.ToString());

            if (setupPacketResultCallback != null)
            {
                this.Log(LogLevel.Error, "Setup packet result handler is set. It means that the previous setup packet handler has not yet finished. Expect problems!");
            }
            setupPacketResultCallback = resultCallback;

            slaveToMasterBuffer.Clear();
            slaveToMasterBufferVirtualBase = 0;
            state = State.SetupTokenReceived;

            var packetBytes = Packet.Encode(packet);

#if DEBUG_PACKETS
            this.Log(LogLevel.Noisy, "Setup packet bytes: [{0}]", Misc.PrettyPrintCollectionHex(packetBytes));
#endif
            EnqueueDataFromMaster(packetBytes);

            // this is a trick:
            // in fact we don't know if the master expects any data from the slave,
            // but we can safely assume so - if there is no data, we should simply
            // receive NAK;
            // without generating this interrupt the slave would never know that
            // it should generate any response and we would be stuck
            endpoint0InPacketPending.Value = true;
            UpdateInterrupts();

            if (additionalData != null)
            {
                masterToSlaveAdditionalDataBuffer.EnqueueRange(additionalData);
            }
        }
예제 #2
0
        private USB.ConfigurationDescriptor ReadConfigurationDescriptor(IUSBDevice device, byte configurationId, out USB.InterfaceDescriptor[] interfaceDescriptors)
        {
            var setupPacket = new SetupPacket
            {
                Recipient = PacketRecipient.Device,
                Type      = PacketType.Standard,
                Direction = Direction.DeviceToHost,
                Request   = (byte)StandardRequest.GetDescriptor,
                Value     = (ushort)(((int)DescriptorType.Configuration << 8) | configurationId),
                Count     = (ushort)Packet.CalculateLength <USB.ConfigurationDescriptor>()
            };
            // first ask for the configuration descriptor non-recursively ...
            var configurationDescriptorBytes = HandleSetupPacketSync(device, setupPacket);
            var result = Packet.Decode <USB.ConfigurationDescriptor>(configurationDescriptorBytes);

            interfaceDescriptors = new USB.InterfaceDescriptor[result.NumberOfInterfaces];
            // ... read the total length of a recursive structure ...
            setupPacket.Count = result.TotalLength;
            // ... and only then read the whole structure again.
            var recursiveBytes = HandleSetupPacketSync(device, setupPacket);

            var currentOffset = Packet.CalculateLength <USB.ConfigurationDescriptor>();

            for (var i = 0; i < interfaceDescriptors.Length; i++)
            {
                interfaceDescriptors[i] = Packet.Decode <USB.InterfaceDescriptor>(recursiveBytes, currentOffset);
                // skip next interface descriptor with associated endpoint descriptors (each of size 7)
                currentOffset += Packet.CalculateLength <USBIP.InterfaceDescriptor>()
                                 + interfaceDescriptors[i].NumberOfEndpoints * 7;
            }

            return(result);
        }
예제 #3
0
        private async Task GetStatus()
        {
            byte[] buffer = new byte[6];

            var setupPacket = new SetupPacket(
                requestType: new UsbDeviceRequestType(
                    RequestDirection.In,
                    RequestType.Class,
                    RequestRecipient.Interface),
                request: DFU_REQ_GETSTATUS,
                length: 6
                );

            var res = await performControlTransferWithRetries(setupPacket, buffer);

            if (res.BytesTransferred != buffer.Length)
            {
                throw new Exception("Error while getting the device's status");
            }

            dfuStatus.bStatus        = res.Data[0];
            dfuStatus.bwPollTimeout  = (res.Data[3] & 0xFF) << 16;
            dfuStatus.bwPollTimeout |= (res.Data[2] & 0xFF) << 8;
            dfuStatus.bwPollTimeout |= (res.Data[1] & 0xFF);
            dfuStatus.bState         = res.Data[4];
        }
예제 #4
0
 public static WINUSB_SETUP_PACKET ToWindowsSetupPacket(this SetupPacket setupPacket)
 => setupPacket == null ? throw new ArgumentNullException(nameof(setupPacket)) : new WINUSB_SETUP_PACKET
 {
     Index       = setupPacket.Length,
     Length      = setupPacket.Length,
     Request     = setupPacket.Request,
     RequestType = setupPacket.RequestType.ToByte(),
     Value       = setupPacket.Value
 };
예제 #5
0
        private async Task <TransferResult> performControlTransferWithRetries(SetupPacket setupPacket, byte[] buffer)
        {
            var retryPolicy = Policy.Handle <ApiException>()
                              .Or <ControlTransferException>()
                              .WaitAndRetryAsync(
                5,
                i => TimeSpan.FromMilliseconds(i * 100)
                );

            return(await retryPolicy.ExecuteAsync(async() => await device.Handle.PerformControlTransferAsync(setupPacket, buffer)));
        }
예제 #6
0
        public override BitStream HandleRequest(SetupPacket packet)
        {
            switch (packet.Type)
            {
            case PacketType.Class:
                return(HandleClassRequest((CdcClassRequest)packet.Request));

            default:
                device.Log(LogLevel.Warning, "Unsupported type: 0x{0:x}", packet.Type);
                return(BitStream.Empty);
            }
        }
예제 #7
0
        private async Task ClearStatus()
        {
            var setupPacket = new SetupPacket(
                requestType: new UsbDeviceRequestType(
                    RequestDirection.In,
                    RequestType.Class,
                    RequestRecipient.Interface),
                request: DFU_REQ_CLEARSTATUS
                );

            await performControlTransferWithRetries(setupPacket, null);
        }
예제 #8
0
        private async Task Reboot()
        {
            var setupPacket = new SetupPacket(
                requestType: new UsbDeviceRequestType(
                    RequestDirection.In,
                    RequestType.Class,
                    RequestRecipient.Interface),
                request: DFU_REQ_DOWNLOAD
                );

            await performControlTransferWithRetries(setupPacket, null);
        }
예제 #9
0
        private BitStream HandleClassRequest(SetupPacket packet)
        {
            switch ((ClassRequests)packet.Request)
            {
            case ClassRequests.GetMaxLUN:
                // If no LUN is associated with the device, the value returned shall be 0.
                return(new BitStream().Append((byte)0));

            default:
                device.Log(LogLevel.Warning, "Unsupported class request: 0x{0:X}", packet.Request);
                return(BitStream.Empty);
            }
        }
예제 #10
0
        private USB.DeviceDescriptor ReadDeviceDescriptor(IUSBDevice device)
        {
            var setupPacket = new SetupPacket
            {
                Recipient = PacketRecipient.Device,
                Type      = PacketType.Standard,
                Direction = Direction.DeviceToHost,
                Request   = (byte)StandardRequest.GetDescriptor,
                Value     = ((int)DescriptorType.Device << 8),
                Count     = (ushort)Packet.CalculateLength <USB.DeviceDescriptor>()
            };

            return(Packet.Decode <USB.DeviceDescriptor>(HandleSetupPacketSync(device, setupPacket)));
        }
예제 #11
0
        // using this blocking helper method simplifies the logic of other methods
        // + it seems to be harmless, as this logic is not executed as a result of
        // intra-emulation communication (where it could lead to deadlocks)
        private byte[] HandleSetupPacketSync(IUSBDevice device, SetupPacket setupPacket)
        {
            byte[] result = null;

            var mre = new ManualResetEvent(false);

            device.USBCore.HandleSetupPacket(setupPacket, received =>
            {
                result = received;
                mre.Set();
            });

            mre.WaitOne();
            return(result);
        }
예제 #12
0
        public override BitStream HandleRequest(SetupPacket packet)
        {
            switch (packet.Type)
            {
            case PacketType.Standard:
                return(HandleStandardRequest(packet.Direction, (StandardRequest)packet.Request, packet.Value));

            case PacketType.Class:
                return(HandleClassRequest(packet.Direction, (HidClassRequest)packet.Request, packet.Value));

            default:
                device.Log(LogLevel.Warning, "Unsupported type: 0x{0:X}", packet.Type);
                return(BitStream.Empty);
            }
        }
예제 #13
0
        public void SetFeature(byte[] buffer)
        {
            var length = buffer.Length; // requires HID report descriptor parsing to implement properly, assume caller is correct for now
            var packet = new SetupPacket()
            {
                bmRequestType = new RequestType(RequestDirection.HostToDevice, RequestInternalType.Class, RequestRecipient.Interface),
                bRequest      = 0x09, // SET_REPORT
                wValue        = (ushort)((buffer[0] & 0xff) | (0x0300)),
                wIndex        = (ushort)interfaceNum,
                wLength       = (ushort)length
            };

            fixed(void *bufferPtr = &buffer[0])
            {
                WinUsb_ControlTransfer(winUsbHandle, packet, bufferPtr, (uint)length, out _, null);
            }
        }
예제 #14
0
        private async Task Command(byte[] buffer)
        {
            var setupPacket = new SetupPacket(
                requestType: new UsbDeviceRequestType(
                    RequestDirection.Out,
                    RequestType.Class,
                    RequestRecipient.Interface),
                request: DFU_REQ_DOWNLOAD,
                length: (ushort)buffer.Length
                );

            var res = await performControlTransferWithRetries(setupPacket, buffer);

            if (res.BytesTransferred != buffer.Length)
            {
                throw new Exception("Error while sending a command to the device");
            }
        }
예제 #15
0
        private async Task Download(int blockNumber, byte[] block)
        {
            var setupPacket = new SetupPacket(
                requestType: new UsbDeviceRequestType(
                    RequestDirection.Out,
                    RequestType.Class,
                    RequestRecipient.Interface),
                request: DFU_REQ_DOWNLOAD,
                length: (ushort)block.Length,
                value: (ushort)blockNumber
                );

            var res = await performControlTransferWithRetries(setupPacket, block);

            if (res.BytesTransferred != block.Length)
            {
                throw new Exception("Error while sending a block to the device");
            }
        }
예제 #16
0
        private USB.ConfigurationDescriptor ReadConfigurationDescriptor(IUSBDevice device, byte configurationId, out USB.InterfaceDescriptor[] interfaceDescriptors)
        {
            var setupPacket = new SetupPacket
            {
                Recipient = PacketRecipient.Device,
                Type      = PacketType.Standard,
                Direction = Direction.DeviceToHost,
                Request   = (byte)StandardRequest.GetDescriptor,
                Value     = (ushort)(((int)DescriptorType.Configuration << 8) | configurationId),
                Count     = (ushort)Packet.CalculateLength <USB.ConfigurationDescriptor>()
            };
            // first ask for the configuration descriptor non-recursively ...
            var configurationDescriptorBytes = HandleSetupPacketSync(device, setupPacket);
            var result = Packet.Decode <USB.ConfigurationDescriptor>(configurationDescriptorBytes);

            interfaceDescriptors = new USB.InterfaceDescriptor[result.NumberOfInterfaces];
            // ... read the total length of a recursive structure ...
            setupPacket.Count = result.TotalLength;
            // ... and only then read the whole structure again.
            var recursiveBytes = HandleSetupPacketSync(device, setupPacket);

            var currentOffset = Packet.CalculateLength <USB.ConfigurationDescriptor>();

            for (var i = 0; i < interfaceDescriptors.Length; i++)
            {
                interfaceDescriptors[i] = Packet.Decode <USB.InterfaceDescriptor>(recursiveBytes, currentOffset);
                if (i != interfaceDescriptors.Length - 1)
                {
                    // skip until the next interface descriptor
                    currentOffset += Packet.CalculateLength <USB.InterfaceDescriptor>();

                    // the second byte of each descriptor contains the type
                    while (recursiveBytes[currentOffset + 1] != (byte)DescriptorType.Interface)
                    {
                        // the first byte of each descriptor contains the length in bytes
                        currentOffset += recursiveBytes[currentOffset];
                    }
                }
            }

            return(result);
        }
예제 #17
0
        public unsafe string GetDeviceString(byte index)
        {
            return(WithHandle(winUsbHandle =>
            {
                var packet = SetupPacket.MakeGetStringDescriptor(index);
                var buffer = ArrayPool <byte> .Shared.Rent(StringDescriptor.MaxSize);

                fixed(void *bufferPtr = &buffer[0])
                {
                    if (!WinUsb_ControlTransfer(winUsbHandle, packet, bufferPtr, StringDescriptor.MaxSize, out var descriptorLength, null))
                    {
                        ArrayPool <byte> .Shared.Return(buffer);
                        return null;
                    }

                    ArrayPool <byte> .Shared.Return(buffer);
                    return StringDescriptor.GetString((StringDescriptor *)bufferPtr);
                }
            }));
        }
예제 #18
0
        /// <summary>
        /// This is the low level call to do a control transfer at the Android level. This can be overriden in the contructor
        /// </summary>
        private static Task <TransferResult> PerformControlTransferAndroid(
            UsbDeviceConnection usbDeviceConnection,
            SetupPacket setupPacket,
            byte[] buffer = null,
            int?timeout   = null,
            CancellationToken cancellationToken = default)
        =>
        //Use Task.Run so we can pass the cancellation token in instead of using the async control transfer method which doesn't have a cancellation token
        Task.Run(() =>
        {
            var bytesTransferred = usbDeviceConnection.ControlTransfer(
                setupPacket.RequestType.Direction == RequestDirection.In ? UsbAddressing.In : UsbAddressing.Out,
                setupPacket.Request,
                setupPacket.Value,
                setupPacket.Index,
                buffer,
                setupPacket.Length,
                timeout ?? 0
                );

            return(new TransferResult(buffer, (uint)bytesTransferred));
        }, cancellationToken);
예제 #19
0
 public static unsafe extern bool WinUsb_ControlTransfer(SafeWinUsbInterfaceHandle interfaceHandle, SetupPacket setupPacket, void *pBuffer, uint bufferLength, out uint lengthTransferred, NativeOverlapped *pOverlapped);
예제 #20
0
        public unsafe WinUSBInterface(string devicePath)
        {
            DevicePath = devicePath;
            WithHandle(winUsbHandle =>
            {
                var packet = SetupPacket.MakeGetDescriptor(
                    RequestInternalType.Standard,
                    RequestRecipient.Device,
                    DescriptorType.Device, 0,
                    (ushort)sizeof(DeviceDescriptor)
                    );

                var deviceDescriptor = new DeviceDescriptor();
                void *descriptorPtr  = &deviceDescriptor;

                if (!WinUsb_ControlTransfer(winUsbHandle, packet, descriptorPtr, (uint)sizeof(DeviceDescriptor), out _, null))
                {
                    throw new IOException("Failed to retrieve device descriptor");
                }

                var interfaceDescriptor = new InterfaceDescriptor();
                if (!WinUsb_QueryInterfaceSettings(winUsbHandle, 0, &interfaceDescriptor))
                {
                    throw new IOException("Failed to get interface descriptor");
                }

                InterfaceNum = interfaceDescriptor.bInterfaceNumber;

                for (byte i = 0; i < interfaceDescriptor.bNumEndpoints; i++)
                {
                    var b = i; // Workaround CoreCLR bug
                    if (!WinUsb_QueryPipe(winUsbHandle, 0, i, out var pipeInfo))
                    {
                        throw new IOException("Failed to get pipe information");
                    }
                    i = b;

                    var direction = (PipeDirection)((pipeInfo.PipeID >> 7) & 1);
                    switch (direction)
                    {
                    case PipeDirection.Out:
                        if (OutputPipe.HasValue)
                        {
                            throw new IOException("WinUSB HID device unexpectedly have more than one output endpoint on the same interface");
                        }

                        OutputPipe         = pipeInfo.PipeID;
                        OutputReportLength = pipeInfo.MaximumPacketSize;
                        break;

                    case PipeDirection.In:
                        if (InputPipe.HasValue)
                        {
                            throw new IOException("WinUSB HID device unexpectedly have more than one input endpoint on the same interface");
                        }

                        InputPipe         = pipeInfo.PipeID;
                        InputReportLength = pipeInfo.MaximumPacketSize;
                        break;
                    }
                }

                ProductID = deviceDescriptor.idProduct;
                VendorID  = deviceDescriptor.idVendor;

                Manufacturer = deviceDescriptor.iManufacturer != 0
                    ? GetDeviceString(deviceDescriptor.iManufacturer)
                    : "Unknown Manufacturer";

                ProductName = deviceDescriptor.iProduct != 0
                    ? GetDeviceString(deviceDescriptor.iProduct)
                    : "Unknown Product Name";

                SerialNumber = deviceDescriptor.iSerialNumber != 0
                    ? GetDeviceString(deviceDescriptor.iSerialNumber)
                    : "Unknown Serial Number";
            });
        }