| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2017 The Qt Company Ltd. | 
| 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 "qcanbusdevice.h" | 
| 38 | #include "qcanbusdevice_p.h" | 
| 39 | #include "qcanbusdeviceinfo_p.h" | 
| 40 |  | 
| 41 | #include "qcanbusframe.h" | 
| 42 |  | 
| 43 | #include <QtCore/qdebug.h> | 
| 44 | #include <QtCore/qdatastream.h> | 
| 45 | #include <QtCore/qeventloop.h> | 
| 46 | #include <QtCore/qloggingcategory.h> | 
| 47 | #include <QtCore/qscopedvaluerollback.h> | 
| 48 | #include <QtCore/qtimer.h> | 
| 49 |  | 
| 50 | QT_BEGIN_NAMESPACE | 
| 51 |  | 
| 52 | Q_LOGGING_CATEGORY(QT_CANBUS, "qt.canbus" ) | 
| 53 |  | 
| 54 | /*! | 
| 55 |     \class QCanBusDevice | 
| 56 |     \inmodule QtSerialBus | 
| 57 |     \since 5.8 | 
| 58 |  | 
| 59 |     \brief The QCanBusDevice class is the interface class for CAN bus. | 
| 60 |  | 
| 61 |     QCanBusDevice communicates with a CAN plugin providing users with a convenient API. | 
| 62 |     The CAN plugin must be specified during the object creation. | 
| 63 | */ | 
| 64 |  | 
| 65 | /*! | 
| 66 |     \enum QCanBusDevice::CanBusError | 
| 67 |     This enum describes all the possible error conditions. | 
| 68 |  | 
| 69 |     \value NoError              No errors have occurred. | 
| 70 |     \value ReadError            An error occurred during a read operation. | 
| 71 |     \value WriteError           An error occurred during a write operation. | 
| 72 |     \value ConnectionError      An error occurred when attempting to open the plugin. | 
| 73 |     \value ConfigurationError   An error occurred when attempting to set a configuration | 
| 74 |                                 parameter. | 
| 75 |     \value UnknownError         An unknown error occurred. | 
| 76 |     \value OperationError       An operation was attempted while the device was in | 
| 77 |                                 a state that did not permit it. This enum was introduced | 
| 78 |                                 in Qt 5.14. | 
| 79 |     \value TimeoutError         An timeout occurred while waiting for frames written or | 
| 80 |                                 received. This enum was introduced in Qt 5.14. | 
| 81 | */ | 
| 82 |  | 
| 83 | /*! | 
| 84 |     \enum QCanBusDevice::CanBusDeviceState | 
| 85 |     This enum describes all possible device states. | 
| 86 |  | 
| 87 |     \value UnconnectedState The device is disconnected. | 
| 88 |     \value ConnectingState  The device is being connected. | 
| 89 |     \value ConnectedState   The device is connected to the CAN bus. | 
| 90 |     \value ClosingState     The device is being closed. | 
| 91 | */ | 
| 92 |  | 
| 93 | /*! | 
| 94 |     \enum QCanBusDevice::ConfigurationKey | 
| 95 |     This enum describes the possible configuration options for | 
| 96 |     the CAN bus connection. | 
| 97 |  | 
| 98 |     \value RawFilterKey     This configuration determines the type of CAN bus frames | 
| 99 |                             that the current device accepts. The expected value | 
| 100 |                             is \c QList<QCanBusDevice::Filter>. Passing an empty list clears | 
| 101 |                             all previously set filters including default filters. For more details | 
| 102 |                             see \l QCanBusDevice::Filter. | 
| 103 |     \value ErrorFilterKey   This key defines the type of error that should be | 
| 104 |                             forwarded via the current connection. The associated | 
| 105 |                             value should be of type \l QCanBusFrame::FrameErrors. | 
| 106 |     \value LoopbackKey      This key defines whether the CAN bus device should operate in loopback | 
| 107 |                             mode. Loopback means, whenever a CAN frame is transmitted on the CAN | 
| 108 |                             bus, a local echo of this frame is sent to all applications connected to | 
| 109 |                             this CAN device. The expected value for this key is \c bool. | 
| 110 |     \value ReceiveOwnKey    This key defines whether this CAN device receives its own send frames. | 
| 111 |                             This can be used to check if the transmission was successful. | 
| 112 |                             The expected value for this key is \c bool. | 
| 113 |     \value BitRateKey       This key defines the CAN bitrate in bits per second. With CAN FD, | 
| 114 |                             the payload can be transmitted at a higher data bitrate, | 
| 115 |                             if \l QCanBusFrame::hasBitrateSwitch() is set. In this case, | 
| 116 |                             \c QCanBusDevice::BitRateKey is only used for the CAN ID arbitration | 
| 117 |                             phase. See also \c QCanBusDevice::DataBitRateKey | 
| 118 |     \value CanFdKey         This key defines whether sending and receiving of CAN FD frames | 
| 119 |                             should be enabled. The expected value for this key is \c bool. | 
| 120 |     \value DataBitRateKey   This key defines the CAN FD payload bitrate in bits per second. | 
| 121 |                             CAN FD allows to transmit the payload of frames with | 
| 122 |                             \l QCanBusFrame::hasBitrateSwitch() flag at a higher data bitrate, | 
| 123 |                             after the arbitration phase at the nominal bitrate is finished. | 
| 124 |                             This enum value was introduced in Qt 5.9. | 
| 125 |                             See also \c QCanBusDevice::BitRateKey | 
| 126 |     \value ProtocolKey      This key allows to specify another protocol. For now, this | 
| 127 |                             parameter can only be set and used in the SocketCAN plugin. | 
| 128 |                             This enum value was introduced in Qt 5.14. | 
| 129 |     \value UserKey          This key defines the range where custom keys start. Its most | 
| 130 |                             common purpose is to permit platform-specific configuration | 
| 131 |                             options. | 
| 132 |  | 
| 133 |     \sa configurationParameter() | 
| 134 | */ | 
| 135 |  | 
| 136 | /*! | 
| 137 |     \class QCanBusDevice::Filter | 
| 138 |     \inmodule QtSerialBus | 
| 139 |     \since 5.8 | 
| 140 |  | 
| 141 |     \brief The QCanBusDevice::Filter struct defines a filter for CAN bus frames. | 
| 142 |  | 
| 143 |     A list of QCanBusDevice::Filter instances is passed to | 
| 144 |     \l QCanBusDevice::setConfigurationParameter() to enable filtering. If a received CAN frame | 
| 145 |     matches at least one of the filters in the list, the QCanBusDevice will accept it. | 
| 146 |  | 
| 147 |     The example below demonstrates how to use the struct: | 
| 148 |  | 
| 149 |     \snippet snippetmain.cpp Filter Examples | 
| 150 | */ | 
| 151 |  | 
| 152 | /*! | 
| 153 |     \fn bool operator==(const QCanBusDevice::Filter &a, const QCanBusDevice::Filter &b) | 
| 154 |     \relates QCanBusDevice::Filter | 
| 155 |  | 
| 156 |     Returns true, if the filter \a a is equal to the filter \a b, | 
| 157 |     otherwise returns false. | 
| 158 | */ | 
| 159 |  | 
| 160 | /*! | 
| 161 |     \fn bool operator!=(const QCanBusDevice::Filter &a, const QCanBusDevice::Filter &b) | 
| 162 |     \relates QCanBusDevice::Filter | 
| 163 |  | 
| 164 |     Returns true, if the filter \a a is not equal to the filter \a b, | 
| 165 |     otherwise returns false. | 
| 166 | */ | 
| 167 |  | 
| 168 | /*! | 
| 169 |     \enum QCanBusDevice::Filter::FormatFilter | 
| 170 |     This enum describes the format pattern, which is used to filter incoming | 
| 171 |     CAN bus frames. | 
| 172 |  | 
| 173 |     \value MatchBaseFormat              The CAN bus frame must use the base frame format | 
| 174 |                                         (11 bit identifier). | 
| 175 |     \value MatchExtendedFormat          The CAN bus frame must use the extended frame format | 
| 176 |                                         (29 bit identifier). | 
| 177 |     \value MatchBaseAndExtendedFormat   The CAN bus frame can have a base or an extended | 
| 178 |                                         frame format. | 
| 179 | */ | 
| 180 |  | 
| 181 | /*! | 
| 182 |     \variable QCanBusDevice::Filter::frameId | 
| 183 |  | 
| 184 |     \brief The frame id used to filter the incoming frames. | 
| 185 |  | 
| 186 |     The frameId is used in conjunction with \a frameIdMask. | 
| 187 |     The matching is successful if the following evaluates to \c true: | 
| 188 |  | 
| 189 |     \code | 
| 190 |         (receivedFrameId & frameIdMask) == (frameId & frameIdMask) | 
| 191 |     \endcode | 
| 192 |  | 
| 193 |     By default this field is set to \c 0x0. | 
| 194 |  | 
| 195 |     \sa frameIdMask | 
| 196 | */ | 
| 197 |  | 
| 198 | /*! | 
| 199 |     \variable QCanBusDevice::Filter::frameIdMask | 
| 200 |  | 
| 201 |     \brief The bit mask that is applied to the frame id of the filter and the received frame. | 
| 202 |  | 
| 203 |     The two frame ids are matching if the following evaluates to \c true: | 
| 204 |  | 
| 205 |     \code | 
| 206 |         (receivedFrameId & frameIdMask) == (frameId & frameIdMask) | 
| 207 |     \endcode | 
| 208 |  | 
| 209 |     By default this field is set to \c 0x0. | 
| 210 |  | 
| 211 |     \sa frameId | 
| 212 | */ | 
| 213 |  | 
| 214 | /*! | 
| 215 |     \variable QCanBusDevice::Filter::type | 
| 216 |  | 
| 217 |     \brief The type of the frame to be filtered. | 
| 218 |  | 
| 219 |     Any CAN bus frame type can be matched by setting this variable | 
| 220 |     to \l QCanBusFrame::InvalidFrame. The filter object is invalid if | 
| 221 |     type is equal to \l QCanBusFrame::UnknownFrame. | 
| 222 |  | 
| 223 |     By default this field is set to \l QCanBusFrame::InvalidFrame. | 
| 224 |  | 
| 225 |     \sa QCanBusFrame::FrameType | 
| 226 | */ | 
| 227 |  | 
| 228 | /*! | 
| 229 |     \variable QCanBusDevice::Filter::format | 
| 230 |  | 
| 231 |     \brief The frame format of the matching CAN bus frame. | 
| 232 |  | 
| 233 |     By default this field is set to \l QCanBusDevice::Filter::MatchBaseAndExtendedFormat. | 
| 234 | */ | 
| 235 |  | 
| 236 | /*! | 
| 237 |     \fn void QCanBusDevice::errorOccurred(CanBusError) | 
| 238 |  | 
| 239 |     This signal is emitted when an error occurs. | 
| 240 | */ | 
| 241 |  | 
| 242 | /*! | 
| 243 |     Constructs a serial bus device with the specified \a parent. | 
| 244 | */ | 
| 245 | QCanBusDevice::QCanBusDevice(QObject *parent) : | 
| 246 |     QObject(*new QCanBusDevicePrivate, parent) | 
| 247 | { | 
| 248 | } | 
| 249 |  | 
| 250 |  | 
| 251 | /*! | 
| 252 |     Sets the human readable description of the last device error to | 
| 253 |     \a errorText. \a errorId categorizes the type of error. | 
| 254 |  | 
| 255 |     CAN bus implementations must use this function to update the device's | 
| 256 |     error state. | 
| 257 |  | 
| 258 |     \sa error(), errorOccurred(), clearError() | 
| 259 | */ | 
| 260 | void QCanBusDevice::setError(const QString &errorText, CanBusError errorId) | 
| 261 | { | 
| 262 |     Q_D(QCanBusDevice); | 
| 263 |  | 
| 264 |     d->errorText = errorText; | 
| 265 |     d->lastError = errorId; | 
| 266 |  | 
| 267 |     emit errorOccurred(errorId); | 
| 268 | } | 
| 269 |  | 
| 270 | /*! | 
| 271 |     \since 5.14 | 
| 272 |     Clears the error id and the human readable description of the last | 
| 273 |     device error. | 
| 274 |  | 
| 275 |     CAN bus implementations must use this function to update the device's | 
| 276 |     error state. | 
| 277 |  | 
| 278 |     \sa error(), errorOccurred(), setError() | 
| 279 | */ | 
| 280 | void QCanBusDevice::clearError() | 
| 281 | { | 
| 282 |     Q_D(QCanBusDevice); | 
| 283 |  | 
| 284 |     d->errorText.clear(); | 
| 285 |     d->lastError = NoError; | 
| 286 | } | 
| 287 |  | 
| 288 | /*! | 
| 289 |     Appends \a newFrames to the internal list of frames which can be | 
| 290 |     accessed using \l readFrame() and emits the \l framesReceived() | 
| 291 |     signal. | 
| 292 |  | 
| 293 |     Subclasses must call this function when they receive frames. | 
| 294 |  | 
| 295 | */ | 
| 296 | void QCanBusDevice::enqueueReceivedFrames(const QVector<QCanBusFrame> &newFrames) | 
| 297 | { | 
| 298 |     Q_D(QCanBusDevice); | 
| 299 |  | 
| 300 |     if (Q_UNLIKELY(newFrames.isEmpty())) | 
| 301 |         return; | 
| 302 |  | 
| 303 |     d->incomingFramesGuard.lock(); | 
| 304 |     d->incomingFrames.append(l: newFrames); | 
| 305 |     d->incomingFramesGuard.unlock(); | 
| 306 |     emit framesReceived(); | 
| 307 | } | 
| 308 |  | 
| 309 | /*! | 
| 310 |     Appends \a newFrame to the internal list of outgoing frames which | 
| 311 |     can be accessed by \l writeFrame(). | 
| 312 |  | 
| 313 |     Subclasses must call this function when they write a new frame. | 
| 314 | */ | 
| 315 | void QCanBusDevice::enqueueOutgoingFrame(const QCanBusFrame &newFrame) | 
| 316 | { | 
| 317 |     Q_D(QCanBusDevice); | 
| 318 |  | 
| 319 |     d->outgoingFrames.append(t: newFrame); | 
| 320 | } | 
| 321 |  | 
| 322 | /*! | 
| 323 |     Returns the next \l QCanBusFrame from the internal list of outgoing frames; | 
| 324 |     otherwise returns an invalid QCanBusFrame. The returned frame is removed | 
| 325 |     from the internal list. | 
| 326 | */ | 
| 327 | QCanBusFrame QCanBusDevice::dequeueOutgoingFrame() | 
| 328 | { | 
| 329 |     Q_D(QCanBusDevice); | 
| 330 |  | 
| 331 |     if (Q_UNLIKELY(d->outgoingFrames.isEmpty())) | 
| 332 |         return QCanBusFrame(QCanBusFrame::InvalidFrame); | 
| 333 |     return d->outgoingFrames.takeFirst(); | 
| 334 | } | 
| 335 |  | 
| 336 | /*! | 
| 337 |     Returns \c true if the internal list of outgoing frames is not | 
| 338 |     empty; otherwise returns \c false. | 
| 339 | */ | 
| 340 | bool QCanBusDevice::hasOutgoingFrames() const | 
| 341 | { | 
| 342 |     Q_D(const QCanBusDevice); | 
| 343 |  | 
| 344 |     return !d->outgoingFrames.isEmpty(); | 
| 345 | } | 
| 346 |  | 
| 347 | /*! | 
| 348 |  * \since 5.14 | 
| 349 |  * Called from the derived plugin to register a function \a resetter which performs the | 
| 350 |  * CAN controller hardware reset when resetController() is called. | 
| 351 |  */ | 
| 352 | void QCanBusDevice::setResetControllerFunction(std::function<void()> resetter) | 
| 353 | { | 
| 354 |     Q_D(QCanBusDevice); | 
| 355 |  | 
| 356 |     d->m_resetControllerFunction = std::move(resetter); | 
| 357 | } | 
| 358 |  | 
| 359 | /*! | 
| 360 |  * \since 5.14 | 
| 361 |  * Called from the derived plugin to register a function \a busStatusGetter | 
| 362 |  * which returns the CAN controller bus status when busStatus() is called. | 
| 363 |  */ | 
| 364 | void QCanBusDevice::setCanBusStatusGetter(std::function<CanBusStatus()> busStatusGetter) | 
| 365 | { | 
| 366 |     Q_D(QCanBusDevice); | 
| 367 |  | 
| 368 |     d->m_busStatusGetter = std::move(busStatusGetter); | 
| 369 | } | 
| 370 |  | 
| 371 | /*! | 
| 372 |     Sets the configuration parameter \a key for the CAN bus connection | 
| 373 |     to \a value. The potential keys are represented by \l ConfigurationKey. | 
| 374 |  | 
| 375 |     A parameter can be unset by setting an invalid \l QVariant. | 
| 376 |     Unsetting a parameter implies that the configuration is reset to | 
| 377 |     its default setting. | 
| 378 |  | 
| 379 |     \note In most cases, configuration changes only take effect | 
| 380 |     after a reconnect. | 
| 381 |  | 
| 382 |     \sa configurationParameter() | 
| 383 | */ | 
| 384 | void QCanBusDevice::setConfigurationParameter(int key, const QVariant &value) | 
| 385 | { | 
| 386 |     Q_D(QCanBusDevice); | 
| 387 |  | 
| 388 |     for (int i = 0; i < d->configOptions.size(); i++) { | 
| 389 |         if (d->configOptions.at(i).first == key) { | 
| 390 |             if (value.isValid()) { | 
| 391 |                 ConfigEntry entry = d->configOptions.at(i); | 
| 392 |                 entry.second = value; | 
| 393 |                 d->configOptions.replace(i, t: entry); | 
| 394 |             } else { | 
| 395 |                 d->configOptions.remove(i); | 
| 396 |             } | 
| 397 |             return; | 
| 398 |         } | 
| 399 |     } | 
| 400 |  | 
| 401 |     if (!value.isValid()) | 
| 402 |         return; | 
| 403 |  | 
| 404 |     ConfigEntry newEntry(key, value); | 
| 405 |     d->configOptions.append(t: newEntry); | 
| 406 | } | 
| 407 |  | 
| 408 | /*! | 
| 409 |     Returns the current value assigned to the \l ConfigurationKey \a key; otherwise | 
| 410 |     an invalid \l QVariant. | 
| 411 |  | 
| 412 |     \sa setConfigurationParameter(), configurationKeys() | 
| 413 | */ | 
| 414 | QVariant QCanBusDevice::configurationParameter(int key) const | 
| 415 | { | 
| 416 |     Q_D(const QCanBusDevice); | 
| 417 |  | 
| 418 |     for (const ConfigEntry &e : d->configOptions) { | 
| 419 |         if (e.first == key) | 
| 420 |             return e.second; | 
| 421 |     } | 
| 422 |  | 
| 423 |     return QVariant(); | 
| 424 | } | 
| 425 |  | 
| 426 | /*! | 
| 427 |     Returns the list of keys used by the CAN bus connection. | 
| 428 |  | 
| 429 |     The the meaning of the keys is equivalent to \l ConfigurationKey. | 
| 430 |     If a key is not explicitly mentioned the platform's | 
| 431 |     default setting for the relevant key is used. | 
| 432 | */ | 
| 433 | QVector<int> QCanBusDevice::configurationKeys() const | 
| 434 | { | 
| 435 |     Q_D(const QCanBusDevice); | 
| 436 |  | 
| 437 |     QVector<int> result; | 
| 438 |     for (const ConfigEntry &e : d->configOptions) | 
| 439 |         result.append(t: e.first); | 
| 440 |  | 
| 441 |     return result; | 
| 442 | } | 
| 443 |  | 
| 444 | /*! | 
| 445 |     Returns the last error that has occurred. The error value is always set to last error that | 
| 446 |     occurred and it is never reset. | 
| 447 |  | 
| 448 |     \sa errorString() | 
| 449 | */ | 
| 450 | QCanBusDevice::CanBusError QCanBusDevice::error() const | 
| 451 | { | 
| 452 |     return d_func()->lastError; | 
| 453 | } | 
| 454 |  | 
| 455 | /*! | 
| 456 |     Returns a human-readable description of the last device error that occurred. | 
| 457 |  | 
| 458 |     \sa error() | 
| 459 | */ | 
| 460 | QString QCanBusDevice::errorString() const | 
| 461 | { | 
| 462 |     Q_D(const QCanBusDevice); | 
| 463 |  | 
| 464 |     if (d->lastError == QCanBusDevice::NoError) | 
| 465 |         return QString(); | 
| 466 |  | 
| 467 |     return d->errorText; | 
| 468 | } | 
| 469 |  | 
| 470 | /*! | 
| 471 |     Returns the number of available frames. If no frames are available, | 
| 472 |     this function returns 0. | 
| 473 |  | 
| 474 |     \sa clear(), readFrame(), readAllFrames() | 
| 475 | */ | 
| 476 | qint64 QCanBusDevice::framesAvailable() const | 
| 477 | { | 
| 478 |     return d_func()->incomingFrames.size(); | 
| 479 | } | 
| 480 |  | 
| 481 | /*! | 
| 482 |     For buffered devices, this function returns the number of frames waiting to be written. | 
| 483 |     For unbuffered devices, this function always returns zero. | 
| 484 |  | 
| 485 |     \note There may be additional buffering in the CAN driver and CAN hardware layer. | 
| 486 |     Therefore, if this function returns zero, that does not mean all CAN frames are | 
| 487 |     already written to the CAN bus. | 
| 488 |  | 
| 489 |     \sa clear(), writeFrame() | 
| 490 | */ | 
| 491 | qint64 QCanBusDevice::framesToWrite() const | 
| 492 | { | 
| 493 |     return d_func()->outgoingFrames.size(); | 
| 494 | } | 
| 495 |  | 
| 496 | /*! | 
| 497 |     \since 5.14 | 
| 498 |  | 
| 499 |     Performs a CAN controller reset to release the CAN controller from | 
| 500 |     bus off state, if possible. | 
| 501 |  | 
| 502 |     \note CAN controller resets disturb the running communication and | 
| 503 |     may take up to one second to complete. Only call this function to | 
| 504 |     recover from bus errors. | 
| 505 |  | 
| 506 |     \note This function may not be implemented in all CAN plugins. | 
| 507 |     Please refer to the plugins help pages for more information. | 
| 508 |  | 
| 509 |     \sa busStatus() | 
| 510 | */ | 
| 511 | void QCanBusDevice::resetController() | 
| 512 | { | 
| 513 |     if (d_func()->m_resetControllerFunction) { | 
| 514 |         d_func()->m_resetControllerFunction(); | 
| 515 |     } else { | 
| 516 |         const char error[] = QT_TRANSLATE_NOOP("QCanBusDevice" , | 
| 517 |                 "This CAN bus plugin does not support hardware controller reset." ); | 
| 518 |         qCWarning(QT_CANBUS, error); | 
| 519 |         setError(errorText: tr(s: error), errorId: QCanBusDevice::CanBusError::ConfigurationError); | 
| 520 |     } | 
| 521 | } | 
| 522 |  | 
| 523 | /*! | 
| 524 |     \since 5.14 | 
| 525 |  | 
| 526 |     Return true, if the CAN plugin supports requesting the CAN bus status. | 
| 527 |  | 
| 528 |     \sa busStatus() | 
| 529 |  */ | 
| 530 | bool QCanBusDevice::hasBusStatus() const | 
| 531 | { | 
| 532 |     return d_func()->m_busStatusGetter != nullptr; | 
| 533 | } | 
| 534 |  | 
| 535 | /*! | 
| 536 |     \since 5.14 | 
| 537 |     \enum QCanBusDevice::CanBusStatus | 
| 538 |  | 
| 539 |     This enum describes possible CAN bus status values. | 
| 540 |  | 
| 541 |     \value Unknown  The CAN bus status is unknown | 
| 542 |                     (e.g. not supported by the CAN plugin). | 
| 543 |     \value Good     The CAN controller is fully operational | 
| 544 |     \value Warning  The CAN controller is in warning status | 
| 545 |     \value Error    The CAN controller is in error status | 
| 546 |                     (no longer sending CAN frames) | 
| 547 |     \value BusOff   The CAN controller is in bus off status | 
| 548 |                     (disconnected from the CAN bus) | 
| 549 | */ | 
| 550 |  | 
| 551 | /*! | 
| 552 |     \since 5.14 | 
| 553 |  | 
| 554 |     Returns the current CAN bus status. If the status cannot be requested, | 
| 555 |     QCanBusDevice::UnknownStatus is returned. | 
| 556 |  | 
| 557 |     \note This function may not be implemented in all CAN plugins. | 
| 558 |     Please refer to the plugins help pages for more information. | 
| 559 |     The function hasBusStatus() can be used at runtime to check if | 
| 560 |     the used CAN plugin has support for requesting the CAN bus status. | 
| 561 |  | 
| 562 |     \sa hasBusStatus(), resetController() | 
| 563 | */ | 
| 564 | QCanBusDevice::CanBusStatus QCanBusDevice::busStatus() const | 
| 565 | { | 
| 566 |     if (d_func()->m_busStatusGetter) | 
| 567 |         return d_func()->m_busStatusGetter(); | 
| 568 |  | 
| 569 |     return QCanBusDevice::CanBusStatus::Unknown; | 
| 570 | } | 
| 571 |  | 
| 572 | /*! | 
| 573 |     \since 5.12 | 
| 574 |     \enum QCanBusDevice::Direction | 
| 575 |  | 
| 576 |     This enum describes possible data transmission directions. | 
| 577 |  | 
| 578 |     \value Input            Input direction. | 
| 579 |     \value Output           Output direction. | 
| 580 |     \value AllDirections    Both directions, input and output. | 
| 581 | */ | 
| 582 |  | 
| 583 | /*! | 
| 584 |     \since 5.12 | 
| 585 |     Clears the devices input or output buffers, depending on \a direction. | 
| 586 |  | 
| 587 |     This function only operates on QCanBusDevice buffers. Frames that are | 
| 588 |     already written to the CAN driver or CAN hardware layer, or that are | 
| 589 |     not yet read from these layers, are not cleared by this function. | 
| 590 |  | 
| 591 |     \note Clearing the output buffers is only possible for buffered devices. | 
| 592 |  | 
| 593 |     \sa framesAvailable(), readFrame(), framesToWrite(), writeFrame(), | 
| 594 | */ | 
| 595 | void QCanBusDevice::clear(QCanBusDevice::Directions direction) | 
| 596 | { | 
| 597 |     Q_D(QCanBusDevice); | 
| 598 |  | 
| 599 |     if (Q_UNLIKELY(d->state != ConnectedState)) { | 
| 600 |         const QString error = tr(s: "Cannot clear buffers as device is not connected." ); | 
| 601 |         qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); | 
| 602 |         setError(errorText: error, errorId: CanBusError::OperationError); | 
| 603 |         return; | 
| 604 |     } | 
| 605 |  | 
| 606 |     clearError(); | 
| 607 |  | 
| 608 |     if (direction & Direction::Input) { | 
| 609 |         QMutexLocker locker(&d->incomingFramesGuard); | 
| 610 |         d->incomingFrames.clear(); | 
| 611 |     } | 
| 612 |  | 
| 613 |     if (direction & Direction::Output) | 
| 614 |         d->outgoingFrames.clear(); | 
| 615 | } | 
| 616 |  | 
| 617 | /*! | 
| 618 |     For buffered devices, this function waits until all buffered frames | 
| 619 |     have been written to the device and the \l framesWritten() signal has been emitted, | 
| 620 |     or until \a msecs milliseconds have passed. If \a msecs is -1, | 
| 621 |     this function will not time out. For unbuffered devices, it returns immediately with \c false | 
| 622 |     as \l writeFrame() does not require a write buffer. | 
| 623 |  | 
| 624 |     Returns \c true if the \l framesWritten() signal is emitted; | 
| 625 |     otherwise returns \c false (i.e. if the operation timed out, or if an error occurred). | 
| 626 |  | 
| 627 |     \note This function will start a local event loop. This may lead to scenarios whereby | 
| 628 |     other application slots may be called while the execution of this function scope is blocking. | 
| 629 |     To avoid problems, the signals for this class should not be connected to slots. | 
| 630 |     Similarly this function must never be called in response to the \l framesWritten() | 
| 631 |     or \l errorOccurred() signals. | 
| 632 |  | 
| 633 |     \sa waitForFramesReceived() | 
| 634 |  */ | 
| 635 | bool QCanBusDevice::waitForFramesWritten(int msecs) | 
| 636 | { | 
| 637 |     // do not enter this function recursively | 
| 638 |     if (Q_UNLIKELY(d_func()->waitForWrittenEntered)) { | 
| 639 |         qCWarning(QT_CANBUS, "QCanBusDevice::waitForFramesWritten() must not be called "  | 
| 640 |                              "recursively. Check that no slot containing waitForFramesReceived() "  | 
| 641 |                              "is called in response to framesWritten(qint64) or "  | 
| 642 |                              "errorOccurred(CanBusError) signals." ); | 
| 643 |         setError(errorText: tr(s: "QCanBusDevice::waitForFramesWritten() must not be called recursively." ), | 
| 644 |                  errorId: CanBusError::OperationError); | 
| 645 |         return false; | 
| 646 |     } | 
| 647 |  | 
| 648 |     if (Q_UNLIKELY(d_func()->state != ConnectedState)) { | 
| 649 |         const QString error = tr(s: "Cannot wait for frames written as device is not connected." ); | 
| 650 |         qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); | 
| 651 |         setError(errorText: error, errorId: CanBusError::OperationError); | 
| 652 |         return false; | 
| 653 |     } | 
| 654 |  | 
| 655 |     if (!framesToWrite()) | 
| 656 |         return false; // nothing pending, nothing to wait upon | 
| 657 |  | 
| 658 |     QScopedValueRollback<bool> guard(d_func()->waitForWrittenEntered); | 
| 659 |     d_func()->waitForWrittenEntered = true; | 
| 660 |  | 
| 661 |     enum { Written = 0, Error, Timeout }; | 
| 662 |     QEventLoop loop; | 
| 663 |     connect(sender: this, signal: &QCanBusDevice::framesWritten, context: &loop, slot: [&]() { loop.exit(returnCode: Written); }); | 
| 664 |     connect(sender: this, signal: &QCanBusDevice::errorOccurred, context: &loop, slot: [&]() { loop.exit(returnCode: Error); }); | 
| 665 |     if (msecs >= 0) | 
| 666 |         QTimer::singleShot(interval: msecs, context: &loop, slot: [&]() { loop.exit(returnCode: Timeout); }); | 
| 667 |  | 
| 668 |     int result = Written; | 
| 669 |     while (framesToWrite() > 0) { | 
| 670 |         // wait till all written or time out | 
| 671 |         result = loop.exec(flags: QEventLoop::ExcludeUserInputEvents); | 
| 672 |         if (Q_UNLIKELY(result == Timeout)) { | 
| 673 |             const QString error = tr(s: "Timeout (%1 ms) during wait for frames written." ).arg(a: msecs); | 
| 674 |             setError(errorText: error, errorId: CanBusError::TimeoutError); | 
| 675 |             qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); | 
| 676 |         } | 
| 677 |  | 
| 678 |         if (result > Written) | 
| 679 |             return false; | 
| 680 |     } | 
| 681 |  | 
| 682 |     clearError(); | 
| 683 |     return true; | 
| 684 | } | 
| 685 |  | 
| 686 | /*! | 
| 687 |     Blocks until new frames are available for reading and the \l framesReceived() | 
| 688 |     signal has been emitted, or until \a msecs milliseconds have passed. If | 
| 689 |     \a msecs is \c -1, this function will not time out. | 
| 690 |  | 
| 691 |     Returns \c true if new frames are available for reading and the \l framesReceived() | 
| 692 |     signal is emitted; otherwise returns \c false (if the operation timed out | 
| 693 |     or if an error occurred). | 
| 694 |  | 
| 695 |     \note This function will start a local event loop. This may lead to scenarios whereby | 
| 696 |     other application slots may be called while the execution of this function scope is blocking. | 
| 697 |     To avoid problems, the signals for this class should not be connected to slots. | 
| 698 |     Similarly this function must never be called in response to the \l framesReceived() | 
| 699 |     or \l errorOccurred() signals. | 
| 700 |  | 
| 701 |     \sa waitForFramesWritten() | 
| 702 |  */ | 
| 703 | bool QCanBusDevice::waitForFramesReceived(int msecs) | 
| 704 | { | 
| 705 |     // do not enter this function recursively | 
| 706 |     if (Q_UNLIKELY(d_func()->waitForReceivedEntered)) { | 
| 707 |         qCWarning(QT_CANBUS, "QCanBusDevice::waitForFramesReceived() must not be called "  | 
| 708 |                              "recursively. Check that no slot containing waitForFramesReceived() "  | 
| 709 |                              "is called in response to framesReceived() or "  | 
| 710 |                              "errorOccurred(CanBusError) signals." ); | 
| 711 |         setError(errorText: tr(s: "QCanBusDevice::waitForFramesReceived() must not be called recursively." ), | 
| 712 |                  errorId: CanBusError::OperationError); | 
| 713 |         return false; | 
| 714 |     } | 
| 715 |  | 
| 716 |     if (Q_UNLIKELY(d_func()->state != ConnectedState)) { | 
| 717 |         const QString error = tr(s: "Cannot wait for frames received as device is not connected." ); | 
| 718 |         qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); | 
| 719 |         setError(errorText: error, errorId: CanBusError::OperationError); | 
| 720 |         return false; | 
| 721 |     } | 
| 722 |  | 
| 723 |     QScopedValueRollback<bool> guard(d_func()->waitForReceivedEntered); | 
| 724 |     d_func()->waitForReceivedEntered = true; | 
| 725 |  | 
| 726 |     enum { Received = 0, Error, Timeout }; | 
| 727 |     QEventLoop loop; | 
| 728 |     connect(sender: this, signal: &QCanBusDevice::framesReceived, context: &loop, slot: [&]() { loop.exit(returnCode: Received); }); | 
| 729 |     connect(sender: this, signal: &QCanBusDevice::errorOccurred, context: &loop, slot: [&]() { loop.exit(returnCode: Error); }); | 
| 730 |     if (msecs >= 0) | 
| 731 |         QTimer::singleShot(interval: msecs, context: &loop, slot: [&]() { loop.exit(returnCode: Timeout); }); | 
| 732 |  | 
| 733 |     int result = loop.exec(flags: QEventLoop::ExcludeUserInputEvents); | 
| 734 |  | 
| 735 |     if (Q_UNLIKELY(result == Timeout)) { | 
| 736 |         const QString error = tr(s: "Timeout (%1 ms) during wait for frames received." ).arg(a: msecs); | 
| 737 |         setError(errorText: error, errorId: CanBusError::TimeoutError); | 
| 738 |         qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); | 
| 739 |     } | 
| 740 |  | 
| 741 |     if (result == Received) | 
| 742 |         clearError(); | 
| 743 |     return result == Received; | 
| 744 | } | 
| 745 |  | 
| 746 | /*! | 
| 747 |     \fn bool QCanBusDevice::open() | 
| 748 |  | 
| 749 |     This function is called by connectDevice(). Subclasses must provide | 
| 750 |     an implementation which returns \c true if the CAN bus connection | 
| 751 |     could be established; otherwise \c false. The QCanBusDevice implementation | 
| 752 |     ensures upon entry of this function that the device's \l state() is set | 
| 753 |     to \l QCanBusDevice::ConnectingState already. | 
| 754 |  | 
| 755 |     The implementation must ensure that upon success the instance's \l state() | 
| 756 |     is set to \l QCanBusDevice::ConnectedState; otherwise | 
| 757 |     \l QCanBusDevice::UnconnectedState. \l setState() must be used to set the new | 
| 758 |     device state. | 
| 759 |  | 
| 760 |     The custom implementation is responsible for opening the socket, instanciation | 
| 761 |     of a potentially required \l QSocketNotifier and the application of custom and default | 
| 762 |     \l QCanBusDevice::configurationParameter(). | 
| 763 |  | 
| 764 |     \sa connectDevice() | 
| 765 | */ | 
| 766 |  | 
| 767 | /*! | 
| 768 |     \fn void QCanBusDevice::close() | 
| 769 |  | 
| 770 |     This function is responsible for closing the CAN bus connection. | 
| 771 |     The implementation must ensure that the instance's | 
| 772 |     \l state() is set to \l QCanBusDevice::UnconnectedState. | 
| 773 |  | 
| 774 |     This function's most important task is to close the socket to the CAN device | 
| 775 |     and to call \l QCanBusDevice::setState(). | 
| 776 |  | 
| 777 |     \sa disconnectDevice() | 
| 778 | */ | 
| 779 |  | 
| 780 | /*! | 
| 781 |     \fn void QCanBusDevice::framesReceived() | 
| 782 |  | 
| 783 |     This signal is emitted when one or more frames have been received. | 
| 784 |     The frames should be read using \l readFrame() and \l framesAvailable(). | 
| 785 | */ | 
| 786 |  | 
| 787 | /*! | 
| 788 |     Returns the next \l QCanBusFrame from the queue; otherwise returns | 
| 789 |     an empty QCanBusFrame. The returned frame is removed from the queue. | 
| 790 |  | 
| 791 |     The queue operates according to the FIFO principle. | 
| 792 |  | 
| 793 |     \sa clear(), framesAvailable(), readAllFrames() | 
| 794 | */ | 
| 795 | QCanBusFrame QCanBusDevice::readFrame() | 
| 796 | { | 
| 797 |     Q_D(QCanBusDevice); | 
| 798 |  | 
| 799 |     if (Q_UNLIKELY(d->state != ConnectedState)) { | 
| 800 |         const QString error = tr(s: "Cannot read frame as device is not connected." ); | 
| 801 |         qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); | 
| 802 |         setError(errorText: error, errorId: CanBusError::OperationError); | 
| 803 |         return QCanBusFrame(QCanBusFrame::InvalidFrame); | 
| 804 |     } | 
| 805 |  | 
| 806 |     clearError(); | 
| 807 |  | 
| 808 |     QMutexLocker locker(&d->incomingFramesGuard); | 
| 809 |  | 
| 810 |     if (Q_UNLIKELY(d->incomingFrames.isEmpty())) | 
| 811 |         return QCanBusFrame(QCanBusFrame::InvalidFrame); | 
| 812 |  | 
| 813 |     return d->incomingFrames.takeFirst(); | 
| 814 | } | 
| 815 |  | 
| 816 | /*! | 
| 817 |     \since 5.12 | 
| 818 |     Returns all \l{QCanBusFrame}s from the queue; otherwise returns | 
| 819 |     an empty QVector. The returned frames are removed from the queue. | 
| 820 |  | 
| 821 |     The queue operates according to the FIFO principle. | 
| 822 |  | 
| 823 |     \sa clear(), framesAvailable(), readFrame() | 
| 824 | */ | 
| 825 | QVector<QCanBusFrame> QCanBusDevice::readAllFrames() | 
| 826 | { | 
| 827 |     Q_D(QCanBusDevice); | 
| 828 |  | 
| 829 |     if (Q_UNLIKELY(d->state != ConnectedState)) { | 
| 830 |         const QString error = tr(s: "Cannot read frame as device is not connected." ); | 
| 831 |         qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); | 
| 832 |         setError(errorText: error, errorId: CanBusError::OperationError); | 
| 833 |         return QVector<QCanBusFrame>(); | 
| 834 |     } | 
| 835 |  | 
| 836 |     clearError(); | 
| 837 |  | 
| 838 |     QMutexLocker locker(&d->incomingFramesGuard); | 
| 839 |  | 
| 840 |     QVector<QCanBusFrame> result; | 
| 841 |     result.swap(other&: d->incomingFrames); | 
| 842 |     return result; | 
| 843 | } | 
| 844 |  | 
| 845 | /*! | 
| 846 |     \fn void QCanBusDevice::framesWritten(qint64 framesCount) | 
| 847 |  | 
| 848 |     This signal is emitted every time a payload of frames has been | 
| 849 |     written to the CAN bus. The \a framesCount argument is set to | 
| 850 |     the number of frames that were written in this payload. | 
| 851 | */ | 
| 852 |  | 
| 853 | /*! | 
| 854 |     \fn bool QCanBusDevice::writeFrame(const QCanBusFrame &frame) | 
| 855 |  | 
| 856 |     Writes \a frame to the CAN bus and returns \c true on success; | 
| 857 |     otherwise \c false. | 
| 858 |  | 
| 859 |     On some platforms, the frame may be put into a queue and the return | 
| 860 |     value may only indicate a successful insertion into the queue. | 
| 861 |     The actual frame will be send later on. Therefore the \l framesWritten() | 
| 862 |     signal is the final confirmation that the frame has been handed off to | 
| 863 |     the transport layer. If an error occurs the \l errorOccurred() is emitted. | 
| 864 |  | 
| 865 |     As per CAN bus specification, frames of type | 
| 866 |     \l {QCanBusFrame::RemoteRequestFrame} {remote transfer request (RTR)} | 
| 867 |     do not have a payload, but a length from 0 to 8 (including). This length | 
| 868 |     indicates the expected response payload length from the remote party. | 
| 869 |     Therefore when sending a RTR frame using this function it may still | 
| 870 |     be required to set an arbitrary payload on \a frame. The length of | 
| 871 |     the arbitrary payload is what is set as size expectation for the RTR frame. | 
| 872 |  | 
| 873 |     \sa QCanBusFrame::setPayload() | 
| 874 | */ | 
| 875 |  | 
| 876 | /*! | 
| 877 |     \fn QString QCanBusDevice::interpretErrorFrame(const QCanBusFrame &frame) | 
| 878 |  | 
| 879 |     Interprets \a frame as error frame and returns a human readable | 
| 880 |     description of the error. | 
| 881 |  | 
| 882 |     If \a frame is not an error frame, the returned string is empty. | 
| 883 | */ | 
| 884 |  | 
| 885 | /*! | 
| 886 |     Connects the device to the CAN bus. Returns \c true on success; | 
| 887 |     otherwise \c false. | 
| 888 |  | 
| 889 |     This function calls \l open() as part of its implementation. | 
| 890 |  | 
| 891 |     \sa disconnectDevice() | 
| 892 | */ | 
| 893 | bool QCanBusDevice::connectDevice() | 
| 894 | { | 
| 895 |     Q_D(QCanBusDevice); | 
| 896 |  | 
| 897 |     if (Q_UNLIKELY(d->state != QCanBusDevice::UnconnectedState)) { | 
| 898 |         const char error[] = QT_TRANSLATE_NOOP("QCanBusDevice" , | 
| 899 |                                 "Can not connect an already connected device." ); | 
| 900 |         qCWarning(QT_CANBUS, error); | 
| 901 |         setError(errorText: tr(s: error), errorId: QCanBusDevice::ConnectionError); | 
| 902 |         return false; | 
| 903 |     } | 
| 904 |  | 
| 905 |     setState(ConnectingState); | 
| 906 |  | 
| 907 |     if (!open()) { | 
| 908 |         setState(UnconnectedState); | 
| 909 |         return false; | 
| 910 |     } | 
| 911 |  | 
| 912 |     clearError(); | 
| 913 |  | 
| 914 |     //Connected is set by backend -> might be delayed by event loop | 
| 915 |     return true; | 
| 916 | } | 
| 917 |  | 
| 918 |  | 
| 919 | /*! | 
| 920 |     Disconnects the device from the CAN bus. | 
| 921 |  | 
| 922 |     This function calls \l close() as part of its implementation. | 
| 923 |  | 
| 924 |     \note This function should only be called, if connectDevice() | 
| 925 |     returned \c true. | 
| 926 |  | 
| 927 |     \sa connectDevice() | 
| 928 | */ | 
| 929 | void QCanBusDevice::disconnectDevice() | 
| 930 | { | 
| 931 |     Q_D(QCanBusDevice); | 
| 932 |  | 
| 933 |     if (Q_UNLIKELY(d->state == QCanBusDevice::UnconnectedState | 
| 934 |             || d->state == QCanBusDevice::ClosingState)) { | 
| 935 |         qCWarning(QT_CANBUS, "Can not disconnect an unconnected device." ); | 
| 936 |         return; | 
| 937 |     } | 
| 938 |  | 
| 939 |     setState(QCanBusDevice::ClosingState); | 
| 940 |  | 
| 941 |     //Unconnected is set by backend -> might be delayed by event loop | 
| 942 |     close(); | 
| 943 | } | 
| 944 |  | 
| 945 | /*! | 
| 946 |     \fn void QCanBusDevice::stateChanged(QCanBusDevice::CanBusDeviceState state) | 
| 947 |  | 
| 948 |     This signal is emitted every time the state of the device changes. | 
| 949 |     The new state is represented by \a state. | 
| 950 |  | 
| 951 |     \sa setState(), state() | 
| 952 | */ | 
| 953 |  | 
| 954 | /*! | 
| 955 |     Returns the current state of the device. | 
| 956 |  | 
| 957 |     \sa setState(), stateChanged() | 
| 958 | */ | 
| 959 | QCanBusDevice::CanBusDeviceState QCanBusDevice::state() const | 
| 960 | { | 
| 961 |     return d_func()->state; | 
| 962 | } | 
| 963 |  | 
| 964 | /*! | 
| 965 |     Sets the state of the device to \a newState. CAN bus implementations | 
| 966 |     must use this function to update the device state. | 
| 967 | */ | 
| 968 | void QCanBusDevice::setState(QCanBusDevice::CanBusDeviceState newState) | 
| 969 | { | 
| 970 |     Q_D(QCanBusDevice); | 
| 971 |  | 
| 972 |     if (newState == d->state) | 
| 973 |         return; | 
| 974 |  | 
| 975 |     d->state = newState; | 
| 976 |     emit stateChanged(state: newState); | 
| 977 | } | 
| 978 |  | 
| 979 | /*! | 
| 980 |  * Returns a QCanBusDeviceInfo created from the given parameters \a name, | 
| 981 |  * \a isVirtual, and \a isFlexibleDataRateCapable. | 
| 982 |  * \internal | 
| 983 |  */ | 
| 984 | QCanBusDeviceInfo QCanBusDevice::createDeviceInfo(const QString &name, bool isVirtual, | 
| 985 |                                                   bool isFlexibleDataRateCapable) | 
| 986 | { | 
| 987 |     return createDeviceInfo(name, serialNumber: QString(), description: QString(), channel: 0, isVirtual, isFlexibleDataRateCapable); | 
| 988 | } | 
| 989 |  | 
| 990 | /*! | 
| 991 |     \since 5.11 | 
| 992 |     Returns a QCanBusDeviceInfo created from the given parameters \a name, | 
| 993 |     \a serialNumber, \a description, \a channel, \a isVirtual, and \a | 
| 994 |     isFlexibleDataRateCapable. | 
| 995 |     \internal | 
| 996 |  */ | 
| 997 | QCanBusDeviceInfo QCanBusDevice::createDeviceInfo(const QString &name, const QString &serialNumber, | 
| 998 |                                                   const QString &description, int channel, | 
| 999 |                                                   bool isVirtual, bool isFlexibleDataRateCapable) | 
| 1000 | { | 
| 1001 |     QScopedPointer<QCanBusDeviceInfoPrivate> info(new QCanBusDeviceInfoPrivate); | 
| 1002 |     info->name = name; | 
| 1003 |     info->serialNumber = serialNumber; | 
| 1004 |     info->description = description; | 
| 1005 |     info->channel = channel; | 
| 1006 |     info->hasFlexibleDataRate = isFlexibleDataRateCapable; | 
| 1007 |     info->isVirtual = isVirtual; | 
| 1008 |     return QCanBusDeviceInfo(*info.take()); | 
| 1009 | } | 
| 1010 |  | 
| 1011 | QT_END_NAMESPACE | 
| 1012 |  |