1/* 2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include <algorithm> 17#include <cerrno> 18#include <climits> 19#include <cstring> 20#include <fstream> 21#include <iostream> 22#include <iterator> 23#include <optional> 24#include <sstream> 25#include <string> 26#include <unordered_map> 27#include <vector> 28 29#include <unistd.h> 30 31#include <libudev.h> 32#include <linux/input.h> 33 34#include "mmi_log.h" 35 36#undef MMI_LOG_DOMAIN 37#define MMI_LOG_DOMAIN MMI_LOG_SERVER 38#undef MMI_LOG_TAG 39#define MMI_LOG_TAG "MmiLibudev" 40 41using namespace std::literals; 42namespace { 43constexpr int UTIL_PATH_SIZE { 1024 }; 44constexpr int UTIL_LINE_SIZE { 16384 }; 45 46bool StartsWith(std::string_view str, std::string_view prefix) 47{ 48 return str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix; 49} 50 51bool ChopTail(std::string_view &str, char sep) 52{ 53 auto pos = str.rfind(sep); 54 if (pos == std::string_view::npos) { 55 return false; 56 } 57 str.remove_suffix(str.size() - pos); 58 return true; 59} 60 61std::string ResolveSymLink(const std::string &syspath) 62{ 63 constexpr auto backStr = "../"sv; 64 char linkTarget[UTIL_PATH_SIZE]; 65 66 ssize_t len = readlink(syspath.c_str(), linkTarget, sizeof(linkTarget)); 67 if (len <= 0 || len == static_cast<ssize_t>(sizeof(linkTarget))) { 68 return syspath; 69 } 70 71 std::string_view tail{ linkTarget, len }; 72 int32_t back = 0; 73 for (; StartsWith(tail, backStr); back++) { 74 tail.remove_prefix(backStr.size()); 75 } 76 77 std::string_view base = syspath; 78 for (int32_t i = 0; i <= back; i++) { 79 if (!ChopTail(base, '/')) { 80 return syspath; 81 } 82 } 83 84 return std::string{ base }.append("/").append(tail); 85} 86 87std::optional<std::string> GetLinkValue(const std::string &slink, const std::string &syspath) 88{ 89 auto path = syspath + "/" + slink; 90 91 char target[UTIL_PATH_SIZE]; 92 ssize_t len = readlink(path.c_str(), target, sizeof(target)); 93 if (len <= 0 || len == static_cast<ssize_t>(sizeof(target))) { 94 MMI_HILOGE("Failed to read link"); 95 return std::nullopt; 96 } 97 98 std::string_view result{ target, len }; 99 auto pos = result.rfind('/'); 100 if (pos == std::string_view::npos) { 101 MMI_HILOGE("Failed to get link value"); 102 return std::nullopt; 103 } 104 return std::string{ result.substr(pos + 1) }; 105} 106 107class BitVector { 108public: 109 // This type depends on kernel definition 110 using val_t = unsigned long; 111 112 // Input string is hexadecimal 64-bit numbers separated by spaces with high bit number first 113 explicit BitVector(const std::string &str) 114 { 115 std::istringstream ss{ str }; 116 ss >> std::hex; 117 std::copy(std::istream_iterator<val_t>(ss), std::istream_iterator<val_t>(), std::back_inserter(bits_)); 118 // Since numbers in string starts with high number we need to reverse vector to count numbers from low to high 119 std::reverse(bits_.begin(), bits_.end()); 120 } 121 122 [[nodiscard]] bool CheckBit(size_t idx) const 123 { 124 auto vidx = idx / (sizeof(val_t) * CHAR_BIT); 125 auto bidx = idx % (sizeof(val_t) * CHAR_BIT); 126 if (vidx >= bits_.size()) { 127 return false; 128 } 129 return (bits_[vidx] & (1ULL << bidx)) != 0; 130 } 131 132private: 133 std::vector<val_t> bits_; 134}; 135} // namespace 136 137struct udev {}; 138 139struct udev_device { 140public: 141 // Not copyable and not movable 142 udev_device(udev_device &) = delete; 143 udev_device(udev_device &&) = delete; 144 udev_device &operator = (udev_device &) = delete; 145 udev_device &operator = (udev_device &&) = delete; 146 147 static udev_device *NewFromSyspath(const std::string &syspathParam) 148 { 149 // path starts in sys 150 if (!StartsWith(syspathParam, "/sys/") || syspathParam.back() == '/') { 151 errno = EINVAL; 152 return nullptr; 153 } 154 155 // resolve possible symlink to real path 156 std::string path = ResolveSymLink(syspathParam); 157 if (StartsWith(path, "/sys/devices/")) { 158 // all "devices" require a "uevent" file 159 struct stat statbuf; 160 std::string filename = path + "/uevent"; 161 if (stat(filename.c_str(), &statbuf) != 0) { 162 return nullptr; 163 } 164 } else { 165 return nullptr; 166 } 167 168 auto *inst = new udev_device; 169 inst->SetSyspath(std::move(path)); 170 171 return inst; 172 } 173 174 static udev_device *NewFromDevnum(char type, dev_t devnum) 175 { 176 const char *typeStr = nullptr; 177 178 if (type == 'b') { 179 typeStr = "block"; 180 } else if (type == 'c') { 181 typeStr = "char"; 182 } else { 183 MMI_HILOGE("Param invalid"); 184 errno = EINVAL; 185 return nullptr; 186 } 187 188 // use /sys/dev/{block,char}/<maj>:<min> link 189 auto majStr = std::to_string(major(devnum)); 190 auto minStr = std::to_string(minor(devnum)); 191 return NewFromSyspath("/sys/dev/"s + typeStr + "/" + majStr + ":" + minStr); 192 } 193 194 void Ref() 195 { 196 refcount++; 197 } 198 199 void Unref() 200 { 201 if (--refcount <= 0) { 202 delete this; 203 } 204 } 205 206 udev_device *GetParent() 207 { 208 if (!parentDevice_.has_value()) { 209 parentDevice_ = NewFromChild(this); 210 } 211 return *parentDevice_; 212 } 213 214 const std::string &GetSyspath() const 215 { 216 return syspath; 217 } 218 219 const std::string &GetSysname() const 220 { 221 return sysname; 222 } 223 224 const std::string &GetDevnode() 225 { 226 return GetProperty("DEVNAME"); 227 } 228 229 bool IsInitialized() 230 { 231 if (!ueventLoaded) { 232 ReadUeventFile(); 233 } 234 return ueventLoaded; 235 } 236 237 udev_device *GetParentWithSubsystem(const std::string &subsystem) 238 { 239 udev_device *parent = GetParent(); 240 while (parent != nullptr) { 241 auto parentSubsystem = parent->GetSubsystem(); 242 if (parentSubsystem.has_value() && parentSubsystem.value() == subsystem) { 243 break; 244 } 245 parent = parent->GetParent(); 246 } 247 248 if (parent == nullptr) { 249 errno = ENOENT; 250 } 251 return parent; 252 } 253 254 bool HasProperty(const std::string &key) 255 { 256 if (!ueventLoaded) { 257 ReadUeventFile(); 258 } 259 return property_.find(key) != property_.end(); 260 } 261 262 const std::string &GetProperty(const std::string &key) 263 { 264 if (!ueventLoaded) { 265 ReadUeventFile(); 266 } 267 return property_[key]; 268 } 269 270private: 271 udev_device() = default; 272 273 ~udev_device() 274 { 275 if (parentDevice_.has_value() && parentDevice_.value() != nullptr) { 276 parentDevice_.value()->Unref(); 277 } 278 } 279 280 static udev_device *NewFromChild(udev_device *child) 281 { 282 std::string_view path = child->GetSyspath(); 283 284 while (true) { 285 if (!ChopTail(path, '/')) { 286 break; 287 } 288 udev_device *parent = NewFromSyspath(std::string{ path }); 289 if (parent != nullptr) { 290 return parent; 291 } 292 } 293 294 return nullptr; 295 } 296 297 void SetSyspath(std::string newSyspath) 298 { 299 syspath = std::move(newSyspath); 300 301 AddProperty("DEVPATH", syspath.substr(0, "/sys"sv.size())); 302 303 auto pos = syspath.rfind('/'); 304 if (pos == std::string::npos) { 305 return; 306 } 307 sysname = syspath.substr(pos + 1); 308 309 // some devices have '!' in their name, change that to '/' 310 for (char &c : sysname) { 311 if (c == '!') { 312 c = '/'; 313 } 314 } 315 } 316 317 void AddPropertyFromString(const std::string &line) 318 { 319 auto pos = line.find('='); 320 if (pos == std::string::npos) { 321 return; 322 } 323 std::string key = line.substr(0, pos); 324 if (key == "DEVNAME") { 325 SetDevnode(line.substr(pos + 1)); 326 return; 327 } 328 AddProperty(std::move(key), line.substr(pos + 1)); 329 } 330 331 void ReadUeventFile() 332 { 333 if (ueventLoaded) { 334 return; 335 } 336 337 auto filename = syspath + "/uevent"; 338 char realPath[PATH_MAX] = {}; 339 CHKPV(realpath(filename.c_str(), realPath)); 340 std::ifstream f(realPath, std::ios_base::in); 341 if (!f.is_open()) { 342 MMI_HILOGE("ReadUeventFile(): path:%{private}s, error:%{public}s", realPath, std::strerror(errno)); 343 return; 344 } 345 ueventLoaded = true; 346 347 char line[UTIL_LINE_SIZE]; 348 while (f.getline(line, sizeof(line))) { 349 AddPropertyFromString(line); 350 } 351 352 CheckInputProperties(); 353 } 354 355 bool CheckAccel(const BitVector &ev, const BitVector &abs, const BitVector &prop) 356 { 357 bool hasKeys = ev.CheckBit(EV_KEY); 358 bool has3dCoordinates = abs.CheckBit(ABS_X) && abs.CheckBit(ABS_Y) && abs.CheckBit(ABS_Z); 359 bool isAccelerometer = prop.CheckBit(INPUT_PROP_ACCELEROMETER); 360 361 if (!hasKeys && has3dCoordinates) { 362 isAccelerometer = true; 363 } 364 365 if (isAccelerometer) { 366 SetInputProperty("ID_INPUT_ACCELEROMETER"); 367 } 368 return isAccelerometer; 369 } 370 371 bool HasJoystickAxesOrButtons(const BitVector &abs, const BitVector &key) 372 { 373 bool hasJoystickAxesOrButtons = false; 374 // Some mouses have so much buttons that they overflow in joystick range, ignore them 375 if (!key.CheckBit(BTN_JOYSTICK - 1)) { 376 for (int32_t button = BTN_JOYSTICK; button < BTN_DIGI && !hasJoystickAxesOrButtons; button++) { 377 hasJoystickAxesOrButtons = key.CheckBit(button); 378 } 379 for (int32_t button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40 && !hasJoystickAxesOrButtons; 380 button++) { 381 hasJoystickAxesOrButtons = key.CheckBit(button); 382 } 383 for (int32_t button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT && !hasJoystickAxesOrButtons; button++) { 384 hasJoystickAxesOrButtons = key.CheckBit(button); 385 } 386 } 387 for (int32_t axis = ABS_RX; axis < ABS_PRESSURE && !hasJoystickAxesOrButtons; axis++) { 388 hasJoystickAxesOrButtons = abs.CheckBit(axis); 389 } 390 return hasJoystickAxesOrButtons; 391 } 392 393 bool CheckPointingStick(const BitVector &prop) 394 { 395 if (prop.CheckBit(INPUT_PROP_POINTING_STICK)) { 396 SetInputProperty("ID_INPUT_POINTINGSTICK"); 397 return true; 398 } 399 return false; 400 } 401 402 void CheckAndSetProp(std::string prop, const bool &flag) 403 { 404 if (flag) { 405 SetInputProperty(prop); 406 MMI_HILOGD("device has prop with %{public}s", prop.c_str()); 407 } 408 } 409 410 void CheckMouseButton(const BitVector &key, bool &flag) 411 { 412 for (int32_t button = BTN_MOUSE; button < BTN_JOYSTICK && !flag; button++) { 413 flag = key.CheckBit(button); 414 } 415 } 416 417 void UpdateProByKey(const BitVector &key, const bool &isDirect, bool &probablyTablet, bool &probablyTouchpad, 418 bool &probablyTouchscreen) 419 { 420 probablyTablet = key.CheckBit(BTN_STYLUS) || key.CheckBit(BTN_TOOL_PEN); 421 probablyTouchpad = key.CheckBit(BTN_TOOL_FINGER) && !key.CheckBit(BTN_TOOL_PEN) && !isDirect; 422 probablyTouchscreen = key.CheckBit(BTN_TOUCH) && isDirect; 423 } 424 425 bool CheckMtCoordinates(const BitVector &abs) 426 { 427 bool hasMtCoordinates = abs.CheckBit(ABS_MT_POSITION_X) && abs.CheckBit(ABS_MT_POSITION_Y); 428 /* unset hasMtCoordinates if devices claims to have all abs axis */ 429 if (hasMtCoordinates && abs.CheckBit(ABS_MT_SLOT) && abs.CheckBit(ABS_MT_SLOT - 1)) { 430 hasMtCoordinates = false; 431 } 432 return hasMtCoordinates; 433 } 434 435 void UpdateProByStatus(const bool &isMouse, const bool &isTouchpad, const bool &isTouchscreen, 436 const bool &isJoystick, const bool &isTablet) 437 { 438 CheckAndSetProp("ID_INPUT_MOUSE", isMouse); 439 CheckAndSetProp("ID_INPUT_TOUCHPAD", isTouchpad); 440 CheckAndSetProp("ID_INPUT_TOUCHSCREEN", isTouchscreen); 441 CheckAndSetProp("ID_INPUT_JOYSTICK", isJoystick); 442 CheckAndSetProp("ID_INPUT_TABLET", isTablet); 443 } 444 445 bool CheckPointers(const BitVector &ev, const BitVector &abs, const BitVector &key, const BitVector &rel, 446 const BitVector &prop) 447 { 448 bool isDirect = prop.CheckBit(INPUT_PROP_DIRECT); 449 bool hasAbsCoordinates = abs.CheckBit(ABS_X) && abs.CheckBit(ABS_Y); 450 bool hasRelCoordinates = ev.CheckBit(EV_REL) && rel.CheckBit(REL_X) && rel.CheckBit(REL_Y); 451 bool hasMtCoordinates = CheckMtCoordinates(abs); 452 453 bool hasMouseButton = false; 454 CheckMouseButton(key, hasMouseButton); 455 456 bool probablyTablet; 457 bool probablyTouchpad; 458 bool probablyTouchscreen; 459 UpdateProByKey(key, isDirect, probablyTablet, probablyTouchpad, probablyTouchscreen); 460 bool probablyJoystick = HasJoystickAxesOrButtons(abs, key); 461 462 bool isTablet = false; 463 bool isMouse = false; 464 bool isTouchpad = false; 465 bool isTouchscreen = false; 466 bool isJoystick = false; 467 if (hasAbsCoordinates) { 468 if (probablyTablet) { 469 isTablet = true; 470 } else if (probablyTouchpad) { 471 isTouchpad = true; 472 } else if (hasMouseButton) { 473 /* This path is taken by VMware's USB mouse, which has 474 * absolute axes, but no touch/pressure button. */ 475 isMouse = true; 476 } else if (probablyTouchscreen) { 477 isTouchscreen = true; 478 } else { 479 isJoystick = probablyJoystick; 480 } 481 } else { 482 isJoystick = probablyJoystick; 483 } 484 485 if (hasMtCoordinates) { 486 if (probablyTablet) { 487 isTablet = true; 488 } else if (probablyTouchpad) { 489 isTouchpad = true; 490 } else if (probablyTouchscreen) { 491 isTouchscreen = true; 492 } 493 } 494 495 /* mouse buttons and no axis */ 496 if (!isTablet && !isTouchpad && !isJoystick && hasMouseButton && (hasRelCoordinates || !hasAbsCoordinates)) { 497 isMouse = true; 498 } 499 500 UpdateProByStatus(isMouse, isTouchpad, isTouchscreen, isJoystick, isTablet); 501 502 return isTablet || isMouse || isTouchpad || isTouchscreen || isJoystick || CheckPointingStick(prop); 503 } 504 505 bool CheckKeys(const BitVector &ev, const BitVector &key) 506 { 507 if (!ev.CheckBit(EV_KEY)) { 508 return false; 509 } 510 511 /* only consider KEY_* here, not BTN_* */ 512 bool found = false; 513 for (int32_t i = 0; i < BTN_MISC && !found; ++i) { 514 found = key.CheckBit(i); 515 } 516 /* If there are no keys in the lower block, check the higher blocks */ 517 for (int32_t i = KEY_OK; i < BTN_DPAD_UP && !found; ++i) { 518 found = key.CheckBit(i); 519 } 520 for (int32_t i = KEY_ALS_TOGGLE; i < BTN_TRIGGER_HAPPY && !found; ++i) { 521 found = key.CheckBit(i); 522 } 523 524 if (found) { 525 SetInputProperty("ID_INPUT_KEY"); 526 } 527 528 /* the first 32 bits are ESC, numbers, and Q to D; if we have all of 529 * those, consider it a full keyboard; do not test KEY_RESERVED, though */ 530 bool isKeyboard = true; 531 for (int32_t i = KEY_ESC; i < KEY_D && isKeyboard; i++) { 532 isKeyboard = key.CheckBit(i); 533 } 534 if (isKeyboard) { 535 SetInputProperty("ID_INPUT_KEYBOARD"); 536 } 537 538 return found || isKeyboard; 539 } 540 541 void SetInputProperty(std::string prop) 542 { 543 AddProperty("ID_INPUT", "1"); 544 AddProperty(std::move(prop), "1"); 545 } 546 547 void CheckInputProperties() 548 { 549 BitVector ev{ GetProperty("EV") }; 550 BitVector abs{ GetProperty("ABS") }; 551 BitVector key{ GetProperty("KEY") }; 552 BitVector rel{ GetProperty("REL") }; 553 BitVector prop{ GetProperty("PROP") }; 554 555 bool isPointer = CheckAccel(ev, abs, prop) || CheckPointers(ev, abs, key, rel, prop); 556 bool isKey = CheckKeys(ev, key); 557 /* Some evdev nodes have only a scrollwheel */ 558 if (!isPointer && !isKey && ev.CheckBit(EV_REL) && (rel.CheckBit(REL_WHEEL) || rel.CheckBit(REL_HWHEEL))) { 559 SetInputProperty("ID_INPUT_KEY"); 560 } 561 if (ev.CheckBit(EV_SW)) { 562 SetInputProperty("ID_INPUT_SWITCH"); 563 } 564 } 565 566 void SetDevnode(std::string newDevnode) 567 { 568 if (newDevnode[0] != '/') { 569 newDevnode = "/dev/" + newDevnode; 570 } 571 AddProperty("DEVNAME", std::move(newDevnode)); 572 } 573 574 void AddProperty(std::string key, std::string value) 575 { 576 property_[std::move(key)] = std::move(value); 577 } 578 579 std::optional<std::string> GetSubsystem() 580 { 581 if (!subsystem_.has_value()) { 582 auto res = GetLinkValue("subsystem", syspath); 583 // read "subsystem" link 584 if (res.has_value()) { 585 SetSubsystem(std::move(*res)); 586 return subsystem_; 587 } 588 subsystem_ = ""; 589 } 590 return subsystem_; 591 } 592 593 void SetSubsystem(std::string newSubsystem) 594 { 595 subsystem_ = newSubsystem; 596 AddProperty("SUBSYSTEM", std::move(newSubsystem)); 597 } 598 599private: 600 int refcount = 1; 601 std::string syspath; 602 std::string sysname; 603 604 std::optional<udev_device *> parentDevice_; 605 std::optional<std::string> subsystem_; 606 607 bool ueventLoaded = false; 608 std::unordered_map<std::string, std::string> property_; 609}; 610 611// C-style interface 612 613udev *udev_new(void) 614{ 615 static udev instance{}; 616 return &instance; 617} 618 619udev *udev_unref([[maybe_unused]] udev *udev) 620{ 621 return nullptr; 622} 623 624udev_device *udev_device_ref(udev_device *device) 625{ 626 CHKPP(device); 627 device->Ref(); 628 return device; 629} 630 631udev_device *udev_device_unref(udev_device *device) 632{ 633 CHKPP(device); 634 device->Unref(); 635 return nullptr; 636} 637 638udev *udev_device_get_udev(udev_device *device) 639{ 640 CHKPP(device); 641 return udev_new(); 642} 643 644udev_device *udev_device_new_from_syspath(udev *udev, const char *syspath) 645{ 646 if (udev == nullptr || syspath == nullptr) { 647 errno = EINVAL; 648 return nullptr; 649 } 650 return udev_device::NewFromSyspath(syspath); 651} 652 653udev_device *udev_device_new_from_devnum(udev *udev, char type, dev_t devnum) 654{ 655 if (udev == nullptr) { 656 errno = EINVAL; 657 return nullptr; 658 } 659 return udev_device::NewFromDevnum(type, devnum); 660} 661 662udev_device *udev_device_get_parent(udev_device *device) 663{ 664 if (device == nullptr) { 665 errno = EINVAL; 666 return nullptr; 667 } 668 return device->GetParent(); 669} 670 671udev_device *udev_device_get_parent_with_subsystem_devtype(udev_device *device, const char *subsystem, 672 const char *devtype) 673{ 674 CHKPP(device); 675 if (subsystem == nullptr) { 676 errno = EINVAL; 677 return nullptr; 678 } 679 // Searching with specific devtype is not supported, since not used by libinput 680 CHKPP(devtype); 681 return device->GetParentWithSubsystem(subsystem); 682} 683 684const char *udev_device_get_syspath(udev_device *device) 685{ 686 CHKPP(device); 687 return device->GetSyspath().c_str(); 688} 689 690const char *udev_device_get_sysname(udev_device *device) 691{ 692 CHKPP(device); 693 return device->GetSysname().c_str(); 694} 695 696const char *udev_device_get_devnode(udev_device *device) 697{ 698 CHKPP(device); 699 return device->GetDevnode().c_str(); 700} 701 702int udev_device_get_is_initialized(udev_device *device) 703{ 704 return (device != nullptr) ? static_cast<int>(device->IsInitialized()) : -1; 705} 706 707const char *udev_device_get_property_value(udev_device *device, const char *key) 708{ 709 CHKPP(device); 710 CHKPP(key); 711 std::string skey{ key }; 712 if (!device->HasProperty(key)) { 713 return nullptr; 714 } 715 return device->GetProperty(key).c_str(); 716} 717