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 | |
12 | QT_BEGIN_NAMESPACE |
13 | |
14 | Q_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 | |
25 | struct 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 | |
36 | enum 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 | |
46 | GENERATE_SYMBOL(int, can_do_restart, const char * /* name */) |
47 | GENERATE_SYMBOL(int, can_do_stop, const char * /* name */) |
48 | GENERATE_SYMBOL(int, can_do_start, const char * /* name */) |
49 | GENERATE_SYMBOL(int, can_set_bitrate, const char * /* name */, quint32 /* bitrate */) |
50 | GENERATE_SYMBOL(int, can_get_bittiming, const char * /* name */, struct can_bittiming * /* bt */) |
51 | GENERATE_SYMBOL(int, can_get_state, const char * /* name */, int * /* state */) |
52 | |
53 | LibSocketCan::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 | */ |
98 | bool 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 | */ |
114 | bool 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 | */ |
132 | bool 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 | */ |
146 | quint32 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 | */ |
166 | bool 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 | |
176 | bool LibSocketCan::hasBusStatus() const |
177 | { |
178 | return ::can_get_state != nullptr; |
179 | } |
180 | |
181 | QCanBusDevice::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 | |
209 | QT_END_NAMESPACE |
210 | |