Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1b2c6efe authored by Tomasz Wasilczyk's avatar Tomasz Wasilczyk
Browse files

Implement CAN bus HAL tools

These tools are for manual interaction with CAN bus HAL instances.
Meant primarily for testing/debugging, not useful in user builds.

Bug: 135918744
Test: adb shell <tool name>
Change-Id: I1dfbe94c0006f69954806c395cd888f3bf2a4249
parent 87329670
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
//
// Copyright (C) 2019 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.
//

cc_library_headers {
    name: "android.hardware.automotive.can@hidl-utils-lib",
    export_include_dirs: ["include"],
}
+67 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.
 */

#pragma once

namespace android {
namespace hardware {
namespace automotive {
namespace hidl_utils {

/**
 * Helper functor to fetch results from multi-return HIDL calls.
 * It's meant to be used in place of _hidl_cb callbacks.
 *
 * Please note extracting these return variables outside of the callback scope requires making
 * a copy of each return variable. This may be costly for frequently called HIDL methods with
 * non-negligible return object size. Please be cautious about performance when using this.
 *
 * Example usage:
 *     Result result;
 *     sp<ISomeInterface> iface;
 *     hidlObject->someMethod(arg1, arg2, hidl_utils::fill(&result, &iface)).assertOk();
 *     // use result and iface
 */
template <typename... T>
struct fill : public std::function<void(const T&...)> {
    /**
     * Create _hidl_cb functor that copies the call arguments to specified pointers.
     *
     * \param args... Targets to copy the call arguments to
     */
    fill(T*... args) : mTargets(args...) {}

    void operator()(const T&... args) { copy<0, T...>(args...); }

  private:
    std::tuple<T*...> mTargets;

    template <int Pos, typename First>
    inline void copy(const First& first) {
        *std::get<Pos>(mTargets) = first;
    }

    template <int Pos, typename First, typename... Rest>
    inline void copy(const First& first, const Rest&... rest) {
        *std::get<Pos>(mTargets) = first;
        copy<Pos + 1, Rest...>(rest...);
    }
};

}  // namespace hidl_utils
}  // namespace automotive
}  // namespace hardware
}  // namespace android
+60 −0
Original line number Diff line number Diff line
//
// Copyright (C) 2019 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.
//

cc_binary {
    name: "canhalctrl",
    defaults: ["android.hardware.automotive.can@defaults"],
    srcs: [
        "canhalctrl.cpp",
    ],
    shared_libs: [
        "android.hardware.automotive.can@1.0",
        "libhidlbase",
        "libhidltransport",
    ],
    header_libs: [
        "android.hardware.automotive.can@hidl-utils-lib",
    ],
}

cc_binary {
    name: "canhaldump",
    defaults: ["android.hardware.automotive.can@defaults"],
    srcs: [
        "canhaldump.cpp",
    ],
    shared_libs: [
        "android.hardware.automotive.can@1.0",
        "libhidlbase",
        "libhidltransport",
    ],
    header_libs: [
        "android.hardware.automotive.can@hidl-utils-lib",
    ],
}

cc_binary {
    name: "canhalsend",
    defaults: ["android.hardware.automotive.can@defaults"],
    srcs: [
        "canhalsend.cpp",
    ],
    shared_libs: [
        "android.hardware.automotive.can@1.0",
        "libhidlbase",
        "libhidltransport",
    ],
}
+181 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 <android-base/logging.h>
#include <android/hardware/automotive/can/1.0/ICanController.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <hidl-utils/hidl-utils.h>

#include <iostream>
#include <string>

