mirror of https://github.com/ArduPilot/ardupilot
301 lines
7.5 KiB
C++
301 lines
7.5 KiB
C++
/*
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* Many thanks to members of the UAVCAN project:
|
|
* Pavel Kirienko <pavel.kirienko@gmail.com>
|
|
* Ilia Sheremet <illia.sheremet@gmail.com>
|
|
*
|
|
* license info can be found in the uavcan submodule located:
|
|
* modules/uavcan/libuavcan_drivers/linux/include/uavcan_linux/socketcan.hpp
|
|
*/
|
|
|
|
#include <AP_HAL/AP_HAL.h>
|
|
#include <AP_HAL/system.h>
|
|
|
|
#if HAL_NUM_CAN_IFACES
|
|
|
|
#include "CANSocketIface.h"
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <net/if.h>
|
|
#include <cstring>
|
|
#include <cstdint>
|
|
#include "Scheduler.h"
|
|
#include <AP_CANManager/AP_CANManager.h>
|
|
#include <AP_Common/ExpandingString.h>
|
|
#include "CAN_Multicast.h"
|
|
#include "CAN_SocketCAN.h"
|
|
|
|
extern const AP_HAL::HAL& hal;
|
|
|
|
using namespace HALSITL;
|
|
|
|
#if HAL_CANMANAGER_ENABLED
|
|
#define Debug(fmt, args...) do { AP::can().log_text(AP_CANManager::LOG_DEBUG, "CANSITLIface", fmt, ##args); } while (0)
|
|
#else
|
|
#define Debug(fmt, args...)
|
|
#endif
|
|
|
|
uint8_t CANIface::_num_interfaces;
|
|
|
|
bool CANIface::is_initialized() const
|
|
{
|
|
return transport != nullptr;
|
|
}
|
|
|
|
int16_t CANIface::send(const AP_HAL::CANFrame& frame, const uint64_t tx_deadline,
|
|
const CANIface::CanIOFlags flags)
|
|
{
|
|
WITH_SEMAPHORE(sem);
|
|
CanTxItem tx_item {};
|
|
tx_item.frame = frame;
|
|
if (flags & Loopback) {
|
|
tx_item.loopback = true;
|
|
}
|
|
if (flags & AbortOnError) {
|
|
tx_item.abort_on_error = true;
|
|
}
|
|
tx_item.setup = true;
|
|
tx_item.index = _tx_frame_counter;
|
|
tx_item.deadline = tx_deadline;
|
|
if (_tx_queue.push(tx_item)) {
|
|
_tx_frame_counter++;
|
|
stats.tx_requests++;
|
|
} else {
|
|
stats.tx_overflow++;
|
|
}
|
|
_pollRead(); // Read poll is necessary because it can release the pending TX flag
|
|
_pollWrite();
|
|
|
|
return AP_HAL::CANIface::send(frame, tx_deadline, flags);
|
|
}
|
|
|
|
int16_t CANIface::receive(AP_HAL::CANFrame& out_frame, uint64_t& out_timestamp_us,
|
|
CANIface::CanIOFlags& out_flags)
|
|
{
|
|
WITH_SEMAPHORE(sem);
|
|
if (_rx_queue.is_empty()) {
|
|
_pollRead(); // This allows to use the socket not calling poll() explicitly.
|
|
if (_rx_queue.is_empty()) {
|
|
return 0;
|
|
}
|
|
}
|
|
{
|
|
const CanRxItem &rx = *_rx_queue[0];
|
|
out_frame = rx.frame;
|
|
out_timestamp_us = rx.timestamp_us;
|
|
out_flags = rx.flags;
|
|
}
|
|
IGNORE_RETURN(_rx_queue.pop());
|
|
return AP_HAL::CANIface::receive(out_frame, out_timestamp_us, out_flags);
|
|
}
|
|
|
|
bool CANIface::_hasReadyTx()
|
|
{
|
|
WITH_SEMAPHORE(sem);
|
|
return !_tx_queue.is_empty();
|
|
}
|
|
|
|
bool CANIface::_hasReadyRx()
|
|
{
|
|
WITH_SEMAPHORE(sem);
|
|
return !_rx_queue.is_empty();
|
|
}
|
|
|
|
void CANIface::_poll(bool read, bool write)
|
|
{
|
|
if (read) {
|
|
_pollRead(); // Read poll must be executed first because it may decrement _frames_in_socket_tx_queue
|
|
}
|
|
if (write) {
|
|
_pollWrite();
|
|
}
|
|
}
|
|
|
|
uint32_t CANIface::getErrorCount() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void CANIface::_pollWrite()
|
|
{
|
|
if (transport == nullptr) {
|
|
return;
|
|
}
|
|
while (_hasReadyTx()) {
|
|
WITH_SEMAPHORE(sem);
|
|
const CanTxItem tx = *_tx_queue[0];
|
|
const uint64_t curr_time = AP_HAL::micros64();
|
|
if (tx.deadline >= curr_time) {
|
|
// hal.console->printf("%x TDEAD: %lu CURRT: %lu DEL: %lu\n",tx.frame.id, tx.deadline, curr_time, tx.deadline-curr_time);
|
|
bool ok = transport->send(tx.frame);
|
|
if (ok) {
|
|
stats.tx_success++;
|
|
stats.last_transmit_us = curr_time;
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
stats.tx_timedout++;
|
|
}
|
|
|
|
// Removing the frame from the queue
|
|
IGNORE_RETURN(_tx_queue.pop());
|
|
}
|
|
}
|
|
|
|
bool CANIface::_pollRead()
|
|
{
|
|
if (transport == nullptr) {
|
|
return false;
|
|
}
|
|
CanRxItem rx {};
|
|
bool ok = transport->receive(rx.frame);
|
|
if (!ok) {
|
|
return false;
|
|
}
|
|
rx.timestamp_us = AP_HAL::micros64();
|
|
WITH_SEMAPHORE(sem);
|
|
add_to_rx_queue(rx);
|
|
stats.rx_received++;
|
|
return true;
|
|
}
|
|
|
|
// Might block forever, only to be used for testing
|
|
void CANIface::flush_tx()
|
|
{
|
|
WITH_SEMAPHORE(sem);
|
|
do {
|
|
_poll(true, true);
|
|
} while(!_tx_queue.is_empty());
|
|
}
|
|
|
|
void CANIface::clear_rx()
|
|
{
|
|
WITH_SEMAPHORE(sem);
|
|
// Clean Rx Queue
|
|
_rx_queue.clear();
|
|
}
|
|
|
|
void CANIface::_confirmSentFrame()
|
|
{
|
|
if (_frames_in_socket_tx_queue > 0) {
|
|
_frames_in_socket_tx_queue--;
|
|
}
|
|
}
|
|
|
|
bool CANIface::init(const uint32_t bitrate, const uint32_t fdbitrate, const OperatingMode mode)
|
|
{
|
|
return init(bitrate, mode);
|
|
}
|
|
|
|
bool CANIface::init(const uint32_t bitrate, const OperatingMode mode)
|
|
{
|
|
const auto *_sitl = AP::sitl();
|
|
if (_sitl == nullptr) {
|
|
return false;
|
|
}
|
|
if (_self_index >= HAL_NUM_CAN_IFACES) {
|
|
return false;
|
|
}
|
|
const SITL::SIM::CANTransport can_type = _sitl->can_transport[_self_index];
|
|
switch (can_type) {
|
|
case SITL::SIM::CANTransport::MulticastUDP:
|
|
transport = NEW_NOTHROW CAN_Multicast();
|
|
break;
|
|
case SITL::SIM::CANTransport::SocketCAN:
|
|
#if HAL_CAN_WITH_SOCKETCAN
|
|
transport = NEW_NOTHROW CAN_SocketCAN();
|
|
#endif
|
|
break;
|
|
}
|
|
if (transport == nullptr) {
|
|
return false;
|
|
}
|
|
if (!transport->init(_self_index)) {
|
|
delete transport;
|
|
transport = nullptr;
|
|
return false;
|
|
}
|
|
if (sem_handle != nullptr) {
|
|
transport->set_event_handle(sem_handle);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CANIface::select(bool &read_select, bool &write_select,
|
|
const AP_HAL::CANFrame* const pending_tx, uint64_t blocking_deadline)
|
|
{
|
|
if (transport == nullptr) {
|
|
return false;
|
|
}
|
|
// Detecting whether we need to block at all
|
|
bool need_block = !write_select; // Write queue is infinite
|
|
|
|
// call poll here to flush some tx
|
|
_poll(true, true);
|
|
|
|
if (read_select && _hasReadyRx()) {
|
|
need_block = false;
|
|
}
|
|
|
|
if (need_block) {
|
|
_pollfd.fd = transport->get_read_fd();
|
|
_pollfd.events |= POLLIN;
|
|
}
|
|
const uint64_t now_us = AP_HAL::micros64();
|
|
if (sem_handle != nullptr && blocking_deadline > now_us) {
|
|
IGNORE_RETURN(sem_handle->wait(blocking_deadline - now_us));
|
|
}
|
|
|
|
// Writing the output masks
|
|
write_select = true;
|
|
read_select = _hasReadyRx();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CANIface::set_event_handle(AP_HAL::BinarySemaphore *handle)
|
|
{
|
|
sem_handle = handle;
|
|
if (transport != nullptr) {
|
|
transport->set_event_handle(handle);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
void CANIface::get_stats(ExpandingString &str)
|
|
{
|
|
str.printf("tx_requests: %u\n"
|
|
"tx_rejected: %u\n"
|
|
"tx_success: %u\n"
|
|
"tx_timedout: %u\n"
|
|
"rx_received: %u\n"
|
|
"rx_errors: %u\n",
|
|
stats.tx_requests,
|
|
stats.tx_rejected,
|
|
stats.tx_success,
|
|
stats.tx_timedout,
|
|
stats.rx_received,
|
|
stats.rx_errors);
|
|
}
|
|
|
|
#endif
|