/*************************************************************************** BionxControl © 2025 -2026 christoph holzheuer christoph.holzheuer@gmail.com Using: mhs_can_drv.c © 2011 - 2023 by MHS-Elektronik GmbH & Co. KG, Germany Klaus Demlehner, klaus@mhs-elektronik.de @see www.mhs-elektronik.de Based on Bionx data type descriptions from: BigXionFlasher USB V 0.2.4 rev. 97 © 2011-2013 by Thomas Koenig @see www.bigxionflasher.org Bionx Bike Info © 2018 Thorsten Schmidt (tschmidt@ts-soft.de) @see www.ts-soft.de This program 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 3 of the License, or (at your option) any later version. @see https://github.com/bikemike/bionx-bikeinfo ***************************************************************************/ #include #include #include #if defined(Q_OS_WIN) // Unter Windows steht der Treibername in der registry static const char* cMHS_DRIVERNAME = NULL; #elif defined(Q_OS_LINUX) // Unter linux(artigen) muss der Treibername explizit mit übergeben werden static const char* cMHS_DRIVERNAME = "libmhstcan.so"; #endif /* // TinyCan C-Api // ------------- int32_t CanInitDriver(char *options); void CanDownDriver(void); int32_t CanSetOptions(char *options); int32_t CanDeviceOpen(uint32_t index, char *parameter); int32_t CanDeviceClose(uint32_t index); int32_t CanSetMode(uint32_t index, unsigned char can_op_mode, uint16_t can_command); int32_t CanTransmit(uint32_t index, struct TCanMsg *msg, int32_t count); void CanTransmitClear(uint32_t index); uint32_t CanTransmitGetCount(uint32_t index); int32_t CanTransmitSet(uint32_t index, uint16_t cmd, uint32_t time); int32_t CanReceive(uint32_t index, struct TCanMsg *msg, int32_t count); void CanReceiveClear(uint32_t index); uint32_t CanReceiveGetCount(uint32_t index); int32_t CanSetSpeed(uint32_t index, uint16_t speed); int32_t CanSetSpeedUser(uint32_t index, uint32_t value); char* CanDrvInfo(void); char* CanDrvHwInfo(uint32_t index); int32_t CanSetFilter(uint32_t index, struct TMsgFilter *msg_filter); int32_t CanGetDeviceStatus(uint32_t index, struct TDeviceStatus *status); void CanSetPnPEventCallback(void (DRV_CALLBACK_TYPE *event)(uint32_t index, int32_t status)); void CanSetStatusEventCallback(void (DRV_CALLBACK_TYPE *event) (uint32_t index, struct TDeviceStatus *device_status) ); void CanSetRxEventCallback(void (DRV_CALLBACK_TYPE *event)(uint32_t index, struct TCanMsg *msg, int32_t count) ); void CanSetEvents( uint16_t events ); uint32_t CanEventStatus(void); */ /** * @brief Destruktor. Entlädt den CAN-Bus Treiber wieder. */ BCDriverTinyCan::~BCDriverTinyCan() { resetDriver(); } /** * @brief BCDriverTinyCan::loadAndStartDriver * @return */ BCDriver::DriverStateResult BCDriverTinyCan::loadAndStartDriver() { DriverStateResult result; if( _driverState < DriverState::Opened) result = loadDriver(); if( _driverState == DriverState::Opened) result = setConsoleSlaveMode(); return result; } /** * @brief BCDriverTinyCan::loadDriver * @return */ BCDriver::DriverStateResult BCDriverTinyCan::loadDriver() { auto callLoadDriver = [&]() -> DriverStateResult { if( ::LoadDriver( cMHS_DRIVERNAME ) < 0 ) return std::unexpected(QString("Driver Error: 'LoadDriver'")); _driverState = DriverState::Loaded; return _driverState; }; auto callInitDriver = [&](DriverState state) -> DriverStateResult { Q_UNUSED(state) if( ::CanInitDriver( NULL ) < 0 ) return std::unexpected(QString("Driver Error: 'InitDriver'")); _driverState = DriverState::Initialized; return _driverState; }; auto callOpenDevice = [&](DriverState state) -> DriverStateResult { Q_UNUSED(state) if( ::CanDeviceOpen( 0, NULL ) < 0 ) return std::unexpected(QString("Driver Error: 'DeviceOpen'")); ::TDeviceStatus deviceStatus; ::CanSetSpeed( 0, CAN_125K_BIT ); ::CanSetMode( 0, OP_CAN_START, CAN_CMD_ALL_CLEAR ); ::CanGetDeviceStatus( 0, &deviceStatus ); if( deviceStatus.DrvStatus < DRV_STATUS_CAN_OPEN ) return std::unexpected(QString("Driver Error: could not open device." )); if( deviceStatus.CanStatus == CAN_STATUS_BUS_OFF ) { ::CanSetMode( 0, OP_CAN_RESET, CAN_CMD_NONE ); return std::unexpected(QString("Driver Error: CAN Status 'BusOff'" )); } _driverState = DriverState::Opened; return _driverState; }; qDebug() << " --- DRIVER: " << cMHS_DRIVERNAME; // #1. erstmal komplett zurücksetzen resetDriver(); // #2. Treiber laden, initialisieren und // mit dem tinyCan Interface verbinden. auto newDriverState = callLoadDriver() .and_then( callInitDriver ) .and_then( callOpenDevice ); // in Fehlerfall ist der Errorstring gesetzt, // der interne _driverstate ist // irgendwo unter DriverState::Opened return newDriverState; } /** * @brief Um mit dem Bionx eBike reden zu können, müssen wir * die Console in den Slave-Mode setzen. * @return Fehlerstring oder DriverState::DeviceReady */ BCDriver::DriverStateResult BCDriverTinyCan::setConsoleSlaveMode() { // Wir versuchen ein Test-Byte zu lesen, hier: einfach die Hardware // Revision der Console. uint32_t console = static_cast(BCDevice::ID::Console); uint8_t slaveFlag = static_cast (BC::ID::Cons_Status_Slave); unsigned int retry = cTimeOuts; TransmitResult isSlave = 0; // Already slave? isSlave = readRawByte( console, slaveFlag ); if( isSlave.has_value() && isSlave.value() == 1 ) goto happyEnd; do { writeRawByte( console, slaveFlag, 1 ); isSlave = readRawByte( console, slaveFlag ); bc::delay_millis( 200 ); } while( retry-- && !(*isSlave) ); bc::delay_millis( 500 ); // give the Console some time to settle if( isSlave.has_value() && isSlave.value() == 1 ) goto happyEnd; // ist das jetzt irgendwie schlimm, wenn wir keine slave Console haben return DriverState::Opened; happyEnd: _driverState = DriverState::DeviceReady; return DriverState::DeviceReady; } void BCDriverTinyCan::resetDriver() { if( _driverState > DriverState::NotPresent ) { ::CanDownDriver(); ::UnloadDriver(); _driverState = DriverState::NotPresent; } } /** * @brief BCDriverTinyCan::readRawByte * Kapselt den Treiberzugiff über die legacy C-Api. Liest ein byte, gibt dieses im std::expected aber * als uint32_t zurück, um mit der ReadFunctions der ValueTypes kompatible zu sein. * Im Fehlerfall wird ein Fehlerstring zurückgegeben. */ TransmitResult BCDriverTinyCan::readRawByte( uint32_t deviceID, uint8_t registerID ) const { if( _driverState