Aktualizr
C++ SOTA Client
All Classes Namespaces Files Functions Variables Enumerations Enumerator Pages
isotp_conn.cc
1 #include "isotp_conn.h"
2 #include <linux/can.h>
3 #include <linux/can/raw.h>
4 #include <net/if.h>
5 #include <sys/ioctl.h>
6 #include <sys/time.h>
7 
8 #include <exception>
9 #include <iostream>
10 
11 #include <cstring>
12 
13 #include <chrono>
14 #include <thread>
15 
16 #include <boost/algorithm/hex.hpp>
17 
18 #include "logging/logging.h"
19 
20 IsoTpSendRecv::IsoTpSendRecv(std::string can_iface_, uint16_t canaddr_rx_, uint16_t canaddr_tx_)
21  : can_iface{std::move(can_iface_)}, canaddr_rx{canaddr_rx_}, canaddr_tx{canaddr_tx_} {
22  can_socket = socket(PF_CAN, SOCK_RAW, CAN_RAW);
23 
24  if (can_socket < -1) {
25  throw std::runtime_error("Unable to open socket");
26  }
27 
28  struct can_filter filter {};
29  filter.can_id = canaddr_rx & 0x7FF;
30  filter.can_mask = 0x7FF;
31  setsockopt(can_socket, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(filter));
32 
33  struct ifreq ifr {};
34  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
35  memcpy(ifr.ifr_name, can_iface.c_str(), IFNAMSIZ);
36 
37  if (ioctl(can_socket, SIOCGIFINDEX, &ifr) != 0) {
38  throw std::runtime_error("Unable to get interface index");
39  }
40 
41  struct sockaddr_can addr {};
42  addr.can_family = AF_CAN;
43  addr.can_ifindex = ifr.ifr_ifindex; // NOLINT
44 
45  if (bind(can_socket, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) {
46  throw std::runtime_error("Unable to bind socket");
47  }
48 
49  isotp_shims = isotp_init_shims(nullptr, canSend, nullptr, this);
50 }
51 
52 bool IsoTpSendRecv::canSend(uint32_t arbitration_id, const uint8_t* data, uint8_t size, void* private_data) {
53  auto* instance = static_cast<IsoTpSendRecv*>(private_data);
54 
55  if ((instance == nullptr) || size > 8) {
56  return false;
57  }
58 
59  LOG_TRACE << "Sending CAN message AF: 0x" << std::hex << arbitration_id << "; Data:";
60  LOG_TRACE << " " << boost::algorithm::hex(std::string(reinterpret_cast<const char*>(data), size));
61 
62  int can_socket = instance->can_socket;
63 
64  struct can_frame frame {};
65 
66  frame.can_id = arbitration_id;
67  frame.can_dlc = size;
68  memcpy(frame.data, data, size);
69 
70  ssize_t res = write(can_socket, &frame, sizeof(frame));
71  if (res < 0) {
72  LOG_ERROR << "CAN write error: " << strerror(errno);
73  return false;
74  }
75  if (res != sizeof(frame)) {
76  LOG_ERROR << "CAN write error: " << res << " bytes of " << sizeof(frame) << " were sent";
77  return false;
78  }
79  return true;
80 }
81 
82 bool IsoTpSendRecv::Send(const std::string& out) {
83  IsoTpMessage message_tx = isotp_new_send_message(canaddr_tx, reinterpret_cast<const uint8_t*>(out.c_str()),
84  static_cast<uint16_t>(out.length()));
85  IsoTpSendHandle send_handle = isotp_send(&isotp_shims, &message_tx, nullptr);
86  if (send_handle.completed) {
87  if (send_handle.success) {
88  return true;
89  }
90  LOG_ERROR << "ISO/TP message send failed";
91  return false;
92  } else {
93  while (true) {
94  fd_set read_set;
95  FD_ZERO(&read_set); // NOLINT(readability-isolate-declaration)
96  FD_SET(can_socket, &read_set);
97 
98  // struct timeval timeout = {0, 20000}; // 20 ms
99  // if (select((can_socket + 1), &read_set, nullptr, nullptr, &timeout) >= 0) {
100  if (select((can_socket + 1), &read_set, nullptr, nullptr, nullptr) >= 0) {
101  if (FD_ISSET(can_socket, &read_set)) {
102  struct can_frame f {};
103  ssize_t ret = read(can_socket, &f, sizeof(f));
104  if (ret < 0) {
105  std::cerr << "Error receiving CAN frame" << std::endl;
106  return false;
107  }
108 
109  LOG_TRACE << "Reveived CAN message in Send method AF: 0x" << std::hex << f.can_id << "; Data:";
110  LOG_TRACE << " " << boost::algorithm::hex(std::string(reinterpret_cast<const char*>(f.data), f.can_dlc));
111 
112  if (!isotp_receive_flowcontrol(&isotp_shims, &send_handle, static_cast<uint16_t>(f.can_id), f.data,
113  f.can_dlc)) {
114  std::cerr << "IsoTp receiving error" << std::endl;
115  return false;
116  }
117 
118  while (send_handle.to_send != 0) {
119  if (send_handle.gap_ms != 0) {
120  std::this_thread::sleep_for(std::chrono::milliseconds(send_handle.gap_ms));
121  }
122  if (send_handle.gap_us != 0) {
123  std::this_thread::sleep_for(std::chrono::microseconds(send_handle.gap_us));
124  }
125 
126  if (!isotp_continue_send(&isotp_shims, &send_handle)) {
127  LOG_ERROR << "IsoTp sending error";
128  return false;
129  }
130  if (send_handle.completed) {
131  // Wait before (potentially) sending another packet
132  if (send_handle.gap_ms != 0) {
133  std::this_thread::sleep_for(std::chrono::milliseconds(send_handle.gap_ms));
134  }
135  if (send_handle.gap_us != 0) {
136  std::this_thread::sleep_for(std::chrono::microseconds(send_handle.gap_us));
137  }
138 
139  if (send_handle.success) {
140  return true;
141  }
142  LOG_ERROR << "IsoTp send failed";
143  return false;
144  }
145  }
146 
147  } else {
148  LOG_TRACE << "Timeout on CAN socket";
149  return true;
150  }
151  if (send_handle.completed) {
152  break;
153  }
154  } else {
155  std::cerr << "Select failed" << std::endl;
156  return false;
157  }
158  }
159  }
160  return false;
161 }
162 
163 bool IsoTpSendRecv::Recv(std::string* in) {
164  IsoTpReceiveHandle recv_handle = isotp_receive(&isotp_shims, canaddr_tx, canaddr_rx, nullptr);
165 
166  while (true) {
167  fd_set read_set;
168  FD_ZERO(&read_set); // NOLINT(readability-isolate-declaration)
169  FD_SET(can_socket, &read_set);
170 
171  // struct timeval timeout = {0, 2000000}; // 20 ms
172  // if (select((can_socket + 1), &read_set, nullptr, nullptr, &timeout) >= 0) {
173  if (select((can_socket + 1), &read_set, nullptr, nullptr, nullptr) >= 0) {
174  if (FD_ISSET(can_socket, &read_set)) {
175  struct can_frame f {};
176  ssize_t ret = read(can_socket, &f, sizeof(f));
177  if (ret < 0) {
178  std::cerr << "Error receiving CAN frame" << std::endl;
179  return false;
180  }
181 
182  LOG_TRACE << "Reveived CAN message in Recv method AF: 0x" << std::hex << f.can_id << "; Data:";
183  LOG_TRACE << " " << boost::algorithm::hex(std::string(reinterpret_cast<const char*>(f.data), f.can_dlc));
184  // std::this_thread::sleep_for(std::chrono::milliseconds(10)); // hack for RIOT to start waiting for flow
185  // control
186  IsoTpMessage message_rx = isotp_continue_receive(&isotp_shims, &recv_handle, f.can_id, f.data, f.can_dlc);
187  if (message_rx.completed && recv_handle.completed) {
188  if (!recv_handle.success) {
189  std::cerr << "IsoTp receiving error" << std::endl;
190  return false;
191  }
192  *in = std::string(reinterpret_cast<const char*>(message_rx.payload), static_cast<size_t>(message_rx.size));
193  return true;
194  }
195  } else {
196  LOG_TRACE << "Timeout on CAN socket";
197  *in = "";
198  return false;
199  }
200  } else {
201  LOG_ERROR << "Select failed";
202  return false;
203  }
204  }
205 }
General data structures.
Definition: types.cc:55