1 // 2 // Copyright (c) 2010 - 2019 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using Antmicro.Renode.Peripherals.Input; 8 using Antmicro.Renode.Core.USB; 9 using Antmicro.Renode.Extensions.Utilities.USBIP; 10 using System.Linq; 11 using Antmicro.Renode.Exceptions; 12 using Antmicro.Renode.Utilities; 13 using Antmicro.Renode.Logging; 14 15 namespace Antmicro.Renode.Peripherals.USB 16 { 17 public static class USBKeyboardExtensions 18 { 19 // 20 // DISCLAIMER: 21 // 22 // Those are helper methods needed because `host` object (part of which `USBIPServer` is) 23 // is not fully supported in monitor/repl. 24 // AttachUSBKeyboard(this USBIPServer usbController, int? port = null)25 public static void AttachUSBKeyboard(this USBIPServer usbController, int? port = null) 26 { 27 if(usbController.Children.Where(m => m.Peripheral.GetType() == typeof(USBKeyboard)).Count() != 0) 28 { 29 throw new RecoverableException("There is already a USB keyboard connected to the USB/IP server"); 30 } 31 32 usbController.Register(new USBKeyboard(), port); 33 } 34 KeyboardType(this USBIPServer usbController, string text)35 public static void KeyboardType(this USBIPServer usbController, string text) 36 { 37 var keyboard = usbController.Children.Where(m => m.Peripheral.GetType() == typeof(USBKeyboard)).Select(m => m.Peripheral).Cast<USBKeyboard>().FirstOrDefault(); 38 if(keyboard == null) 39 { 40 throw new RecoverableException("No USB keyboard attached to the host. Did you forget to call 'host AttachUSBKeyboard'?"); 41 } 42 43 keyboard.TypeText(text); 44 } 45 TypeText(this USBKeyboard keyboard, string text)46 public static void TypeText(this USBKeyboard keyboard, string text) 47 { 48 var inEscapeMode = false; 49 foreach(var character in text) 50 { 51 if(inEscapeMode) 52 { 53 inEscapeMode = false; 54 if(character == 'n') 55 { 56 keyboard.Press(KeyScanCode.Enter); 57 keyboard.Release(KeyScanCode.Enter); 58 continue; 59 } 60 else if(character == '\\') 61 { 62 keyboard.Press(KeyScanCode.OemPipe); 63 keyboard.Release(KeyScanCode.OemPipe); 64 continue; 65 } 66 else 67 { 68 keyboard.Log(LogLevel.Warning, "Unexpected escaped character: {0}", character); 69 keyboard.Press(KeyScanCode.OemPipe); 70 keyboard.Release(KeyScanCode.OemPipe); 71 // intentionally no continue - press the unexpected escaped character 72 } 73 } 74 else 75 { 76 if(character == '\\') 77 { 78 inEscapeMode = true; 79 continue; 80 } 81 } 82 83 var scanCodes = character.ToKeyScanCodes(); 84 foreach(var scanCode in scanCodes) 85 { 86 keyboard.Press(scanCode); 87 } 88 89 foreach(var scanCode in scanCodes) 90 { 91 keyboard.Release(scanCode); 92 } 93 } 94 95 if(inEscapeMode) 96 { 97 // handle dangling `\` at the end of string 98 var scanCodes = '\\'.ToKeyScanCodes(); 99 foreach(var scanCode in scanCodes) 100 { 101 keyboard.Press(scanCode); 102 } 103 104 foreach(var scanCode in scanCodes) 105 { 106 keyboard.Release(scanCode); 107 } 108 } 109 } 110 } 111 112 public class USBKeyboard : IUSBDevice, IKeyboard 113 { USBKeyboard()114 public USBKeyboard() 115 { 116 USBCore = new USBDeviceCore(this) 117 .WithConfiguration(configure: c => 118 c.WithInterface(new Core.USB.HID.Interface(this, 0, 119 subClassCode: (byte)Core.USB.HID.SubclassCode.BootInterfaceSubclass, 120 protocol: (byte)Core.USB.HID.Protocol.Keyboard, 121 reportDescriptor: new Core.USB.HID.ReportDescriptor(ReportHidDescriptor)), 122 configure: i => 123 i.WithEndpoint( 124 Direction.DeviceToHost, 125 EndpointTransferType.Interrupt, 126 maximumPacketSize: 0x4, 127 interval: 0xa, 128 createdEndpoint: out endpoint))); 129 } 130 Reset()131 public void Reset() 132 { 133 USBCore.Reset(); 134 modifiers = 0; 135 pressedKey = 0; 136 } 137 Press(KeyScanCode scanCode)138 public void Press(KeyScanCode scanCode) 139 { 140 this.Log(LogLevel.Noisy, "Pressing {0}", scanCode); 141 if(!UpdateModifiers(scanCode, true)) 142 { 143 pressedKey = (byte)((int)scanCode & 0x7f); 144 } 145 SendPacket(); 146 } 147 Release(KeyScanCode scanCode)148 public void Release(KeyScanCode scanCode) 149 { 150 this.Log(LogLevel.Noisy, "Releasing {0}", scanCode); 151 if(!UpdateModifiers(scanCode, false)) 152 { 153 pressedKey = 0; 154 } 155 SendPacket(); 156 } 157 158 public USBDeviceCore USBCore { get; } 159 UpdateModifiers(KeyScanCode scanCode, bool set)160 private bool UpdateModifiers(KeyScanCode scanCode, bool set) 161 { 162 if(scanCode >= KeyScanCode.CtrlL && scanCode <= KeyScanCode.WinR) 163 { 164 BitHelper.SetBit(ref modifiers, (byte)((int)scanCode & 0x7), set); 165 return true; 166 } 167 return false; 168 } 169 SendPacket()170 private void SendPacket() 171 { 172 using(var p = endpoint.PreparePacket()) 173 { 174 p.Add(modifiers); 175 p.Add(0); // reserved field 176 p.Add(pressedKey); 177 178 // keypresses 2-6 are currently not supported 179 p.Add(0); 180 p.Add(0); 181 p.Add(0); 182 p.Add(0); 183 p.Add(0); 184 } 185 } 186 187 private byte modifiers; 188 private byte pressedKey; 189 private USBEndpoint endpoint; 190 191 private readonly byte[] ReportHidDescriptor = new byte[] 192 { 193 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x75, 0x01, 194 0x95, 0x08, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 195 0x15, 0x00, 0x25, 0x01, 0x81, 0x02, 0x95, 0x01, 196 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, 197 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 198 0x95, 0x01, 0x75, 0x03, 0x91, 0x01, 0x95, 0x06, 199 0x75, 0x08, 0x15, 0x00, 0x25, 0xff, 0x05, 0x07, 200 0x19, 0x00, 0x29, 0xff, 0x81, 0x00, 0xc0 201 }; 202 } 203 } 204