/* * 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 "CanSocket.h" #include #include #include #include #include #include namespace android::hardware::automotive::can::V1_0::implementation { using namespace std::chrono_literals; /* How frequently the read thread checks whether the interface was asked to be down. * * Note: This does *not* affect read timing or bandwidth, just CPU load vs time to * down the interface. */ static constexpr auto kReadPooling = 100ms; std::unique_ptr CanSocket::open(const std::string& ifname, ReadCallback rdcb, ErrorCallback errcb) { auto sock = netdevice::can::socket(ifname); if (!sock.ok()) { LOG(ERROR) << "Can't open CAN socket on " << ifname; return nullptr; } // Can't use std::make_unique due to private CanSocket constructor. return std::unique_ptr(new CanSocket(std::move(sock), rdcb, errcb)); } CanSocket::CanSocket(base::unique_fd socket, ReadCallback rdcb, ErrorCallback errcb) : mReadCallback(rdcb), mErrorCallback(errcb), mSocket(std::move(socket)), mReaderThread(&CanSocket::readerThread, this) {} CanSocket::~CanSocket() { mStopReaderThread = true; /* CanSocket can be brought down as a result of read failure, from the same thread, * so let's just detach and let it finish on its own. */ if (mReaderThreadFinished) { mReaderThread.detach(); } else { mReaderThread.join(); } } bool CanSocket::send(const struct canfd_frame& frame) { const auto res = write(mSocket.get(), &frame, CAN_MTU); if (res < 0) { PLOG(DEBUG) << "CanSocket send failed"; return false; } if (res != CAN_MTU) { LOG(DEBUG) << "CanSocket sent wrong number of bytes: " << res; return false; } return true; } static struct timeval toTimeval(std::chrono::microseconds t) { struct timeval tv; tv.tv_sec = t / 1s; tv.tv_usec = (t % 1s) / 1us; return tv; } static int selectRead(const base::unique_fd& fd, std::chrono::microseconds timeout) { auto timeouttv = toTimeval(timeout); fd_set readfds; FD_ZERO(&readfds); FD_SET(fd.get(), &readfds); return select(fd.get() + 1, &readfds, nullptr, nullptr, &timeouttv); } void CanSocket::readerThread() { LOG(VERBOSE) << "Reader thread started"; int errnoCopy = 0; while (!mStopReaderThread) { /* The ideal would be to have a blocking read(3) call and interrupt it with shutdown(3). * This is unfortunately not supported for SocketCAN, so we need to rely on select(3). */ const auto sel = selectRead(mSocket, kReadPooling); if (sel == 0) continue; // timeout if (sel == -1) { PLOG(ERROR) << "Select failed"; break; } struct canfd_frame frame; const auto nbytes = read(mSocket.get(), &frame, CAN_MTU); /* We could use SIOCGSTAMP to get a precise UNIX timestamp for a given packet, but what * we really need is a time since boot. There is no direct way to convert between these * clocks. We could implement a class to calculate the difference between the clocks * (querying both several times and picking the smallest difference); apply the difference * to a SIOCGSTAMP returned value; re-synchronize if the elapsed time is too much in the * past (indicating the UNIX timestamp might have been adjusted). * * Apart from the added complexity, it's possible the added calculations and system calls * would add so much time to the processing pipeline so the precision of the reported time * was buried under the subsystem latency. Let's just use a local time since boot here and * leave precise hardware timestamps for custom proprietary implementations (if needed). */ const std::chrono::nanoseconds ts(elapsedRealtimeNano()); if (nbytes != CAN_MTU) { if (nbytes >= 0) { LOG(ERROR) << "Failed to read CAN packet, got " << nbytes << " bytes"; break; } if (errno == EAGAIN) continue; errnoCopy = errno; PLOG(ERROR) << "Failed to read CAN packet"; break; } mReadCallback(frame, ts); } bool failed = !mStopReaderThread; auto errCb = mErrorCallback; mReaderThreadFinished = true; // Don't access any fields from here, see CanSocket::~CanSocket comment about detached thread if (failed) errCb(errnoCopy); LOG(VERBOSE) << "Reader thread stopped"; } } // namespace android::hardware::automotive::can::V1_0::implementation