diff options
author | Tom Cherry <tomcherry@google.com> | 2017-05-25 15:58:59 -0700 |
---|---|---|
committer | Tom Cherry <tomcherry@google.com> | 2017-05-25 16:17:19 -0700 |
commit | ed506f7356346b74eabcf45e207f9afe54b63089 (patch) | |
tree | 510ac5eaeaa29f52d32eebe15d867a87d8f872a3 /init/firmware_handler.cpp | |
parent | 0f185bb1484e22cf270c5807d0228880281e408f (diff) |
ueventd: Break devices.cpp into discrete classes
devices.cpp handles too many things for creating one class. This
change breaks it up into various files and classes.
* Parsing is moved to ueventd_parser.cpp
* Reading from the uevent socket and Cold booting is moved to a
UeventListener class, in uevent_listener.cpp
* Firmware handling is moved to firmware_handler.cpp
* The remaining contents form a DeviceHandler class within devices.cpp
Bug: 33785894
Test: boot bullhead x40, observe no major differences in /dev and /sys
Test: boot sailfish x40, observe no major differences in /dev and /sys
Test: init unit tests
Change-Id: I846a2e5995fbb344c7a8e349065c18a934fa6aba
Diffstat (limited to 'init/firmware_handler.cpp')
-rw-r--r-- | init/firmware_handler.cpp | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp new file mode 100644 index 000000000..1471aeb47 --- /dev/null +++ b/init/firmware_handler.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2017 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. + */ + +#include "firmware_handler.h" + +#include <fcntl.h> +#include <sys/sendfile.h> +#include <unistd.h> + +#include <string> +#include <thread> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/unique_fd.h> + +#include "util.h" + +static void LoadFirmware(const Uevent& uevent, const std::string& root, int fw_fd, size_t fw_size, + int loading_fd, int data_fd) { + // Start transfer. + android::base::WriteFully(loading_fd, "1", 1); + + // Copy the firmware. + int rc = sendfile(data_fd, fw_fd, nullptr, fw_size); + if (rc == -1) { + PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent.firmware + << "' }"; + } + + // Tell the firmware whether to abort or commit. + const char* response = (rc != -1) ? "0" : "-1"; + android::base::WriteFully(loading_fd, response, strlen(response)); +} + +static bool IsBooting() { + return access("/dev/.booting", F_OK) == 0; +} + +static void ProcessFirmwareEvent(const Uevent& uevent) { + int booting = IsBooting(); + + LOG(INFO) << "firmware: loading '" << uevent.firmware << "' for '" << uevent.path << "'"; + + std::string root = "/sys" + uevent.path; + std::string loading = root + "/loading"; + std::string data = root + "/data"; + + android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC)); + if (loading_fd == -1) { + PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent.firmware; + return; + } + + android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC)); + if (data_fd == -1) { + PLOG(ERROR) << "couldn't open firmware data fd for " << uevent.firmware; + return; + } + + static const char* firmware_dirs[] = {"/etc/firmware/", "/vendor/firmware/", + "/firmware/image/"}; + +try_loading_again: + for (size_t i = 0; i < arraysize(firmware_dirs); i++) { + std::string file = firmware_dirs[i] + uevent.firmware; + android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC)); + struct stat sb; + if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) { + LoadFirmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd); + return; + } + } + + if (booting) { + // If we're not fully booted, we may be missing + // filesystems needed for firmware, wait and retry. + std::this_thread::sleep_for(100ms); + booting = IsBooting(); + goto try_loading_again; + } + + LOG(ERROR) << "firmware: could not find firmware for " << uevent.firmware; + + // Write "-1" as our response to the kernel's firmware request, since we have nothing for it. + write(loading_fd, "-1", 2); +} + +void HandleFirmwareEvent(const Uevent& uevent) { + if (uevent.subsystem != "firmware" || uevent.action != "add") return; + + // Loading the firmware in a child means we can do that in parallel... + // (We ignore SIGCHLD rather than wait for our children.) + pid_t pid = fork(); + if (pid == 0) { + Timer t; + ProcessFirmwareEvent(uevent); + LOG(INFO) << "loading " << uevent.path << " took " << t; + _exit(EXIT_SUCCESS); + } else if (pid == -1) { + PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware; + } +} |