1// Copyright (C) 2019 Andre Hartmann <aha_1980@gmx.de>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "libsocketcan.h"
5
6#include <QtCore/qloggingcategory.h>
7
8#if QT_CONFIG(library)
9# include <QtCore/qlibrary.h>
10#endif
11
12QT_BEGIN_NAMESPACE
13
14Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_SOCKETCAN)
15
16#define GENERATE_SYMBOL(returnType, symbolName, ...) \
17 typedef returnType (*fp_##symbolName)(__VA_ARGS__); \
18 static fp_##symbolName symbolName = nullptr;
19
20#define RESOLVE_SYMBOL(symbolName) \
21 symbolName = reinterpret_cast<fp_##symbolName>(library->resolve(#symbolName)); \
22 if (!symbolName) \
23 return false;
24
25struct can_bittiming {
26 quint32 bitrate = 0; /* Bit-rate in bits/second */
27 quint32 sample_point = 0; /* Sample point in one-tenth of a percent */
28 quint32 tq = 0; /* Time quanta (TQ) in nanoseconds */
29 quint32 prop_seg = 0; /* Propagation segment in TQs */
30 quint32 phase_seg1 = 0; /* Phase buffer segment 1 in TQs */
31 quint32 phase_seg2 = 0; /* Phase buffer segment 2 in TQs */
32 quint32 sjw = 0; /* Synchronization jump width in TQs */
33 quint32 brp = 0; /* Bit-rate prescaler */
34};
35
36enum can_state {
37 CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */
38 CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */
39 CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */
40 CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */
41 CAN_STATE_STOPPED, /* Device is stopped */
42 CAN_STATE_SLEEPING, /* Device is sleeping */
43 CAN_STATE_MAX
44};
45
46GENERATE_SYMBOL(int, can_do_restart, const char * /* name */)
47GENERATE_SYMBOL(int, can_do_stop, const char * /* name */)
48GENERATE_SYMBOL(int, can_do_start, const char * /* name */)
49GENERATE_SYMBOL(int, can_set_bitrate, const char * /* name */, quint32 /* bitrate */)
50GENERATE_SYMBOL(int, can_get_bittiming, const char * /* name */, struct can_bittiming * /* bt */)
51GENERATE_SYMBOL(int, can_get_state, const char * /* name */, int * /* state */)
52
53LibSocketCan::LibSocketCan(QString *errorString)
54{
55#if QT_CONFIG(library)
56 auto resolveSymbols = [](QLibrary *library) {
57 const QString libName = QStringLiteral("socketcan");
58 if (!library->isLoaded()) {
59 library->setFileName(libName);
60 if (!library->load()) {
61 library->setFileNameAndVersion(fileName: libName, verNum: 2);
62 if (!library->load())
63 return false;
64 }
65 }
66
67 RESOLVE_SYMBOL(can_do_start);
68 RESOLVE_SYMBOL(can_do_stop);
69 RESOLVE_SYMBOL(can_do_restart);
70 RESOLVE_SYMBOL(can_set_bitrate);
71 RESOLVE_SYMBOL(can_get_bittiming);
72 RESOLVE_SYMBOL(can_get_state);
73
74 return true;
75 };
76
77 QLibrary lib;
78 if (Q_UNLIKELY(!resolveSymbols(&lib))) {
79 qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(lib.errorString()));
80 if (errorString)
81 *errorString = lib.errorString();
82 }
83#else
84 const QString error =
85 QObject::tr("Cannot load library libsocketcan as Qt was built without QLibrary.");
86 qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(error));
87 if (errorString)
88 *errorString = error;
89#endif
90}
91
92/*!
93 Brings the CAN \a interface up.
94
95 \internal
96 \note Requires appropriate permissions.
97*/
98bool LibSocketCan::start(const QString &interface)
99{
100 if (!::can_do_start) {
101 qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_start() is not available.");
102 return false;
103 }
104
105 return ::can_do_start(interface.toLatin1().constData()) == 0;
106}
107
108/*!
109 Brings the CAN \a interface down.
110
111 \internal
112 \note Requires appropriate permissions.
113*/
114bool LibSocketCan::stop(const QString &interface)
115{
116 if (!::can_do_stop) {
117 qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_stop() is not available.");
118 return false;
119 }
120
121 return ::can_do_stop(interface.toLatin1().constData()) == 0;
122}
123
124/*!
125 Performs a CAN controller reset on the CAN \a interface.
126
127 \internal
128 \note Reset can only be triggerd if the controller is in bus off
129 and the auto restart not turned on.
130 \note Requires appropriate permissions.
131 */
132bool LibSocketCan::restart(const QString &interface)
133{
134 if (!::can_do_restart) {
135 qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_restart() is not available.");
136 return false;
137 }
138
139 return ::can_do_restart(interface.toLatin1().constData()) == 0;
140}
141
142/*!
143 Returns the configured bitrate for \a interface.
144 \internal
145*/
146quint32 LibSocketCan::bitrate(const QString &interface) const
147{
148 if (!::can_get_bittiming) {
149 qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_get_bittiming() is not available.");
150 return 0;
151 }
152
153 struct can_bittiming bt;
154 if (::can_get_bittiming(interface.toLatin1().constData(), &bt) == 0)
155 return bt.bitrate;
156
157 return 0;
158}
159
160/*!
161 Sets the bitrate for the CAN \a interface.
162
163 \internal
164 \note Requires appropriate permissions.
165 */
166bool LibSocketCan::setBitrate(const QString &interface, quint32 bitrate)
167{
168 if (!::can_set_bitrate) {
169 qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_set_bitrate() is not available.");
170 return false;
171 }
172
173 return ::can_set_bitrate(interface.toLatin1().constData(), bitrate) == 0;
174}
175
176bool LibSocketCan::hasBusStatus() const
177{
178 return ::can_get_state != nullptr;
179}
180
181QCanBusDevice::CanBusStatus LibSocketCan::busStatus(const QString &interface) const
182{
183 if (!::can_get_state) {
184 qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_get_state() is not available.");
185 return QCanBusDevice::CanBusStatus::Unknown;
186 }
187
188 int status = 0;
189 int result = ::can_get_state(interface.toLatin1().constData(), &status);
190
191 if (result < 0)
192 return QCanBusDevice::CanBusStatus::Unknown;
193
194 switch (status) {
195 case CAN_STATE_ERROR_ACTIVE:
196 return QCanBusDevice::CanBusStatus::Good;
197 case CAN_STATE_ERROR_WARNING:
198 return QCanBusDevice::CanBusStatus::Warning;
199 case CAN_STATE_ERROR_PASSIVE:
200 return QCanBusDevice::CanBusStatus::Error;
201 case CAN_STATE_BUS_OFF:
202 return QCanBusDevice::CanBusStatus::BusOff;
203 default:
204 // Device is stopped or sleeping, so status is unknown
205 return QCanBusDevice::CanBusStatus::Unknown;
206 }
207}
208
209QT_END_NAMESPACE
210

source code of qtserialbus/src/plugins/canbus/socketcan/libsocketcan.cpp