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  }