namespace android {
namespace hardware {
namespace automotive {
namespace can {

using ICanController = V1_0::ICanController;

static void usage() {
    std::cerr << "CAN bus HAL Control tool" << std::endl;
    std::cerr << std::endl << "usage:" << std::endl << std::endl;
    std::cerr << "canhalctrl up <bus name> <type> <interface> [baudrate]" << std::endl;
    std::cerr << "where:" << std::endl;
    std::cerr << " bus name - name under which ICanBus will be published" << std::endl;
    std::cerr << " type - one of: virtual, socketcan, slcan, indexed" << std::endl;
    std::cerr << " interface - hardware identifier (like can0, vcan0, /dev/ttyUSB0)" << std::endl;
    std::cerr << " baudrate - such as 100000, 125000, 250000, 500000" << std::endl;
    std::cerr << std::endl;
    std::cerr << "canhalctrl down <bus name>" << std::endl;
    std::cerr << "where:" << std::endl;
    std::cerr << " bus name - name under which ICanBus will be published" << std::endl;
}

static hidl_vec<hidl_string> getControlServices() {
    auto manager = hidl::manager::V1_2::IServiceManager::getService();
    hidl_vec<hidl_string> services;
    manager->listManifestByInterface(ICanController::descriptor, hidl_utils::fill(&services));
    if (services.size() == 0) {
        std::cerr << "No ICanController services registered (missing privileges?)" << std::endl;
        exit(-1);
    }
    return services;
}

static bool isSupported(sp<ICanController> ctrl, ICanController::InterfaceType iftype) {
    hidl_vec<ICanController::InterfaceType> supported;
    if (!ctrl->getSupportedInterfaceTypes(hidl_utils::fill(&supported)).isOk()) return false;
    return supported.contains(iftype);
}

static int up(const std::string& busName, ICanController::InterfaceType type,
              const std::string& interface, uint32_t baudrate) {
    bool anySupported = false;
    for (auto&& service : getControlServices()) {
        auto ctrl = ICanController::getService(service);
        if (ctrl == nullptr) {
            std::cerr << "Couldn't open ICanController/" << service;
            continue;
        }

        if (!isSupported(ctrl, type)) continue;
        anySupported = true;

        ICanController::BusConfiguration config = {};
        config.name = busName;
        config.iftype = type;
        config.baudrate = baudrate;

        if (type == ICanController::InterfaceType::INDEXED) {
            config.interfaceId.index(std::stol(interface));
        } else {
            config.interfaceId.address(interface);
        }

        const auto upresult = ctrl->upInterface(config);
        if (upresult == ICanController::Result::OK) return 0;
        std::cerr << "Failed to bring interface up: " << toString(upresult) << std::endl;
        // Let's continue the loop to try other controllers.
    }

    if (!anySupported) {
        std::cerr << "No controller supports " << toString(type) << std::endl;
    }
    return -1;
}

static int down(const std::string& busName) {
    for (auto&& service : getControlServices()) {
        auto ctrl = ICanController::getService(service);
        if (ctrl == nullptr) continue;

        if (ctrl->downInterface(busName)) return 0;
    }

    std::cerr << "Failed to bring interface " << busName << " down (maybe it's down already?)"
              << std::endl;
    return -1;
}

static std::optional<ICanController::InterfaceType> parseInterfaceType(const std::string& str) {
    if (str == "virtual") return ICanController::InterfaceType::VIRTUAL;
    if (str == "socketcan") return ICanController::InterfaceType::SOCKETCAN;
    if (str == "slcan") return ICanController::InterfaceType::SLCAN;
    if (str == "indexed") return ICanController::InterfaceType::INDEXED;
    return std::nullopt;
}

static int main(int argc, char* argv[]) {
    base::SetDefaultTag("CanHalControl");
    base::SetMinimumLogSeverity(android::base::VERBOSE);

    if (argc == 0) {
        usage();
        return 0;
    }

    std::string cmd(argv[0]);
    argv++;
    argc--;

    if (cmd == "up") {
        if (argc < 3 || argc > 4) {
            std::cerr << "Invalid number of arguments to up command: " << argc << std::endl;
            usage();
            return -1;
        }

        const std::string busName(argv[0]);
        const std::string typeStr(argv[1]);
        const std::string interface(argv[2]);

        const auto type = parseInterfaceType(typeStr);
        if (!type) {
            std::cerr << "Invalid interface type: " << typeStr << std::endl;
            usage();
            return -1;
        }

        long long baudrate = 0;
        if (argc == 4) {
            baudrate = std::stoll(argv[3]);
        }

        return up(busName, *type, interface, baudrate);
    } else if (cmd == "down") {
        if (argc != 1) {
            std::cerr << "Invalid number of arguments to down command: " << argc << std::endl;
            usage();
            return -1;
        }

        return down(argv[0]);
    } else {
        std::cerr << "Invalid command: " << cmd << std::endl;
        usage();
        return -1;
    }
}

}  // namespace can
}  // namespace automotive
}  // namespace hardware
}  // namespace android

