/* Copyright (c) 2022 Ethan Zonca This file is part of cangaroo. cangaroo is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. cangaroo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with cangaroo. If not, see . */ #include "SLCANInterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include SLCANInterface::SLCANInterface(SLCANDriver *driver, int index, QString name, bool fd_support) : CanInterface((CanDriver *)driver), _idx(index), _isOpen(false), _serport(NULL), _msg_queue(), _name(name), _rx_linbuf_ctr(0), _rxbuf_head(0), _rxbuf_tail(0), _ts_mode(ts_mode_SIOCSHWTSTAMP) { // Set defaults _settings.setBitrate(500000); _settings.setSamplePoint(875); _config.supports_canfd = fd_support; } SLCANInterface::~SLCANInterface() { } QString SLCANInterface::getDetailsStr() const { if(_config.supports_canfd) { return "CANable with CANFD support"; } else { return "CANable with standard CAN support"; } } QString SLCANInterface::getName() const { return _name; } void SLCANInterface::setName(QString name) { _name = name; } QList SLCANInterface::getAvailableBitrates() { QList retval; QList bitrates({10000, 20000, 50000, 83333, 100000, 125000, 250000, 500000, 800000, 1000000}); QList bitrates_fd({0, 2000000, 5000000}); QList samplePoints({875}); unsigned i=0; foreach (unsigned br, bitrates) { foreach(unsigned br_fd, bitrates_fd) { foreach (unsigned sp, samplePoints) { retval << CanTiming(i++, br, br_fd, sp); } } } return retval; } void SLCANInterface::applyConfig(const MeasurementInterface &mi) { // Save settings for port configuration _settings = mi; } bool SLCANInterface::updateStatus() { return false; } bool SLCANInterface::readConfig() { return false; } bool SLCANInterface::readConfigFromLink(rtnl_link *link) { return false; } bool SLCANInterface::supportsTimingConfiguration() { return _config.supports_timing; } bool SLCANInterface::supportsCanFD() { return _config.supports_canfd; } bool SLCANInterface::supportsTripleSampling() { return false; } unsigned SLCANInterface::getBitrate() { return _settings.bitrate(); } uint32_t SLCANInterface::getCapabilities() { uint32_t retval = CanInterface::capability_config_os | CanInterface::capability_listen_only | CanInterface::capability_auto_restart; if (supportsCanFD()) { retval |= CanInterface::capability_canfd; } if (supportsTripleSampling()) { retval |= CanInterface::capability_triple_sampling; } return retval; } bool SLCANInterface::updateStatistics() { return updateStatus(); } uint32_t SLCANInterface::getState() { if(_isOpen) return state_ok; else return state_bus_off; } int SLCANInterface::getNumRxFrames() { return _status.rx_count; } int SLCANInterface::getNumRxErrors() { return _status.rx_errors; } int SLCANInterface::getNumTxFrames() { return _status.tx_count; } int SLCANInterface::getNumTxErrors() { return _status.tx_errors; } int SLCANInterface::getNumRxOverruns() { return _status.rx_overruns; } int SLCANInterface::getNumTxDropped() { return _status.tx_dropped; } int SLCANInterface::getIfIndex() { return _idx; } void SLCANInterface::open() { if(_serport != NULL) { delete _serport; } _serport = new QSerialPort(); _serport_mutex.lock(); _serport->setPortName(_name); _serport->setBaudRate(1000000); _serport->setDataBits(QSerialPort::Data8); _serport->setParity(QSerialPort::NoParity); _serport->setStopBits(QSerialPort::OneStop); _serport->setFlowControl(QSerialPort::NoFlowControl); _serport->setReadBufferSize(2048); if (_serport->open(QIODevice::ReadWrite)) { //perror("Serport connected!"); } else { perror("Serport connect failed!"); _serport_mutex.unlock(); _isOpen = false; return; } _serport->flush(); _serport->clear(); // Set the classic CAN bitrate switch(_settings.bitrate()) { case 1000000: _serport->write("S8\r", 3); _serport->flush(); break; case 750000: _serport->write("S7\r", 3); _serport->flush(); break; case 500000: _serport->write("S6\r", 3); _serport->flush(); break; case 250000: _serport->write("S5\r", 3); _serport->flush(); break; case 125000: _serport->write("S4\r", 3); _serport->flush(); break; case 100000: _serport->write("S3\r", 3); _serport->flush(); break; case 83333: _serport->write("S9\r", 3); _serport->flush(); break; case 50000: _serport->write("S2\r", 3); _serport->flush(); break; case 20000: _serport->write("S1\r", 3); _serport->flush(); break; case 10000: _serport->write("S0\r", 3); _serport->flush(); break; default: // Default to 10k _serport->write("S0\r", 3); _serport->flush(); break; } _serport->waitForBytesWritten(300); // Set configured BRS rate if(_config.supports_canfd) { switch(_settings.fdBitrate()) { case 2000000: _serport->write("Y2\r", 3); _serport->flush(); break; case 5000000: _serport->write("Y5\r", 3); _serport->flush(); break; } } _serport->waitForBytesWritten(300); // Open the port _serport->write("O\r\n", 3); _serport->flush(); _isOpen = true; // Release port mutex _serport_mutex.unlock(); } void SLCANInterface::close() { _serport_mutex.lock(); if (_serport->isOpen()) { // Close CAN port _serport->write("C\r", 2); _serport->flush(); _serport->waitForBytesWritten(300); _serport->clear(); _serport->close(); } _isOpen = false; _serport_mutex.unlock(); } bool SLCANInterface::isOpen() { return _isOpen; } void SLCANInterface::sendMessage(const CanMessage &msg) { // SLCAN_MTU plus null terminator char buf[SLCAN_MTU+1] = {0}; uint8_t msg_idx = 0; // Message is FD // Add character for frame type if(msg.isFD()) { if(msg.isBRS()) { buf[msg_idx] = 'b'; } else { buf[msg_idx] = 'd'; } } // Message is not FD // Add character for frame type else { if (msg.isRTR()) { buf[msg_idx] = 'r'; } else { buf[msg_idx] = 't'; } } // Assume standard identifier uint8_t id_len = SLCAN_STD_ID_LEN; uint32_t tmp = msg.getId(); // Check if extended if (msg.isExtended()) { // Convert first char to upper case for extended frame buf[msg_idx] -= 32; id_len = SLCAN_EXT_ID_LEN; } msg_idx++; // Add identifier to buffer for(uint8_t j = id_len; j > 0; j--) { // Add nibble to buffer buf[j] = (tmp & 0xF); tmp = tmp >> 4; msg_idx++; } // Sanity check length int8_t bytes = msg.getLength(); if(bytes < 0) return; if(bytes > 64) return; // If canfd if(bytes > 8) { switch(bytes) { case 12: bytes = 0x9; break; case 16: bytes = 0xA; break; case 20: bytes = 0xB; break; case 24: bytes = 0xC; break; case 32: bytes = 0xD; break; case 48: bytes = 0xE; break; case 64: bytes = 0xF; break; } } // Add DLC to buffer buf[msg_idx++] = bytes; // Add data bytes for (uint8_t j = 0; j < msg.getLength(); j++) { buf[msg_idx++] = (msg.getByte(j) >> 4); buf[msg_idx++] = (msg.getByte(j) & 0x0F); } // Convert to ASCII (2nd character to end) for (uint8_t j = 1; j < msg_idx; j++) { if (buf[j] < 0xA) { buf[j] += 0x30; } else { buf[j] += 0x37; } } // Add CR for slcan EOL buf[msg_idx++] = '\r'; // Ensure null termination buf[msg_idx] = '\0'; _msg_queue.append(QString(buf)); } bool SLCANInterface::readMessage(QList &msglist, unsigned int timeout_ms) { // Don't saturate the thread. Read the buffer every 1ms. QThread().msleep(1); // Transmit all items that are queued while(!_msg_queue.empty()) { // Consume first item QString tmp = _msg_queue.front(); _msg_queue.pop_front(); _serport_mutex.lock(); // Write string to serial device _serport->write(tmp.toStdString().c_str(), tmp.length()); _serport->flush(); _serport->waitForBytesWritten(300); _serport_mutex.unlock(); } // RX doesn't work on windows unless we call this for some reason _serport->waitForReadyRead(1); if(_serport->bytesAvailable()) { // This is called when readyRead() is emitted QByteArray datas = _serport->readAll(); _rxbuf_mutex.lock(); for(int i=0; i= 'a') _rx_linbuf[i] = _rx_linbuf[i] - 'a' + 10; // Uppercase letters else if(_rx_linbuf[i] >= 'A') _rx_linbuf[i] = _rx_linbuf[i] - 'A' + 10; // Numbers else _rx_linbuf[i] = _rx_linbuf[i] - '0'; } // Handle each incoming command switch(_rx_linbuf[0]) { // Transmit data frame command case 'T': msg.setExtended(1); break; case 't': msg.setExtended(0); break; // Transmit remote frame command case 'r': msg.setExtended(0); msg.setRTR(1); break; case 'R': msg.setExtended(1); msg.setRTR(1); break; // CANFD transmit - no BRS case 'd': msg.setExtended(0); msg_is_fd = true; break; case 'D': msg.setExtended(1); msg_is_fd = true; break; // CANFD transmit - with BRS case 'b': msg.setExtended(0); msg_is_fd = true; break; case 'B': msg.setExtended(1); msg_is_fd = true; break; // Invalid command default: return false; } // Start parsing at second byte (skip command byte) uint8_t parse_loc = 1; // Default to standard id len uint8_t id_len = SLCAN_STD_ID_LEN; // Update length if message is extended ID if(msg.isExtended()) id_len = SLCAN_EXT_ID_LEN; uint32_t id_tmp = 0; // Iterate through ID bytes while(parse_loc <= id_len) { id_tmp *= 16; id_tmp += _rx_linbuf[parse_loc++]; } msg.setId(id_tmp); // Attempt to parse DLC and check sanity uint8_t dlc_code_raw = _rx_linbuf[parse_loc++]; // If dlc is too long for an FD frame if(msg_is_fd && dlc_code_raw > 0xF) { return false; } if(!msg_is_fd && dlc_code_raw > 0x8) { return false; } if(dlc_code_raw > 0x8) { switch(dlc_code_raw) { case 0x9: dlc_code_raw = 12; break; case 0xA: dlc_code_raw = 16; break; case 0xB: dlc_code_raw = 20; break; case 0xC: dlc_code_raw = 24; break; case 0xD: dlc_code_raw = 32; break; case 0xE: dlc_code_raw = 48; break; case 0xF: dlc_code_raw = 64; break; default: dlc_code_raw = 0; perror("Invalid length"); break; } } msg.setLength(dlc_code_raw); // Calculate number of bytes we expect in the message int8_t bytes_in_msg = dlc_code_raw; if(bytes_in_msg < 0) { perror("Invalid length < 0"); return false; } if(bytes_in_msg > 64) { perror("Invalid length > 64"); return false; } // Parse data // TODO: Guard against walking off the end of the string! for (uint8_t i = 0; i < bytes_in_msg; i++) { msg.setByte(i, (_rx_linbuf[parse_loc] << 4) + _rx_linbuf[parse_loc+1]); parse_loc += 2; } // Reset buffer _rx_linbuf_ctr = 0; _rx_linbuf[0] = '\0'; return true; /* // FIXME if (_ts_mode == ts_mode_SIOCSHWTSTAMP) { // TODO implement me _ts_mode = ts_mode_SIOCGSTAMPNS; } if (_ts_mode==ts_mode_SIOCGSTAMPNS) { if (ioctl(_fd, SIOCGSTAMPNS, &ts_rcv) == 0) { msg.setTimestamp(ts_rcv.tv_sec, ts_rcv.tv_nsec/1000); } else { _ts_mode = ts_mode_SIOCGSTAMP; } } if (_ts_mode==ts_mode_SIOCGSTAMP) { ioctl(_fd, SIOCGSTAMP, &tv_rcv); msg.setTimestamp(tv_rcv.tv_sec, tv_rcv.tv_usec); }*/ }