| 1 | // Copyright (C) 2017 Ford Motor Company. | 
| 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 "j2534passthru.h" | 
| 5 |  | 
| 6 | #include <QtEndian> | 
| 7 |  | 
| 8 | #include <cstring> | 
| 9 |  | 
| 10 | namespace { | 
| 11 |  | 
| 12 | enum Ioctl { | 
| 13 |     GetConfig = 1, | 
| 14 |     SetConfig = 2 | 
| 15 | }; | 
| 16 |  | 
| 17 | // Template to model the structs SCONFIG_LIST, SBYTE_ARRAY etc as defined | 
| 18 | // in the J2534 spec. | 
| 19 | template <typename T> | 
| 20 | struct SArray | 
| 21 | { | 
| 22 |     SArray(ulong n, T *p) : num(n), ptr(p) {} | 
| 23 |     // On Windows x64, ulong is 32 bit wide and thus the value would normally | 
| 24 |     // be padded so that the pointer begins on a 64-bit boundary. It is not | 
| 25 |     // clear from the J2534 spec whether structs should be packed or not on | 
| 26 |     // x64. Most vendors still only provide a 32-bit DLL, but there is at | 
| 27 |     // least one x64 implementation (from Hatteland Display) out there which | 
| 28 |     // does not pack this struct. | 
| 29 |     ulong num; | 
| 30 |     T *ptr; | 
| 31 | }; | 
| 32 |  | 
| 33 | // Fixed-length string buffers must be at least 80 bytes according to the spec. | 
| 34 | // The example code in the spec document uses 256 bytes though -- let's play it | 
| 35 | // safe and do so, too. | 
| 36 | const int StringBufferSize = 256; | 
| 37 |  | 
| 38 | } // anonymous namespace | 
| 39 |  | 
| 40 | Q_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_PASSTHRU, "qt.canbus.plugins.passthru" , QtWarningMsg) | 
| 41 |  | 
| 42 | namespace J2534 { | 
| 43 |  | 
| 44 | Message::Message() | 
| 45 | { | 
| 46 |     std::memset(s: m_data, c: 0, n: sizeof(m_data)); | 
| 47 | } | 
| 48 |  | 
| 49 | Message::Message(Protocol proto) | 
| 50 |     : m_protocolId (ulong(proto)) | 
| 51 | { | 
| 52 |     std::memset(s: m_data, c: 0, n: sizeof(m_data)); | 
| 53 | } | 
| 54 |  | 
| 55 | PassThru::PassThru(const QString &libraryPath, QObject *parent) | 
| 56 |     : QObject(parent) | 
| 57 |     , m_libJ2534 (libraryPath, this) | 
| 58 | { | 
| 59 |     if (!m_libJ2534.load() | 
| 60 |             || !resolveApiFunction(funcPtr: &m_ptOpen, name: "PassThruOpen" ) | 
| 61 |             || !resolveApiFunction(funcPtr: &m_ptClose, name: "PassThruClose" ) | 
| 62 |             || !resolveApiFunction(funcPtr: &m_ptConnect, name: "PassThruConnect" ) | 
| 63 |             || !resolveApiFunction(funcPtr: &m_ptDisconnect, name: "PassThruDisconnect" ) | 
| 64 |             || !resolveApiFunction(funcPtr: &m_ptReadMsgs, name: "PassThruReadMsgs" ) | 
| 65 |             || !resolveApiFunction(funcPtr: &m_ptWriteMsgs, name: "PassThruWriteMsgs" ) | 
| 66 |             || !resolveApiFunction(funcPtr: &m_ptStartMsgFilter, name: "PassThruStartMsgFilter" ) | 
| 67 |             || !resolveApiFunction(funcPtr: &m_ptGetLastError, name: "PassThruGetLastError" ) | 
| 68 |             || !resolveApiFunction(funcPtr: &m_ptIoctl, name: "PassThruIoctl" )) { | 
| 69 |  | 
| 70 |         m_lastError = LoadFailed; | 
| 71 |         m_lastErrorString = m_libJ2534.errorString(); | 
| 72 |  | 
| 73 |         qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "%ls" , qUtf16Printable(m_lastErrorString)); | 
| 74 |     } | 
| 75 | } | 
| 76 |  | 
| 77 | PassThru::~PassThru() | 
| 78 | { | 
| 79 |     m_libJ2534.unload(); | 
| 80 | } | 
| 81 |  | 
| 82 | PassThru::Status PassThru::open(const QByteArray &name, Handle *deviceId) | 
| 83 | { | 
| 84 |     Q_ASSERT(m_ptOpen); | 
| 85 |  | 
| 86 |     const char *const devName = (name.isEmpty()) ? nullptr : name.data(); | 
| 87 |     const long status = (*m_ptOpen)(devName, deviceId); | 
| 88 |     return handleResult(statusCode: status); | 
| 89 | } | 
| 90 |  | 
| 91 | PassThru::Status PassThru::close(Handle deviceId) | 
| 92 | { | 
| 93 |     Q_ASSERT(m_ptClose); | 
| 94 |  | 
| 95 |     const long status = (*m_ptClose)(deviceId); | 
| 96 |     return handleResult(statusCode: status); | 
| 97 | } | 
| 98 |  | 
| 99 | PassThru::Status PassThru::connect(Handle deviceId, Protocol protocolId, | 
| 100 |                                    ConnectFlags flags, uint baudRate, Handle *channelId) | 
| 101 | { | 
| 102 |     Q_ASSERT(m_ptConnect); | 
| 103 |  | 
| 104 |     const long status = (*m_ptConnect)(deviceId, ulong(protocolId), | 
| 105 |                                        flags, baudRate, channelId); | 
| 106 |     return handleResult(statusCode: status); | 
| 107 | } | 
| 108 |  | 
| 109 | PassThru::Status PassThru::disconnect(Handle channelId) | 
| 110 | { | 
| 111 |     Q_ASSERT(m_ptDisconnect); | 
| 112 |  | 
| 113 |     const long status = (*m_ptDisconnect)(channelId); | 
| 114 |     return handleResult(statusCode: status); | 
| 115 | } | 
| 116 |  | 
| 117 | PassThru::Status PassThru::readMsgs(Handle channelId, Message *msgs, | 
| 118 |                                     ulong *numMsgs, uint timeout) | 
| 119 | { | 
| 120 |     Q_ASSERT(m_ptReadMsgs); | 
| 121 |  | 
| 122 |     const long status = (*m_ptReadMsgs)(channelId, msgs, numMsgs, timeout); | 
| 123 |     return handleResult(statusCode: status); | 
| 124 | } | 
| 125 |  | 
| 126 | PassThru::Status PassThru::writeMsgs(Handle channelId, const Message *msgs, | 
| 127 |                                      ulong *numMsgs, uint timeout) | 
| 128 | { | 
| 129 |     Q_ASSERT(m_ptWriteMsgs); | 
| 130 |  | 
| 131 |     const long status = (*m_ptWriteMsgs)(channelId, msgs, numMsgs, timeout); | 
| 132 |     return handleResult(statusCode: status); | 
| 133 | } | 
| 134 |  | 
| 135 | PassThru::Status PassThru::startMsgFilter(Handle channelId, FilterType filterType, | 
| 136 |                                           const Message &maskMsg, const Message &patternMsg) | 
| 137 | { | 
| 138 |     Q_ASSERT(m_ptStartMsgFilter); | 
| 139 |  | 
| 140 |     // The CAN pass-thru plugin implementation does not need the filter ID. | 
| 141 |     Handle filterId = 0; | 
| 142 |  | 
| 143 |     const long status = (*m_ptStartMsgFilter)(channelId, filterType, &maskMsg, | 
| 144 |                                               &patternMsg, nullptr, &filterId); | 
| 145 |     return handleResult(statusCode: status); | 
| 146 | } | 
| 147 |  | 
| 148 | PassThru::Status PassThru::setConfig(Handle channelId, const Config *params, ulong numParams) | 
| 149 | { | 
| 150 |     Q_ASSERT(m_ptIoctl); | 
| 151 |  | 
| 152 |     const SArray<const Config> configList {numParams, params}; | 
| 153 |     const long status = (*m_ptIoctl)(channelId, SetConfig, &configList, nullptr); | 
| 154 |     return handleResult(statusCode: status); | 
| 155 | } | 
| 156 |  | 
| 157 | PassThru::Status PassThru::clear(Handle channelId, ClearTarget target) | 
| 158 | { | 
| 159 |     Q_ASSERT(m_ptIoctl); | 
| 160 |  | 
| 161 |     const long status = (*m_ptIoctl)(channelId, target, nullptr, nullptr); | 
| 162 |     return handleResult(statusCode: status); | 
| 163 | } | 
| 164 |  | 
| 165 | QString PassThru::lastErrorString() const | 
| 166 | { | 
| 167 |     return m_lastErrorString; | 
| 168 | } | 
| 169 |  | 
| 170 | PassThru::Status PassThru::handleResult(long statusCode) | 
| 171 | { | 
| 172 |     if (Q_UNLIKELY(statusCode != NoError)) { | 
| 173 |         m_lastError = Status(statusCode); | 
| 174 |  | 
| 175 |         QByteArray description (StringBufferSize, 0); | 
| 176 |         Q_ASSERT(m_ptGetLastError); | 
| 177 |         const long descStatus = (*m_ptGetLastError)(description.data()); | 
| 178 |  | 
| 179 |         if (Q_LIKELY(descStatus == NoError)) { | 
| 180 |             m_lastErrorString = QString::fromLatin1(ba: description); | 
| 181 |         } else { | 
| 182 |             m_lastErrorString = tr(s: "Command failed with status code %1" ).arg(a: statusCode); | 
| 183 |             qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "GetLastError failed with code %ld" , descStatus); | 
| 184 |         } | 
| 185 |     } | 
| 186 |     return Status(statusCode); | 
| 187 | } | 
| 188 |  | 
| 189 | } // namespace J2534 | 
| 190 |  |