int main(int argc, char* argv[]) {
    if (argc < 1) return -1;
    return ::android::hardware::automotive::can::main(--argc, ++argv);
}
+136 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 <android-base/logging.h>
#include <android/hardware/automotive/can/1.0/ICanBus.h>
#include <android/hardware/automotive/can/1.0/ICanMessageListener.h>
#include <android/hidl/manager/1.2/IServiceManager.h>
#include <hidl-utils/hidl-utils.h>

#include <chrono>
#include <iomanip>
#include <iostream>
#include <string>
#include <thread>

namespace android {
namespace hardware {
namespace automotive {
namespace can {

using namespace std::chrono_literals;

using ICanBus = V1_0::ICanBus;
using Result = V1_0::Result;

struct CanMessageListener : public V1_0::ICanMessageListener {
    const std::string name;

    CanMessageListener(std::string name) : name(name) {}

    virtual Return<void> onReceive(const V1_0::CanMessage& message) {
        std::cout << "  " << name << "  " << std::hex << std::uppercase << std::setw(3)
                  << std::setfill('0') << message.id << std::setw(0);
        if (message.remoteTransmissionRequest) {
            std::cout << " RTR";
        } else {
            std::cout << "   [" << message.payload.size() << "] ";
            for (const auto byte : message.payload) {
                std::cout << " " << unsigned(byte);
            }
        }
        std::cout << std::nouppercase << std::dec << std::endl;
        return {};
    }

    virtual Return<void> onError(V1_0::ErrorEvent error) {
        std::cout << "  " << name << "  " << toString(error) << std::endl;
        return {};
    }
};

static void usage() {
    std::cerr << "canhaldump - dump CAN bus traffic" << std::endl;
    std::cerr << std::endl << "usage:" << std::endl << std::endl;
    std::cerr << "canhaldump <bus name>" << std::endl;
    std::cerr << "where:" << std::endl;
    std::cerr << " bus name - name under which ICanBus is be published" << std::endl;
}

// TODO(b/135918744): extract to a new library
static sp<ICanBus> tryOpen(const std::string& busname) {
    auto bus = ICanBus::tryGetService(busname);
    if (bus != nullptr) return bus;

    /* Fallback for interfaces not registered in manifest. For testing purposes only,
     * one should not depend on this in production deployment. */
    auto manager = hidl::manager::V1_2::IServiceManager::getService();
    auto ret = manager->get(ICanBus::descriptor, busname).withDefault(nullptr);
    if (ret == nullptr) return nullptr;

    std::cerr << "WARNING: bus " << busname << " is not registered in device manifest, "
              << "trying to fetch it directly..." << std::endl;

    return ICanBus::castFrom(ret);
}

static int candump(const std::string& busname) {
    auto bus = tryOpen(busname);
    if (bus == nullptr) {
        std::cerr << "Bus " << busname << " is not available" << std::endl;
        return -1;
    }

    Result result;
    sp<V1_0::ICloseHandle> chnd;
    // TODO(b/135918744): extract to library
    bus->listen({}, new CanMessageListener(busname), hidl_utils::fill(&result, &chnd)).assertOk();

    if (result != Result::OK) {
        std::cerr << "Listen call failed: " << toString(result) << std::endl;
        return -1;
    }

    while (true) std::this_thread::sleep_for(1h);
}

static int main(int argc, char* argv[]) {
    base::SetDefaultTag("CanHalDump");
    base::SetMinimumLogSeverity(android::base::VERBOSE);

    if (argc == 0) {
        usage();
        return 0;
    }

    if (argc != 1) {
        std::cerr << "Invalid number of arguments" << std::endl;
        usage();
        return -1;
    }

    return candump(argv[0]);
}

}  // namespace can
}  // namespace automotive
}  // namespace hardware
}  // namespace android

int main(int argc, char* argv[]) {
    if (argc < 1) return -1;
    return ::android::hardware::automotive::can::main(--argc, ++argv);
}
Loading