| 1 | // Copyright (C) 2017 The Qt Company Ltd. | 
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only | 
| 3 |  | 
| 4 | #ifndef QCANBUSFRAME_H | 
| 5 | #define QCANBUSFRAME_H | 
| 6 |  | 
| 7 | #include <QtCore/qmetatype.h> | 
| 8 | #include <QtCore/qobject.h> | 
| 9 | #include <QtSerialBus/qtserialbusglobal.h> | 
| 10 |  | 
| 11 | QT_BEGIN_NAMESPACE | 
| 12 |  | 
| 13 | class QDataStream; | 
| 14 |  | 
| 15 | class Q_SERIALBUS_EXPORT QCanBusFrame | 
| 16 | { | 
| 17 | public: | 
| 18 |     using FrameId = quint32; | 
| 19 |  | 
| 20 |     class TimeStamp { | 
| 21 |     public: | 
| 22 |         constexpr TimeStamp(qint64 s = 0, qint64 usec = 0) noexcept | 
| 23 |             : secs(s), usecs(usec) {} | 
| 24 |  | 
| 25 |         constexpr static TimeStamp fromMicroSeconds(qint64 usec) noexcept | 
| 26 |         { return TimeStamp(usec / 1000000, usec % 1000000); } | 
| 27 |  | 
| 28 |         constexpr qint64 seconds() const noexcept { return secs; } | 
| 29 |         constexpr qint64 microSeconds() const noexcept { return usecs; } | 
| 30 |  | 
| 31 |     private: | 
| 32 |         qint64 secs; | 
| 33 |         qint64 usecs; | 
| 34 |     }; | 
| 35 |  | 
| 36 |     enum FrameType { | 
| 37 |         UnknownFrame        = 0x0, | 
| 38 |         DataFrame           = 0x1, | 
| 39 |         ErrorFrame          = 0x2, | 
| 40 |         RemoteRequestFrame  = 0x3, | 
| 41 |         InvalidFrame        = 0x4 | 
| 42 |     }; | 
| 43 |  | 
| 44 |     explicit QCanBusFrame(FrameType type = DataFrame) noexcept : | 
| 45 |         isExtendedFrame(0x0), | 
| 46 |         version(Qt_5_10), | 
| 47 |         isFlexibleDataRate(0x0), | 
| 48 |         isBitrateSwitch(0x0), | 
| 49 |         isErrorStateIndicator(0x0), | 
| 50 |         isLocalEcho(0x0), | 
| 51 |         reserved0(0x0) | 
| 52 |     { | 
| 53 |         Q_UNUSED(reserved0); | 
| 54 |         ::memset(s: reserved, c: 0, n: sizeof(reserved)); | 
| 55 |         setFrameId(0x0); | 
| 56 |         setFrameType(type); | 
| 57 |     } | 
| 58 |  | 
| 59 |     enum FrameError { | 
| 60 |         NoError                     = 0, | 
| 61 |         TransmissionTimeoutError    = (1 << 0), | 
| 62 |         LostArbitrationError        = (1 << 1), | 
| 63 |         ControllerError             = (1 << 2), | 
| 64 |         ProtocolViolationError      = (1 << 3), | 
| 65 |         TransceiverError            = (1 << 4), | 
| 66 |         MissingAcknowledgmentError  = (1 << 5), | 
| 67 |         BusOffError                 = (1 << 6), | 
| 68 |         BusError                    = (1 << 7), | 
| 69 |         ControllerRestartError      = (1 << 8), | 
| 70 |         UnknownError                = (1 << 9), | 
| 71 |         AnyError                    = 0x1FFFFFFFU | 
| 72 |         //only 29 bits usable | 
| 73 |     }; | 
| 74 |     Q_DECLARE_FLAGS(FrameErrors, FrameError) | 
| 75 |     Q_FLAGS(FrameErrors) | 
| 76 |  | 
| 77 |     explicit QCanBusFrame(QCanBusFrame::FrameId identifier, const QByteArray &data) : | 
| 78 |         format(DataFrame), | 
| 79 |         isExtendedFrame(0x0), | 
| 80 |         version(Qt_5_10), | 
| 81 |         isFlexibleDataRate(data.size() > 8 ? 0x1 : 0x0), | 
| 82 |         isBitrateSwitch(0x0), | 
| 83 |         isErrorStateIndicator(0x0), | 
| 84 |         isLocalEcho(0x0), | 
| 85 |         reserved0(0x0), | 
| 86 |         load(data) | 
| 87 |     { | 
| 88 |         ::memset(s: reserved, c: 0, n: sizeof(reserved)); | 
| 89 |         setFrameId(identifier); | 
| 90 |     } | 
| 91 |  | 
| 92 |     bool isValid() const noexcept | 
| 93 |     { | 
| 94 |         if (format == InvalidFrame) | 
| 95 |             return false; | 
| 96 |  | 
| 97 |         // long id used, but extended flag not set | 
| 98 |         if (!isExtendedFrame && (canId & 0x1FFFF800U)) | 
| 99 |             return false; | 
| 100 |  | 
| 101 |         if (!isValidFrameId) | 
| 102 |             return false; | 
| 103 |  | 
| 104 |         // maximum permitted payload size in CAN or CAN FD | 
| 105 |         const qsizetype length = load.size(); | 
| 106 |         if (isFlexibleDataRate) { | 
| 107 |             if (format == RemoteRequestFrame) | 
| 108 |                 return false; | 
| 109 |  | 
| 110 |             return length <= 8 || length == 12 || length == 16 || length == 20 | 
| 111 |                     || length == 24 || length == 32 || length == 48 || length == 64; | 
| 112 |         } | 
| 113 |  | 
| 114 |         return length <= 8; | 
| 115 |     } | 
| 116 |  | 
| 117 |     constexpr FrameType frameType() const noexcept | 
| 118 |     { | 
| 119 |         switch (format) { | 
| 120 |         case 0x1: return DataFrame; | 
| 121 |         case 0x2: return ErrorFrame; | 
| 122 |         case 0x3: return RemoteRequestFrame; | 
| 123 |         case 0x4: return InvalidFrame; | 
| 124 |         // no default to trigger warning | 
| 125 |         } | 
| 126 |  | 
| 127 |         return UnknownFrame; | 
| 128 |     } | 
| 129 |  | 
| 130 |     constexpr void setFrameType(FrameType newFormat) noexcept | 
| 131 |     { | 
| 132 |         switch (newFormat) { | 
| 133 |         case DataFrame: | 
| 134 |             format = 0x1; return; | 
| 135 |         case ErrorFrame: | 
| 136 |             format = 0x2; return; | 
| 137 |         case RemoteRequestFrame: | 
| 138 |             format = 0x3; return; | 
| 139 |         case UnknownFrame: | 
| 140 |             format = 0x0; return; | 
| 141 |         case InvalidFrame: | 
| 142 |             format = 0x4; return; | 
| 143 |         } | 
| 144 |     } | 
| 145 |  | 
| 146 |     constexpr bool hasExtendedFrameFormat() const noexcept { return (isExtendedFrame & 0x1); } | 
| 147 |     constexpr void setExtendedFrameFormat(bool isExtended) noexcept | 
| 148 |     { | 
| 149 |         isExtendedFrame = (isExtended & 0x1); | 
| 150 |     } | 
| 151 |  | 
| 152 |     constexpr QCanBusFrame::FrameId frameId() const noexcept | 
| 153 |     { | 
| 154 |         if (Q_UNLIKELY(format == ErrorFrame)) | 
| 155 |             return 0; | 
| 156 |         return (canId & 0x1FFFFFFFU); | 
| 157 |     } | 
| 158 |     constexpr void setFrameId(QCanBusFrame::FrameId newFrameId) | 
| 159 |     { | 
| 160 |         if (Q_LIKELY(newFrameId < 0x20000000U)) { | 
| 161 |             isValidFrameId = true; | 
| 162 |             canId = newFrameId; | 
| 163 |             setExtendedFrameFormat(isExtendedFrame || (newFrameId & 0x1FFFF800U)); | 
| 164 |         } else { | 
| 165 |             isValidFrameId = false; | 
| 166 |             canId = 0; | 
| 167 |         } | 
| 168 |     } | 
| 169 |  | 
| 170 |     void setPayload(const QByteArray &data) | 
| 171 |     { | 
| 172 |         load = data; | 
| 173 |         if (data.size() > 8) | 
| 174 |             isFlexibleDataRate = 0x1; | 
| 175 |     } | 
| 176 |     constexpr void setTimeStamp(TimeStamp ts) noexcept { stamp = ts; } | 
| 177 |  | 
| 178 |     QByteArray payload() const { return load; } | 
| 179 |     constexpr TimeStamp timeStamp() const noexcept { return stamp; } | 
| 180 |  | 
| 181 |     constexpr FrameErrors error() const noexcept | 
| 182 |     { | 
| 183 |         if (format != ErrorFrame) | 
| 184 |             return NoError; | 
| 185 |  | 
| 186 |         return FrameErrors(canId & 0x1FFFFFFFU); | 
| 187 |     } | 
| 188 |     constexpr void setError(FrameErrors e) | 
| 189 |     { | 
| 190 |         if (format != ErrorFrame) | 
| 191 |             return; | 
| 192 |         canId = (e & AnyError).toInt(); | 
| 193 |     } | 
| 194 |  | 
| 195 |     QString toString() const; | 
| 196 |  | 
| 197 |     constexpr bool hasFlexibleDataRateFormat() const noexcept { return (isFlexibleDataRate & 0x1); } | 
| 198 |     constexpr void setFlexibleDataRateFormat(bool isFlexibleData) noexcept | 
| 199 |     { | 
| 200 |         isFlexibleDataRate = (isFlexibleData & 0x1); | 
| 201 |         if (!isFlexibleData) { | 
| 202 |             isBitrateSwitch = 0x0; | 
| 203 |             isErrorStateIndicator = 0x0; | 
| 204 |         } | 
| 205 |     } | 
| 206 |  | 
| 207 |     constexpr bool hasBitrateSwitch() const noexcept { return (isBitrateSwitch & 0x1); } | 
| 208 |     constexpr void setBitrateSwitch(bool bitrateSwitch) noexcept | 
| 209 |     { | 
| 210 |         isBitrateSwitch = (bitrateSwitch & 0x1); | 
| 211 |         if (bitrateSwitch) | 
| 212 |             isFlexibleDataRate = 0x1; | 
| 213 |     } | 
| 214 |  | 
| 215 |     constexpr bool hasErrorStateIndicator() const noexcept { return (isErrorStateIndicator & 0x1); } | 
| 216 |     constexpr void setErrorStateIndicator(bool errorStateIndicator) noexcept | 
| 217 |     { | 
| 218 |         isErrorStateIndicator = (errorStateIndicator & 0x1); | 
| 219 |         if (errorStateIndicator) | 
| 220 |             isFlexibleDataRate = 0x1; | 
| 221 |     } | 
| 222 |     constexpr bool hasLocalEcho() const noexcept { return (isLocalEcho & 0x1); } | 
| 223 |     constexpr void setLocalEcho(bool localEcho) noexcept | 
| 224 |     { | 
| 225 |         isLocalEcho = (localEcho & 0x1); | 
| 226 |     } | 
| 227 |  | 
| 228 | #ifndef QT_NO_DATASTREAM | 
| 229 |     friend Q_SERIALBUS_EXPORT QDataStream &operator<<(QDataStream &, const QCanBusFrame &); | 
| 230 |     friend Q_SERIALBUS_EXPORT QDataStream &operator>>(QDataStream &, QCanBusFrame &); | 
| 231 | #endif | 
| 232 |  | 
| 233 | private: | 
| 234 |     enum Version { | 
| 235 |         Qt_5_8 = 0x0, | 
| 236 |         Qt_5_9 = 0x1, | 
| 237 |         Qt_5_10 = 0x2 | 
| 238 |     }; | 
| 239 |  | 
| 240 |     quint32 canId:29; // acts as container for error codes too | 
| 241 |     quint8 format:3; // max of 8 frame types | 
| 242 |  | 
| 243 |     quint8 isExtendedFrame:1; | 
| 244 |     quint8 version:5; | 
| 245 |     quint8 isValidFrameId:1; | 
| 246 |     quint8 isFlexibleDataRate:1; | 
| 247 |  | 
| 248 |     quint8 isBitrateSwitch:1; | 
| 249 |     quint8 isErrorStateIndicator:1; | 
| 250 |     quint8 isLocalEcho:1; | 
| 251 |     quint8 reserved0:5; | 
| 252 |  | 
| 253 |     // reserved for future use | 
| 254 |     quint8 reserved[2]; | 
| 255 |  | 
| 256 |     QByteArray load; | 
| 257 |     TimeStamp stamp; | 
| 258 | }; | 
| 259 |  | 
| 260 | Q_DECLARE_TYPEINFO(QCanBusFrame, Q_RELOCATABLE_TYPE); | 
| 261 | Q_DECLARE_TYPEINFO(QCanBusFrame::FrameError, Q_PRIMITIVE_TYPE); | 
| 262 | Q_DECLARE_TYPEINFO(QCanBusFrame::FrameType, Q_PRIMITIVE_TYPE); | 
| 263 | Q_DECLARE_TYPEINFO(QCanBusFrame::TimeStamp, Q_PRIMITIVE_TYPE); | 
| 264 |  | 
| 265 | Q_DECLARE_OPERATORS_FOR_FLAGS(QCanBusFrame::FrameErrors) | 
| 266 |  | 
| 267 | #ifndef QT_NO_DATASTREAM | 
| 268 | Q_SERIALBUS_EXPORT QDataStream &operator<<(QDataStream &, const QCanBusFrame &); | 
| 269 | Q_SERIALBUS_EXPORT QDataStream &operator>>(QDataStream &, QCanBusFrame &); | 
| 270 | #endif | 
| 271 |  | 
| 272 | QT_END_NAMESPACE | 
| 273 |  | 
| 274 | Q_DECLARE_METATYPE(QCanBusFrame::FrameType) | 
| 275 | Q_DECLARE_METATYPE(QCanBusFrame::FrameErrors) | 
| 276 |  | 
| 277 | #endif // QCANBUSFRAME_H | 
| 278 |  |