// 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); } }
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); }
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]; }
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 };
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))); }
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); } }
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); }
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); }
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); } }
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))); }
// 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); }
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); } }
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); } }
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"); } }
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"); } }
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); }
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); } })); }
/// <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);
public static unsafe extern bool WinUsb_ControlTransfer(SafeWinUsbInterfaceHandle interfaceHandle, SetupPacket setupPacket, void *pBuffer, uint bufferLength, out uint lengthTransferred, NativeOverlapped *pOverlapped);
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"; }); }