blob: 35feea1930a9ecf20b048592e9029600ad1ea19f [file] [log] [blame]
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! Wrappers for the Linux evdev APIs.
use std::fs::File;
use std::io;
use std::mem;
use std::os::fd::{AsRawFd, OwnedFd};
use std::path::Path;
use libc::c_int;
use nix::sys::time::TimeVal;
pub const SYN_CNT: usize = 0x10;
pub const KEY_CNT: usize = 0x300;
pub const REL_CNT: usize = 0x10;
pub const ABS_CNT: usize = 0x40;
pub const MSC_CNT: usize = 0x08;
pub const SW_CNT: usize = 0x11;
pub const LED_CNT: usize = 0x10;
pub const SND_CNT: usize = 0x08;
pub const REP_CNT: usize = 0x02;
// Disable naming warnings, as these are supposed to match the EV_ constants in input-event-codes.h.
#[allow(non_camel_case_types)]
// Some of these types aren't referenced for evemu purposes, but are included for completeness.
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u16)]
pub enum EventType {
SYN = 0x00,
KEY = 0x01,
REL = 0x02,
ABS = 0x03,
MSC = 0x04,
SW = 0x05,
LED = 0x11,
SND = 0x12,
REP = 0x14,
FF = 0x15,
PWR = 0x16,
FF_STATUS = 0x17,
}
impl EventType {
fn code_count(&self) -> usize {
match self {
EventType::SYN => SYN_CNT,
EventType::KEY => KEY_CNT,
EventType::REL => REL_CNT,
EventType::ABS => ABS_CNT,
EventType::MSC => MSC_CNT,
EventType::SW => SW_CNT,
EventType::LED => LED_CNT,
EventType::SND => SND_CNT,
EventType::REP => REP_CNT,
_ => {
panic!("Event type {self:?} does not have a defined code count.");
}
}
}
}
pub const EVENT_TYPES_WITH_BITMAPS: [EventType; 7] = [
EventType::KEY,
EventType::REL,
EventType::ABS,
EventType::MSC,
EventType::SW,
EventType::LED,
EventType::SND,
];
const INPUT_PROP_CNT: usize = 32;
/// The `ioctl_*!` macros create public functions by default, so this module makes them private.
mod ioctl {
use nix::{ioctl_read, ioctl_read_buf};
ioctl_read!(eviocgid, b'E', 0x02, super::DeviceId);
ioctl_read_buf!(eviocgname, b'E', 0x06, u8);
ioctl_read_buf!(eviocgprop, b'E', 0x09, u8);
}
#[derive(Default)]
#[repr(C)]
pub struct DeviceId {
pub bus_type: u16,
pub vendor: u16,
pub product: u16,
pub version: u16,
}
#[derive(Default)]
#[repr(C)]
pub struct AbsoluteAxisInfo {
pub value: i32,
pub minimum: i32,
pub maximum: i32,
pub fuzz: i32,
pub flat: i32,
pub resolution: i32,
}
#[repr(C)]
pub struct InputEvent {
pub time: TimeVal,
pub type_: u16,
pub code: u16,
pub value: i32,
}
impl InputEvent {
pub fn offset_time_by(&self, offset: TimeVal) -> InputEvent {
InputEvent { time: self.time - offset, ..*self }
}
}
impl Default for InputEvent {
fn default() -> Self {
InputEvent { time: TimeVal::new(0, 0), type_: 0, code: 0, value: 0 }
}
}
/// An object representing an input device using Linux's evdev protocol.
pub struct Device {
fd: OwnedFd,
}
/// # Safety
///
/// `ioctl` must be safe to call with the given file descriptor and a pointer to a buffer of
/// `initial_buf_size` `u8`s.
unsafe fn buf_from_ioctl(
ioctl: unsafe fn(c_int, &mut [u8]) -> nix::Result<c_int>,
fd: &OwnedFd,
initial_buf_size: usize,
) -> Result<Vec<u8>, nix::errno::Errno> {
let mut buf = vec![0; initial_buf_size];
// SAFETY:
// Here we're relying on the safety guarantees for `ioctl` made by the caller.
match unsafe { ioctl(fd.as_raw_fd(), buf.as_mut_slice()) } {
Ok(len) if len < 0 => {
panic!("ioctl returned invalid length {len}");
}
Ok(len) => {
buf.truncate(len as usize);
Ok(buf)
}
Err(err) => Err(err),
}
}
impl Device {
/// Opens a device from the evdev node at the given path.
pub fn open(path: &Path) -> io::Result<Device> {
Ok(Device { fd: OwnedFd::from(File::open(path)?) })
}
/// Returns the name of the device, as set by the relevant kernel driver.
pub fn name(&self) -> Result<String, nix::errno::Errno> {
// There's no official maximum length for evdev device names. The Linux HID driver
// currently supports names of at most 151 bytes (128 from the device plus a suffix of up
// to 23 bytes). 256 seems to be the buffer size most commonly used in evdev bindings, so
// we use it here.
//
// SAFETY:
// We know that fd is a valid file descriptor as it comes from a File that we have open.
//
// The ioctl_read_buf macro prevents the retrieved data from overflowing the buffer created
// by buf_from_ioctl by passing in the size to the ioctl, meaning that the kernel's
// str_to_user function will truncate the string to that length.
let mut buf = unsafe { buf_from_ioctl(ioctl::eviocgname, &self.fd, 256)? };
assert!(!buf.is_empty(), "buf is too short for an empty null-terminated string");
assert_eq!(buf.pop().unwrap(), 0, "buf is not a null-terminated string");
Ok(String::from_utf8_lossy(buf.as_slice()).into_owned())
}
pub fn ids(&self) -> Result<DeviceId, nix::errno::Errno> {
let mut ids = DeviceId::default();
// SAFETY:
// We know that fd is a valid file descriptor as it comes from a File that we have open.
//
// We know that the pointer to ids is valid because we just allocated it.
unsafe { ioctl::eviocgid(self.fd.as_raw_fd(), &mut ids) }.map(|_| ids)
}
pub fn properties_bitmap(&self) -> Result<Vec<u8>, nix::errno::Errno> {
let buf_size = (INPUT_PROP_CNT + 7) / 8;
// SAFETY:
// We know that fd is a valid file descriptor as it comes from a File that we have open.
//
// The ioctl_read_buf macro prevents the retrieved data from overflowing the buffer created
// by buf_from_ioctl by passing in the size to the ioctl, meaning that the kernel's
// str_to_user function will truncate the string to that length.
unsafe { buf_from_ioctl(ioctl::eviocgprop, &self.fd, buf_size) }
}
pub fn bitmap_for_event_type(&self, event_type: EventType) -> nix::Result<Vec<u8>> {
let buf_size = (event_type.code_count() + 7) / 8;
let mut buf = vec![0; buf_size];
// The EVIOCGBIT ioctl can't be bound using ioctl_read_buf! like the others, since it uses
// part of its ioctl code as an additional parameter, for the event type. Hence this unsafe
// block is a manual expansion of ioctl_read_buf!.
//
// SAFETY:
// We know that fd is a valid file descriptor as it comes from a File that we have open.
//
// We prevent the retrieved data from overflowing buf by passing in the size of buf to the
// ioctl, meaning that the kernel's str_to_user function will truncate the string to that
// length. We also panic if the ioctl returns a length longer than buf, hopefully before the
// overflow can do any damage.
match nix::errno::Errno::result(unsafe {
libc::ioctl(
self.fd.as_raw_fd(),
nix::request_code_read!(b'E', 0x20 + event_type as u16, buf.len())
as nix::sys::ioctl::ioctl_num_type,
buf.as_mut_ptr(),
)
}) {
Ok(len) if len < 0 => {
panic!("EVIOCGBIT returned invalid length {len} for event type {event_type:?}");
}
Ok(len) => {
buf.truncate(len as usize);
Ok(buf)
}
Err(err) => Err(err),
}
}
pub fn supported_axes_of_type(&self, event_type: EventType) -> nix::Result<Vec<u16>> {
let mut axes = Vec::new();
for (i, byte_ref) in self.bitmap_for_event_type(event_type)?.iter().enumerate() {
let mut byte = *byte_ref;
for j in 0..8 {
if byte & 1 == 1 {
axes.push((i * 8 + j) as u16);
}
byte >>= 1;
}
}
Ok(axes)
}
pub fn absolute_axis_info(&self, axis: u16) -> nix::Result<AbsoluteAxisInfo> {
let mut info = AbsoluteAxisInfo::default();
// The EVIOCGABS ioctl can't be bound using ioctl_read! since it uses part of its ioctl code
// as an additional parameter, for the axis code. Hence this unsafe block is a manual
// expansion of ioctl_read!.
//
// SAFETY:
// We know that fd is a valid file descriptor as it comes from a File that we have open.
//
// We know that the pointer to info is valid because we just allocated it.
nix::errno::Errno::result(unsafe {
nix::libc::ioctl(
self.fd.as_raw_fd(),
nix::request_code_read!(b'E', 0x40 + axis, mem::size_of::<AbsoluteAxisInfo>()),
&mut info,
)
})
.map(|_| info)
}
pub fn read_event(&self) -> nix::Result<InputEvent> {
let mut event = InputEvent::default();
// SAFETY:
// We know that fd is a valid file descriptor as it comes from a File that we have open.
//
// We know that the pointer to event is valid because we just allocated it, and that the
// data structures match up because InputEvent is repr(C) and all its members are repr(C)
// or primitives that support all representations without niches.
nix::errno::Errno::result(unsafe {
libc::read(
self.fd.as_raw_fd(),
&mut event as *mut _ as *mut std::ffi::c_void,
mem::size_of::<InputEvent>(),
)
})
.map(|_| event)
}
}