github.com/holoplot/go-evdev@v0.0.0-20220721205823-d31c64b9d636/uinput.go (about) 1 package evdev 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "os" 8 "syscall" 9 ) 10 11 const ( 12 uinputMaxNameSize = 80 13 absSize = 64 14 ) 15 16 // CreateDevice creates a device from scratch with the provided capabilities and name 17 // If set up fails the device will be removed from the system, 18 // once set up it can be removed by calling dev.Close 19 func CreateDevice(name string, id InputID, capabilities map[EvType][]EvCode) (*InputDevice, error) { 20 deviceFile, err := os.OpenFile("/dev/uinput", syscall.O_WRONLY|syscall.O_NONBLOCK, 0660) 21 if err != nil { 22 return nil, err 23 } 24 25 newDev := &InputDevice{ 26 file: deviceFile, 27 } 28 29 for ev, codes := range capabilities { 30 if err := ioctlUISETEVBIT(newDev.file.Fd(), uintptr(ev)); err != nil { 31 DestroyDevice(newDev) 32 return nil, fmt.Errorf("failed to set ev bit: %d - %w", ev, err) 33 } 34 35 if err := setEventCodes(newDev, ev, codes); err != nil { 36 DestroyDevice(newDev) 37 return nil, fmt.Errorf("failed to set ev code: %w", err) 38 } 39 } 40 41 if _, err = createInputDevice(newDev.file, UinputUserDevice{ 42 Name: toUinputName([]byte(name)), 43 ID: id, 44 }); err != nil { 45 DestroyDevice(newDev) 46 return nil, fmt.Errorf("failed to create device: %w", err) 47 } 48 49 return newDev, nil 50 } 51 52 // CloneDevice creates a new device from an existing one 53 // all capabilites will be coppied over to the new virtual device 54 // If set up fails the device will be removed from the system, 55 // once set up it can be removed by calling dev.Close 56 func CloneDevice(name string, dev *InputDevice) (*InputDevice, error) { 57 deviceFile, err := os.OpenFile("/dev/uinput", syscall.O_WRONLY|syscall.O_NONBLOCK, 0660) 58 if err != nil { 59 return nil, err 60 } 61 62 newDev := &InputDevice{ 63 file: deviceFile, 64 driverVersion: dev.driverVersion, 65 } 66 67 for _, ev := range dev.CapableTypes() { 68 if err := ioctlUISETEVBIT(newDev.file.Fd(), uintptr(ev)); err != nil { 69 DestroyDevice(newDev) 70 return nil, fmt.Errorf("failed to set ev bit: %d - %w", ev, err) 71 } 72 73 eventCodes := dev.CapableEvents(ev) 74 if err := setEventCodes(newDev, ev, eventCodes); err != nil { 75 DestroyDevice(newDev) 76 return nil, fmt.Errorf("failed to set ev code: %w", err) 77 } 78 } 79 80 id, err := dev.InputID() 81 if err != nil { 82 DestroyDevice(newDev) 83 return nil, fmt.Errorf("failed to get original device id: %w", err) 84 } 85 86 if _, err = createInputDevice(newDev.file, UinputUserDevice{ 87 Name: toUinputName([]byte(name)), 88 ID: id, 89 }); err != nil { 90 return nil, fmt.Errorf("failed to create device: %w", err) 91 } 92 93 return newDev, nil 94 } 95 96 // Destroy destroys an input device, removing it from the system 97 // This is designed to be called on self created virtual devices and may fail if called 98 // on real devices attached to the system 99 func DestroyDevice(dev *InputDevice) error { 100 return ioctlUIDEVDESTROY(dev.file.Fd()) 101 } 102 103 func setEventCodes(dev *InputDevice, ev EvType, codes []EvCode) error { 104 for _, code := range codes { 105 var err error 106 107 switch ev { 108 case EV_ABS: 109 err = ioctlUISETABSBIT(dev.file.Fd(), uintptr(code)) 110 case EV_FF: 111 err = ioctlUISETFFBIT(dev.file.Fd(), uintptr(code)) 112 case EV_KEY: 113 err = ioctlUISETKEYBIT(dev.file.Fd(), uintptr(code)) 114 case EV_LED: 115 err = ioctlUISETLEDBIT(dev.file.Fd(), uintptr(code)) 116 case EV_MSC: 117 err = ioctlUISETMSCBIT(dev.file.Fd(), uintptr(code)) 118 case EV_REL: 119 err = ioctlUISETRELBIT(dev.file.Fd(), uintptr(code)) 120 case EV_SND: 121 err = ioctlUISETSNDBIT(dev.file.Fd(), uintptr(code)) 122 case EV_SW: 123 err = ioctlUISETSWBIT(dev.file.Fd(), uintptr(code)) 124 } 125 126 if err != nil { 127 return err 128 } 129 } 130 131 return nil 132 } 133 134 func toUinputName(name []byte) (uinputName [uinputMaxNameSize]byte) { 135 var fixedSizeName [uinputMaxNameSize]byte 136 copy(fixedSizeName[:], name) 137 138 return fixedSizeName 139 } 140 141 func createInputDevice(file *os.File, dev UinputUserDevice) (fd *os.File, err error) { 142 buf := new(bytes.Buffer) 143 144 if err = binary.Write(buf, binary.LittleEndian, dev); err != nil { 145 file.Close() 146 return nil, fmt.Errorf("failed to write user device buffer: %w", err) 147 } 148 149 if _, err = file.Write(buf.Bytes()); err != nil { 150 file.Close() 151 return nil, fmt.Errorf("failed to write uidev struct to device file: %w", err) 152 } 153 154 if err = ioctlUIDEVCREATE(file.Fd()); err != nil { 155 file.Close() 156 return nil, fmt.Errorf("failed to create device: %w", err) 157 } 158 159 return file, nil 160 }