1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 Andre Hartmann <aha_1980@gmx.de>
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtSerialBus module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36 
37 #include "libsocketcan.h"
38 
39 #include <QtCore/qloggingcategory.h>
40 
41 #if QT_CONFIG(library)
42 #   include <QtCore/qlibrary.h>
43 #endif
44 
45 QT_BEGIN_NAMESPACE
46 
47 Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_SOCKETCAN)
48 
49 #define GENERATE_SYMBOL(returnType, symbolName, ...) \
50     typedef returnType (*fp_##symbolName)(__VA_ARGS__); \
51     static fp_##symbolName symbolName = nullptr;
52 
53 #define RESOLVE_SYMBOL(symbolName) \
54     symbolName = reinterpret_cast<fp_##symbolName>(library->resolve(#symbolName)); \
55     if (!symbolName) \
56         return false;
57 
58 struct can_bittiming {
59     quint32 bitrate = 0;      /* Bit-rate in bits/second */
60     quint32 sample_point = 0; /* Sample point in one-tenth of a percent */
61     quint32 tq = 0;           /* Time quanta (TQ) in nanoseconds */
62     quint32 prop_seg = 0;     /* Propagation segment in TQs */
63     quint32 phase_seg1 = 0;   /* Phase buffer segment 1 in TQs */
64     quint32 phase_seg2 = 0;   /* Phase buffer segment 2 in TQs */
65     quint32 sjw = 0;          /* Synchronization jump width in TQs */
66     quint32 brp = 0;          /* Bit-rate prescaler */
67 };
68 
69 enum can_state {
70     CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */
71     CAN_STATE_ERROR_WARNING,    /* RX/TX error count < 128 */
72     CAN_STATE_ERROR_PASSIVE,    /* RX/TX error count < 256 */
73     CAN_STATE_BUS_OFF,          /* RX/TX error count >= 256 */
74     CAN_STATE_STOPPED,          /* Device is stopped */
75     CAN_STATE_SLEEPING,         /* Device is sleeping */
76     CAN_STATE_MAX
77 };
78 
GENERATE_SYMBOL(int,can_do_restart,const char *)79 GENERATE_SYMBOL(int, can_do_restart, const char * /* name */)
80 GENERATE_SYMBOL(int, can_do_stop, const char * /* name */)
81 GENERATE_SYMBOL(int, can_do_start, const char * /* name */)
82 GENERATE_SYMBOL(int, can_set_bitrate, const char * /* name */, quint32 /* bitrate */)
83 GENERATE_SYMBOL(int, can_get_bittiming, const char * /* name */, struct can_bittiming * /* bt */)
84 GENERATE_SYMBOL(int, can_get_state, const char * /* name */, int * /* state */)
85 
86 LibSocketCan::LibSocketCan(QString *errorString)
87 {
88 #if QT_CONFIG(library)
89     auto resolveSymbols = [](QLibrary *library) {
90         const QString libName = QStringLiteral("socketcan");
91         if (!library->isLoaded()) {
92             library->setFileName(libName);
93             if (!library->load()) {
94                 library->setFileNameAndVersion(libName, 2);
95                 if (!library->load())
96                     return false;
97             }
98         }
99 
100         RESOLVE_SYMBOL(can_do_start);
101         RESOLVE_SYMBOL(can_do_stop);
102         RESOLVE_SYMBOL(can_do_restart);
103         RESOLVE_SYMBOL(can_set_bitrate);
104         RESOLVE_SYMBOL(can_get_bittiming);
105         RESOLVE_SYMBOL(can_get_state);
106 
107         return true;
108     };
109 
110     QLibrary lib;
111     if (Q_UNLIKELY(!resolveSymbols(&lib))) {
112         qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(lib.errorString()));
113         if (errorString)
114             *errorString = lib.errorString();
115     }
116 #else
117     const QString error =
118             QObject::tr("Cannot load library libsocketcan as Qt was built without QLibrary.");
119     qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(error));
120     if (errorString)
121         *errorString = error;
122 #endif
123 }
124 
125 /*!
126     Brings the CAN \a interface up.
127 
128     \internal
129     \note Requires appropriate permissions.
130 */
start(const QString & interface)131 bool LibSocketCan::start(const QString &interface)
132 {
133     if (!::can_do_start) {
134         qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_start() is not available.");
135         return false;
136     }
137 
138     return ::can_do_start(interface.toLatin1().constData()) == 0;
139 }
140 
141 /*!
142     Brings the CAN \a interface down.
143 
144     \internal
145     \note Requires appropriate permissions.
146 */
stop(const QString & interface)147 bool LibSocketCan::stop(const QString &interface)
148 {
149     if (!::can_do_stop) {
150         qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_stop() is not available.");
151         return false;
152     }
153 
154     return ::can_do_stop(interface.toLatin1().constData()) == 0;
155 }
156 
157 /*!
158     Performs a CAN controller reset on the CAN \a interface.
159 
160     \internal
161     \note Reset can only be triggerd if the controller is in bus off
162     and the auto restart not turned on.
163     \note Requires appropriate permissions.
164  */
restart(const QString & interface)165 bool LibSocketCan::restart(const QString &interface)
166 {
167     if (!::can_do_restart) {
168         qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_restart() is not available.");
169         return false;
170     }
171 
172     return ::can_do_restart(interface.toLatin1().constData()) == 0;
173 }
174 
175 /*!
176     Returns the configured bitrate for \a interface.
177     \internal
178 */
bitrate(const QString & interface) const179 quint32 LibSocketCan::bitrate(const QString &interface) const
180 {
181     if (!::can_get_bittiming) {
182         qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_get_bittiming() is not available.");
183         return 0;
184     }
185 
186     struct can_bittiming bt;
187     if (::can_get_bittiming(interface.toLatin1().constData(), &bt) == 0)
188         return bt.bitrate;
189 
190     return 0;
191 }
192 
193 /*!
194     Sets the bitrate for the CAN \a interface.
195 
196     \internal
197     \note Requires appropriate permissions.
198  */
setBitrate(const QString & interface,quint32 bitrate)199 bool LibSocketCan::setBitrate(const QString &interface, quint32 bitrate)
200 {
201     if (!::can_set_bitrate) {
202         qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_set_bitrate() is not available.");
203         return false;
204     }
205 
206     return ::can_set_bitrate(interface.toLatin1().constData(), bitrate) == 0;
207 }
208 
hasBusStatus() const209 bool LibSocketCan::hasBusStatus() const
210 {
211     return ::can_get_state != nullptr;
212 }
213 
busStatus(const QString & interface) const214 QCanBusDevice::CanBusStatus LibSocketCan::busStatus(const QString &interface) const
215 {
216     if (!::can_get_state) {
217         qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_get_state() is not available.");
218         return QCanBusDevice::CanBusStatus::Unknown;
219     }
220 
221     int status = 0;
222     int result = ::can_get_state(interface.toLatin1().constData(), &status);
223 
224     if (result < 0)
225         return QCanBusDevice::CanBusStatus::Unknown;
226 
227     switch (status) {
228     case CAN_STATE_ERROR_ACTIVE:
229         return QCanBusDevice::CanBusStatus::Good;
230     case CAN_STATE_ERROR_WARNING:
231         return QCanBusDevice::CanBusStatus::Warning;
232     case CAN_STATE_ERROR_PASSIVE:
233         return QCanBusDevice::CanBusStatus::Error;
234     case CAN_STATE_BUS_OFF:
235         return QCanBusDevice::CanBusStatus::BusOff;
236     default:
237         // Device is stopped or sleeping, so status is unknown
238         return QCanBusDevice::CanBusStatus::Unknown;
239     }
240 }
241 
242 QT_END_NAMESPACE
243