├── .gitignore ├── .gitlab-ci.yml ├── .reuse └── dep5 ├── LICENSES ├── GPL-2.0-only.txt ├── LGPL-2.1-only.txt └── MIT.txt ├── Makefile ├── README.md └── src ├── COPYING ├── IoProtocol.h ├── ModGateComError.h ├── ModGateComMain.h ├── ModGateRS485.h ├── PiBridgeMaster.c ├── PiBridgeMaster.h ├── RS485FwuCommand.c ├── RS485FwuCommand.h ├── RevPiDevice.c ├── RevPiDevice.h ├── bsp ├── setjmp │ └── BspSetJmp.h └── systick │ └── systick.h ├── common_define.h ├── fwuFlashFileMain.h ├── json.c ├── json.h ├── kbUtilities.c ├── kbUtilities.h ├── piAIOComm.c ├── piAIOComm.h ├── piConfig.c ├── piConfig.h ├── piControl.h ├── piControlMain.c ├── piControlMain.h ├── piDIOComm.c ├── piDIOComm.h ├── piFirmwareUpdate.c ├── piFirmwareUpdate.h ├── piIOComm.c ├── piIOComm.h ├── picontrol_intern.h ├── picontrol_ioctl.4 ├── picontrol_trace.h ├── process_image.h ├── project.h ├── pt100.c ├── pt100.h ├── pt100_table.inc ├── revpi_common.c ├── revpi_common.h ├── revpi_compact.c ├── revpi_compact.h ├── revpi_core.c ├── revpi_core.h ├── revpi_flat.c ├── revpi_flat.h ├── revpi_gate.c ├── revpi_gate.h ├── revpi_mio.c ├── revpi_mio.h ├── revpi_ro.c ├── revpi_ro.h └── systick.c /.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 KUNBUS GmbH 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | 4 | *.o 5 | *.cmd 6 | .tmp_versions/ 7 | Module.symvers 8 | compiletime.h 9 | modules.order 10 | piControl.ko 11 | piControl.mod.c 12 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 KUNBUS GmbH 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | 4 | include: 5 | - project: "revolutionpi/infrastructure/ci-templates" 6 | file: "base.yml" 7 | - project: "revolutionpi/infrastructure/ci-templates" 8 | file: "kernel/linter-checkpatch.yml" 9 | - project: "revolutionpi/infrastructure/ci-templates" 10 | file: "reuse-lint.yml" 11 | 12 | checkpatch: 13 | variables: 14 | CHECKPATCH_ARGS: "--no-tree" 15 | allow_failure: true 16 | 17 | coverity-submit: 18 | stage: publish 19 | image: registry.gitlab.com/revolutionpi/infrastructure/docker-images/build-container/build-container-bookworm:arm64-latest 20 | tags: 21 | - self-hosted 22 | - host-arm64 23 | rules: 24 | - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/' 25 | variables: 26 | DEBIAN_FRONTEND: noninteractive 27 | script: 28 | - apt update && apt-get build-dep -y picontrol 29 | - | 30 | curl \ 31 | -sSfLo /tmp/cov-analysis-linux-arm64.tar.gz \ 32 | --form project=$CI_PROJECT_NAME \ 33 | --form token=$COVERITY_SCAN_TOKEN \ 34 | "https://scan.coverity.com/download/cxx/linux-ARM64" 35 | - tar -xzf /tmp/cov-analysis-linux-arm64.tar.gz 36 | - | 37 | headers_ver="$(dpkg-query \ 38 | -Wf'${Package}\n' linux-headers-*-rpi-v8 \ 39 | | sort -u \ 40 | | head -n1 \ 41 | | grep -Eo '([[:digit:]]+\.){2}[[:digit:]]-revpi[[:digit:]]+-rpi-v[[:digit:]]+')" 42 | - ./cov-analysis-linux-arm64*/bin/cov-build 43 | --dir cov-int 44 | make -j$(nproc) KDIR=/usr/src/linux-headers-"${headers_ver}"/ 45 | - tar -czf picontrol.tar.gz cov-int 46 | - | 47 | curl -sSfL \ 48 | --form token=$COVERITY_SCAN_TOKEN \ 49 | --form email=$CI_EMAIL \ 50 | --form file=@picontrol.tar.gz \ 51 | --form version="$CI_COMMIT_TAG" \ 52 | --form description="Automated release submit for $CI_COMMIT_TAG" \ 53 | https://scan.coverity.com/builds?project=$CI_PROJECT_NAME 54 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: piControl 3 | Upstream-Contact: KUNBUS GmbH 4 | Source: https://gitlab.com/revolutionpi/piControl 5 | 6 | Files: src/picontrol_ioctl.4 7 | Copyright: 2018-2024 KUNBUS GmbH 8 | License: GPL-2.0-only 9 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | # SPDX-FileCopyrightText: 2016-2024 KUNBUS GmbH 3 | 4 | obj-m := piControl.o 5 | 6 | #add other objects e.g. test.o 7 | piControl-y = src/piControlMain.o 8 | piControl-y += src/piIOComm.o 9 | piControl-y += src/piDIOComm.o 10 | piControl-y += src/piAIOComm.o 11 | piControl-y += src/RevPiDevice.o 12 | piControl-y += src/json.o 13 | piControl-y += src/piConfig.o 14 | piControl-y += src/RS485FwuCommand.o 15 | piControl-y += src/piFirmwareUpdate.o 16 | piControl-y += src/PiBridgeMaster.o 17 | piControl-y += src/kbUtilities.o 18 | piControl-y += src/systick.o 19 | piControl-y += src/revpi_common.o 20 | piControl-y += src/revpi_compact.o 21 | piControl-y += src/revpi_core.o 22 | piControl-y += src/revpi_gate.o 23 | piControl-y += src/revpi_flat.o 24 | piControl-y += src/pt100.o 25 | piControl-y += src/revpi_mio.o 26 | piControl-y += src/revpi_ro.o 27 | 28 | ccflags-y := -O2 29 | ccflags-y += -I$(src)/src 30 | ccflags-y += -D__KUNBUSPI_KERNEL__ -I$(src) 31 | ccflags-$(_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT 32 | 33 | KBUILD_CFLAGS += -g 34 | 35 | PWD := $(shell pwd) 36 | 37 | all: 38 | $(MAKE) -C $(KDIR) M=$(PWD) modules 39 | 40 | clean: 41 | $(MAKE) -C $(KDIR) M=$(PWD) clean 42 | rm -f $(piControl-y) 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | We have moved to GitLab! Read this for more information. 8 | 9 | We have recently moved our repositories to GitLab. You can find piControl 10 | here: https://gitlab.com/revolutionpi/piControl 11 | All repositories on GitHub will stay up-to-date by being synchronised from 12 | GitLab. 13 | 14 | We still maintain a presence on GitHub but our work happens over at GitLab. If 15 | you want to contribute to any of our projects we would prefer this contribution 16 | to happen on GitLab, but we also still accept contributions on GitHub if you 17 | prefer that. 18 |
19 | 20 | # piControl 21 | 22 | piControl is a kernel module for interfacing with RevPi hardware. It provides a 23 | common interface to all RevPi related IOs, which a user can consume via 24 | `/dev/piControl0`. 25 | 26 | ## Usage 27 | 28 | **NOTE**: Building the master branch requires a Linux kernel with support for 29 | the pibridge serdev driver (i.e. 30 | [revpi-6.1](https://gitlab.com/revolutionpi/linux/commits/devel/revpi-6.1)). 31 | The branch 32 | [`revpi-5.10`](https://gitlab.com/revolutionpi/linux/commits/revpi-5.10) does 33 | not require support for this and can be built against an earlier kernel version 34 | of the RevolutionPi kernel. 35 | 36 | ## Build the module 37 | 38 | All the following steps need to be executed on a RevPi device running the 39 | official image. It is also possible to compile the module on another system, 40 | but the instructions may vary. 41 | 42 | Install kernel headers: 43 | 44 | ``` 45 | sudo apt update 46 | sudo apt install linux-headers-revpi-v8 47 | ``` 48 | 49 | The package `linux-headers-revpi-v8` is available through our Debian repository. 50 | 51 | Checkout repository and switch working directory: 52 | 53 | ``` 54 | git clone https://gitlab.com/revolutionpi/piControl.git 55 | cd piControl/src 56 | ``` 57 | 58 | Compile module sources for current kernel and architecture: 59 | 60 | ``` 61 | KDIR=/usr/src/linux-headers-$(uname -r)/ make 62 | ``` 63 | 64 | ## Load the compiled module 65 | 66 | Before the newly compiled module can be loaded, the currently loaded module 67 | needs to be unloaded: 68 | 69 | ``` 70 | sudo rmmod piControl 71 | ``` 72 | 73 | > **NOTE**: If the command fails with an error such as `rmmod: ERROR: Module 74 | > piControl is in use`, applications that are using the module need to be 75 | > stopped. The command `sudo lsof /dev/piControl0` will show a list of 76 | > applications that are using the piControl device. 77 | 78 | Load the newly compiled module: 79 | 80 | ``` 81 | sudo insmod piControl.ko 82 | ``` 83 | 84 | This will only load the newly compiled module once. On the next reboot, the 85 | pre-installed module in `/lib/modules/$(uname -r)/extra/piControl.ko` will be 86 | loaded again. If the newly compiled module should be used permanently, then it 87 | needs to be copied into the modules folder: 88 | 89 | ``` 90 | sudo cp piControl.ko /lib/modules/$(uname -r)/extra/piControl.ko 91 | ``` 92 | -------------------------------------------------------------------------------- /src/ModGateComError.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef MODGATEERROR_H_INC 6 | #define MODGATEERROR_H_INC 7 | 8 | #include 9 | 10 | #include "bsp/setjmp/BspSetJmp.h" 11 | #include "common_define.h" 12 | 13 | // mGate errors 0x37xxxxxx 14 | typedef enum { 15 | MODGATECOM_NO_ERROR = 0x00000000, //!< Default Value 16 | 17 | // mGate communication 18 | MODGATECOM_ERROR_TIMER_INIT = 0x37000001, // Error during timer initialization 19 | MODGATECOM_ERROR_RS485_INIT = 0x37000002, // Error during RS485 initialization 20 | MODGATECOM_ERROR_RESP_SEQUENCE = 0x37000003, // Falsche Sequenznummer im Antwortframe 21 | MODGATECOM_ERROR_RESP_CMDNO = 0x37000004, // Falsche Kommandonummer im Antwortframe 22 | MODGATECOM_ERROR_WRONG_ID = 0x37000005, // Falsche Daten in der ID-Response 23 | MODGATECOM_ERROR_INVALID_DATA_SIZE = 0x37000006, // 24 | MODGATECOM_ERROR_INVALID_DATA_OFFSET = 0x37000007, // 25 | MODGATECOM_ERROR_WRONG_STATE = 0x37000008, // 26 | MODGATECOM_ERROR_CMD_UNKNOWN = 0x37000009, // 27 | MODGATECOM_ERROR_SEND_FAILED = 0x3700000a, // send failed 28 | MODGATECOM_ERROR_SEND_UNACKED = 0x3700000b, // send failed 29 | MODGATECOM_ERROR_NO_LINK = 0x3700000c, // 30 | MODGATECOM_ERROR_OUT_OF_MEMORY_1 = 0x3700000d, // 31 | MODGATECOM_ERROR_OUT_OF_MEMORY_2 = 0x3700000e, // 32 | MODGATECOM_ERROR_OUT_OF_MEMORY_3 = 0x3700000f, // 33 | MODGATECOM_ERROR_OUT_OF_MEMORY_4 = 0x37000010, // 34 | MODGATECOM_ERROR_OUT_OF_MEMORY_5 = 0x37000011, // 35 | MODGATECOM_ERROR_NO_PACKET = 0x37000012, // 36 | MODGATECOM_ERROR_ACK_MISSING = 0x37000013, // 37 | MODGATECOM_ERROR_UNKNOWN_CMD = 0x37000014, // 38 | 39 | // RS485 Command error codes 40 | MODGATECOM_ERROR_CMD_NOT_SUPPORTED = 0x37000100, // Unknown RS485 command 41 | MODGATECOM_ERROR_READ_FW_FLASH = 0x37000101, // Fehler beim Lesen des FW-Flashs 42 | MODGATECOM_ERROR_SERIAL_NUMBER_PROTECTED = 0x37000102, // Die schon vorhandene Seriennummer darf nicht überschrieben werden 43 | MODGATECOM_ERROR_SERIAL_NUMBER_WRITE = 0x37000103, // Beim Schreiben der Seriennummer ist ein Fehler aufgetreten 44 | MODGATECOM_ERROR_ERASE_FW_FLASH = 0x37000104, // Beim Löschen des Firmware Flashspeichers ist ein Fehler aufgetreten 45 | MODGATECOM_ERROR_RESET = 0x37000105, // Der Reset konnte nicht ausgeführt werden 46 | MODGATECOM_ERROR_UPDATE_MODE = 0x37000106, // Es kann nicht in den Updatemodus umgeschaltet werden 47 | MODGATECOM_ERROR_NO_UPDATE_MODE = 0x37000107, // Das Kommando darf nur im Updatemodus ausgeführt werden 48 | MODGATECOM_ERROR_TOO_FEW_FLASH_DATA = 0x37000108, // Flash Write : Too few data 49 | MODGATECOM_ERROR_FLASH_WRITE_OUT_OF_RANGE = 0x37000109, // Flash Write : Start address out of range 50 | MODGATECOM_ERROR_TOO_MANY_FLASH_DATA = 0x3700010a, // Flash Write : Overflow of allowed area 51 | MODGATECOM_ERROR_FLASH_WRITE = 0x3700010b, // Flash Write : Flash Programming Error 52 | MODGATECOM_ERROR_FLASH_WRITE_ALIGNMENT = 0x3700010c, // Flash Driver Write: odd alignment of start address 53 | MODGATECOM_ERROR_FLASH_WRITE_GENERAL = 0x3700010d, // Flash Driver Write: general write error 54 | MODGATECOM_ERROR_FLASH_ERASE_GENERAL = 0x3700010e, // Flash Driver Erase: general error 55 | MODGATECOM_ERROR_FLASH_READ_FORMAT = 0x3700010f, // Read Data: Invalid Data Format in Request 56 | MODGATECOM_ERROR_FLASH_READ_OUT_OF_RANGE = 0x37000110, // Read Data: Startaddress is out of range 57 | MODGATECOM_ERROR_NO_APPL = 0x37000111, // No Application loaded 58 | MODGATECOM_ERROR_NO_APPL_END = 0x37000112, // No valid application start or end address 59 | MODGATECOM_ERROR_DEVTYPE_EXIST = 0x37000113, // Device Type: Type allready programmed 60 | MODGATECOM_ERROR_DEVTYPE_FORMAT = 0x37000114, // Device Type: Telegram Format error 61 | MODGATECOM_ERROR_APPE_FORMAT = 0x37000115, // Application End Addr: Invalid Data Format in Request 62 | MODGATECOM_ERROR_APPE_EXIST = 0x37000116, // Application End Addr: Address was already written 63 | MODGATECOM_ERROR_HWR_FORMAT = 0x37000117, // Hardware Revision: Invalid Data Format in Request 64 | MODGATECOM_ERROR_HWR_EXIST = 0x37000118, // Hardware Revision: Revision was already written 65 | MODGATECOM_ERROR_MAC_FORMAT = 0x37000119, // MAC Address: Invalid Data Format in Request 66 | MODGATECOM_ERROR_MAC_EXIST = 0x3700011a, // MAC Address: Address was already written 67 | 68 | // 69 | MODGATECOM_ERROR_UNKNOWN = 0x37ffffff, // Unknown error, should never happens 70 | 71 | } EModGateComError; 72 | 73 | #define MODGATECOM_ASSERT(expr, errCode) if (!(expr)) MODGATECOM_error (errCode, bTRUE, 0) 74 | 75 | #ifdef __cplusplus 76 | extern "C" { 77 | #endif 78 | 79 | extern void MODGATECOM_errorInit(BSP_TJumpBuf * ptExceptionPoint_p, 80 | void (*cbErrHandler_p) (INT32U i32uErrorCode_p, TBOOL bFatal_p, 81 | INT8U i8uParaCnt_p, va_list argptr_p)); 82 | extern void MODGATECOM_error(INT32U i32uErrCode_p, TBOOL bFatalErr_p, INT8U i8uParaCnt_p, ...); 83 | extern INT32U MODGATECOM_has_fatal_error(void); 84 | 85 | #if defined(MGATE_ERROR_STACK) && defined(STM_WITH_EEPROM) 86 | #define MGATE_ERROR_STACK_ELEMENTS 8 //!< number of error stack elements 87 | 88 | void MODGATECOM_errorLogEEPROM(INT32U i32uErrCode_p); 89 | void MODGATECOM_getErrorLogEEPROM(INT32U * pai32uErrStackBuf_p); 90 | #endif //#if defined(MGATE_ERROR_STACK) && defined(STM_WITH_EEPROM) 91 | 92 | #ifdef __cplusplus 93 | } 94 | #endif 95 | #endif //MODGATEERROR_H_INC 96 | -------------------------------------------------------------------------------- /src/ModGateComMain.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef MODGATECOMMAIN_H_INC 6 | #define MODGATECOMMAIN_H_INC 7 | 8 | #define MODGATECOM_MAX_MODULES 2 9 | 10 | #if defined (_MSC_VER) 11 | #pragma warning (disable: 4200) 12 | #endif 13 | 14 | #include "common_define.h" 15 | #include "kbUtilities.h" 16 | 17 | typedef enum 18 | { 19 | FBSTATE_OFFLINE = 0x00, // Physikalisch nicht verbunden 20 | FBSTATE_LINK = 0x01, // Physikalisch verbunden, jedoch keine Kommunikation 21 | FBSTATE_STANDBY = 0x02, // Kommunikationsbereit (z.B. preoperational) 22 | FBSTATE_CYCLIC_IO = 0x03, // Zyklischer Datenaustausch ist aktiv (z.B. operational) 23 | } MODGATECOM_FieldbusStatus; 24 | 25 | //********************************************************************************************** 26 | // Link Layer 27 | //********************************************************************************************** 28 | typedef struct { 29 | INT8U i8uDestination[6]; 30 | INT8U i8uSource[6]; 31 | INT16U i16uType; 32 | #ifndef __KUNBUSPI_KERNEL__ 33 | INT8U i8uACK; //Acknowledge 34 | INT8U i8uCounter; 35 | #endif 36 | } __attribute__((__packed__)) MODGATECOM_LinkLayer; 37 | 38 | //********************************************************************************************** 39 | // Transport Layer 40 | //********************************************************************************************** 41 | typedef struct { 42 | #ifdef __KUNBUSPI_KERNEL__ 43 | INT8U i8uACK; //Acknowledge 44 | INT8U i8uCounter; 45 | #endif 46 | INT16U i16uCmd; 47 | INT16U i16uDataLength; 48 | INT32U i32uError; 49 | INT8U i8uVersion; 50 | INT8U i8uReserved; 51 | } __attribute__((__packed__)) MODGATECOM_TransportLayer; 52 | 53 | //********************************************************************************************** 54 | // Application Layer 55 | //********************************************************************************************** 56 | 57 | typedef enum 58 | { 59 | MODGATE_ST_HW_CHECK = 0x00, 60 | MODGATE_ST_LINK_CHECK = 0x01, 61 | MODGATE_ST_ID_REQ = 0x02, 62 | MODGATE_ST_ID_RESP = 0x03, 63 | MODGATE_ST_RUN_NO_DATA = 0x04, 64 | MODGATE_ST_RUN = 0x05, 65 | } MODGATE_AL_Status; 66 | 67 | typedef enum 68 | { 69 | MODGATECOM_enPLS_INIT = 0x01, 70 | MODGATECOM_enPLS_LINK_MISSING = 0x02, 71 | MODGATECOM_enPLS_DATA_MISSING = 0x03, 72 | MODGATECOM_enPLS_RUN = 0x04, 73 | MODGATECOM_enPLS_ERROR = 0x05, 74 | } MODGATECOM_EPowerLedState; 75 | 76 | 77 | typedef enum 78 | { 79 | MODGATE_AL_CMD_ID_Req = 0x0001, 80 | MODGATE_AL_CMD_ID_Resp = 0x8001, 81 | MODGATE_AL_CMD_cyclicPD = 0x0002, 82 | MODGATE_AL_CMD_updatePD = 0x0003, // not used yet 83 | MODGATE_AL_CMD_ST_Req = 0x0004, // not used 84 | MODGATE_AL_CMD_ST_Resp = 0x8004, // not used 85 | } MODGATE_AL_Command; 86 | 87 | // Feature descriptor bits 88 | #define MODGATE_feature_IODataExchange 0x0001 // supports data-exchange using ethernet (e.g. mGate) 89 | #define MODGATE_feature_RS485DataExchange 0x0002 // supports data exchange using RS485 (e.g. piDio) 90 | 91 | 92 | #define MODGATE_LL_HEADER_LEN (sizeof(MODGATECOM_LinkLayer)) // 16 93 | #define MODGATE_TL_HEADER_LEN (sizeof(MODGATECOM_LinkLayer) + sizeof(MODGATECOM_TransportLayer)) // 26 94 | #define MODGATE_MAX_PD_DATALEN 512 95 | #define MODGATE_AL_MAX_LEN (sizeof(MODGATECOM_CyclicPD) + MODGATE_MAX_PD_DATALEN) // 543 size of the biggest AL packet 96 | #define MODGATE_LL_MAX_LEN ((MODGATE_TL_HEADER_LEN + MODGATE_AL_MAX_LEN + 3) & 0xfffffffc) // 544 bigger packet on the line, rounded up to the next multiple of 4 97 | 98 | //********************************************************************************************** 99 | typedef struct { 100 | INT32U i32uSerialnumber; 101 | INT16U i16uModulType; 102 | INT16U i16uHW_Revision; 103 | INT16U i16uSW_Major; 104 | INT16U i16uSW_Minor; 105 | INT32U i32uSVN_Revision; 106 | INT16U i16uFBS_InputLength; 107 | INT16U i16uFBS_OutputLength; 108 | INT16U i16uFeatureDescriptor; 109 | } __attribute__((__packed__)) MODGATECOM_IDResp; 110 | 111 | //********************************************************************************************** 112 | typedef struct { 113 | INT8U i8uFieldbusStatus; // type MODGATECOM_FieldbusStatus 114 | INT16U i16uOffset; 115 | INT16U i16uDataLen; 116 | INT8U i8uData[0]; // dummy declaration for up to MODGATE_MAX_PD_DATALEN bytes 117 | } __attribute__((__packed__)) MODGATECOM_CyclicPD; 118 | 119 | typedef struct { 120 | MODGATECOM_LinkLayer strLinkLayer; 121 | MODGATECOM_TransportLayer strTransportLayer; 122 | INT8U i8uData[MODGATE_AL_MAX_LEN]; 123 | } __attribute__((__packed__)) MODGATECOM_Packet; 124 | 125 | //********************************************************************************************** 126 | 127 | #ifdef __cplusplus 128 | extern "C" { 129 | #endif 130 | 131 | //********************************************************************************************** 132 | // Link Layer 133 | //********************************************************************************************** 134 | 135 | typedef struct _sLLData 136 | { 137 | MODGATECOM_LinkLayer Header; 138 | INT8U state; 139 | INT32U send_tick; // tick counter of last sent packet 140 | INT8U send_retry; // retry counter 141 | INT32U recv_tick; // tick counter of last recv packet 142 | TBOOL timed_out; 143 | MODGATECOM_Packet *pLastData; 144 | } sLLData; 145 | 146 | typedef sLLData *LLHandle; 147 | 148 | INT32U MG_LL_init (LLHandle llHdl); 149 | INT32U MG_LL_send (LLHandle llHdl, MODGATECOM_Packet *pData_p); 150 | MODGATECOM_Packet *MG_LL_recv(LLHandle llHdl, INT32U *pi32uStatus_p, INT16U *pi16uLen_p); 151 | TBOOL MG_LL_pending (LLHandle llHdl); 152 | void MG_LL_abort (LLHandle llHdl); 153 | TBOOL MG_LL_timed_out (LLHandle llHdl); 154 | 155 | //********************************************************************************************** 156 | // Application Layer 157 | //********************************************************************************************** 158 | typedef struct _sALData 159 | { 160 | sLLData llParas; 161 | 162 | MODGATECOM_IDResp OtherID; //!< ID-Data of other mGate 163 | kbUT_Timer AL_Timeout; 164 | 165 | INT8U *pi8uInData; 166 | INT16U i16uInDataLen; 167 | INT16U i16uInDataLenActive; 168 | 169 | INT8U *pi8uOutData; 170 | INT16U i16uOutDataLen; 171 | INT16U i16uOutDataLenActive; 172 | 173 | INT8U i8uState; //!< modular Gateway state machine state 174 | INT8U i8uOtherFieldbusState; //!< Fieldbus State of other mGate 175 | MODGATECOM_EPowerLedState enLedStateAct; 176 | MODGATECOM_EPowerLedState enLedStateOld; 177 | } sALData; 178 | 179 | typedef sALData *ALHandle; 180 | 181 | extern sALData AL_Data_s[MODGATECOM_MAX_MODULES]; 182 | 183 | //********************************************************************************************** 184 | #ifndef __KUNBUSPI_KERNEL__ 185 | INT32U MODGATECOM_init (INT8U *pi8uInData_p, INT16U i16uInDataLen_p, INT8U *pi8uOutData_p, INT16U i16uOutDataLen_p, ETHERNET_INTERFACE *EthDrv); 186 | void MODGATECOM_run (void); 187 | 188 | void MODGATECOM_SetOwnFieldbusState(INT8U i8uOwnFieldbusState_p); 189 | INT8U MODGATECOM_GetOwnFieldbusState(void); 190 | INT8U MODGATECOM_GetOtherFieldbusState(INT8U i8uInst_p); // in modular Gateways always 0 must be passed 191 | MODGATECOM_EPowerLedState MODGATECOM_GetLedState(void); 192 | 193 | INT16U MODGATECOM_GetInputDataLengthActive(INT8U i8uInstance_p); // in modular Gateways, always 0 must be passed 194 | INT16U MODGATECOM_GetOutputDataLengthActive(INT8U i8uInstance_p); // ditto 195 | 196 | //********************************************************************************************** 197 | // internal 198 | //********************************************************************************************** 199 | 200 | INT32U MODGATECOM_send_ID_Req (ALHandle); 201 | INT32U MODGATECOM_send_ID_Resp (ALHandle); 202 | INT32U MODGATECOM_send_cyclicPD (ALHandle); 203 | 204 | TBOOL MODGATECOM_recv_Id_Resp (ALHandle, MODGATECOM_Packet *pPacket_p); 205 | TBOOL MODGATECOM_recv_cyclicPD (ALHandle, MODGATECOM_Packet *pPacket_p); 206 | 207 | void MODGATECOM_managePowerLedRun (void); 208 | MODGATE_AL_Status MODGATECOM_GetState(INT8U i8uInst_p); 209 | 210 | //********************************************************************************************** 211 | 212 | extern MODGATECOM_IDResp MODGATE_OwnID_g; //!< ID-Data of this mGate 213 | 214 | 215 | 216 | #define MODGATECOM_GetOtherFieldbusStatePtr(i) (&AL_Data_s[i].i8uOtherFieldbusState) 217 | #define MODGATECOM_GetOwnIdDataPtr() (&MODGATE_OwnID_g) 218 | #define MODGATECOM_GetOtherIdDataPtr(i) (&AL_Data_s[i].OtherID) 219 | 220 | 221 | // internal functions 222 | // for all functions: return value bTRUE is send/receive was successful 223 | TBOOL MODGATECOM_receive (void); 224 | TBOOL MODGATECOM_send (INT16U cmd); 225 | TBOOL MODGATECOM_send_ACK (void); 226 | 227 | void MODGATECOM_T1_Handler (void); 228 | void MODGATECOM_T2_Handler (void); 229 | 230 | #endif // !__KUNBUSPI_KERNEL__ 231 | 232 | #ifdef __cplusplus 233 | } 234 | #endif 235 | 236 | 237 | #endif //MODGATECOMMAIN_H_INC 238 | -------------------------------------------------------------------------------- /src/ModGateRS485.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RevolutionPi/piControl/d463aec177b3549b03efa870f90d03482fdf00de/src/ModGateRS485.h -------------------------------------------------------------------------------- /src/PiBridgeMaster.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | */ 4 | 5 | #pragma once 6 | 7 | #include "common_define.h" 8 | 9 | typedef enum _EPiBridgeMasterStatus { 10 | // states for IO Protocol 11 | enPiBridgeMasterStatus_Init, // 0 12 | enPiBridgeMasterStatus_MasterIsPresentSignalling1, // 1 13 | enPiBridgeMasterStatus_MasterIsPresentSignalling2, // 2 14 | enPiBridgeMasterStatus_InitialSlaveDetectionRight, // 3 15 | enPiBridgeMasterStatus_ConfigRightStart, // 4 16 | enPiBridgeMasterStatus_ConfigDialogueRight, // 5 17 | enPiBridgeMasterStatus_SlaveDetectionRight, // 6 18 | enPiBridgeMasterStatus_InitialSlaveDetectionLeft, // 7 19 | enPiBridgeMasterStatus_ConfigLeftStart, // 8 20 | enPiBridgeMasterStatus_ConfigDialogueLeft, // 9 21 | enPiBridgeMasterStatus_SlaveDetectionLeft, // 10 22 | enPiBridgeMasterStatus_EndOfConfig, // 11 23 | enPiBridgeMasterStatus_Continue, // 12 24 | enPiBridgeMasterStatus_InitRetry, // 13 25 | 26 | // states for MGate Protocol 27 | enPiBridgeMasterStatus_FWUMode, // 14 28 | enPiBridgeMasterStatus_ProgramSerialNum, // 15 29 | enPiBridgeMasterStatus_FWUFlashErase, // 16 30 | enPiBridgeMasterStatus_FWUFlashWrite, // 17 31 | enPiBridgeMasterStatus_FWUReset, // 18 32 | 33 | } EPiBridgeMasterStatus; 34 | 35 | extern EPiBridgeMasterStatus eRunStatus_s; 36 | 37 | void PiBridgeMaster_Reset(void); 38 | int PiBridgeMaster_Adjust(void); 39 | void PiBridgeMaster_setDefaults(void); 40 | int PiBridgeMaster_Run(void); 41 | void PiBridgeMaster_Stop(void); 42 | void PiBridgeMaster_Continue(void); 43 | INT32S PiBridgeMaster_FWUModeEnter(INT32U address, INT8U i8uScanned); 44 | INT32S PiBridgeMaster_FWUsetSerNum(INT32U serNum); 45 | INT32S PiBridgeMaster_FWUflashErase(void); 46 | INT32S PiBridgeMaster_FWUflashWrite(INT32U flash_add, char *data, INT32U length); 47 | INT32S PiBridgeMaster_FWUReset(void); 48 | -------------------------------------------------------------------------------- /src/RS485FwuCommand.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2017-2024 KUNBUS GmbH 3 | 4 | // Firmware update of RevPi modules using gateway protocol over RS-485 5 | 6 | #include 7 | #include 8 | 9 | #include "ModGateRS485.h" 10 | #include "piIOComm.h" 11 | #include "RS485FwuCommand.h" 12 | 13 | int fwuEnterFwuMode(u8 address) 14 | { 15 | int ret; 16 | 17 | /* Ignore the response for this command */ 18 | ret = pibridge_req_send_gate(address, eCmdSetFwUpdateMode, NULL, 0); 19 | if (ret) 20 | return ret; 21 | 22 | msleep(100); 23 | return 0; 24 | } 25 | 26 | int fwuWriteSerialNum (u8 address, u32 sernum) 27 | { 28 | int ret; 29 | u16 err; 30 | 31 | /* 32 | * Writing the serial number to flash takes time. 33 | * Allow an extra 100 msec before expecting a response. 34 | * The response consists of an error code which is 0 on success. 35 | */ 36 | ret = pibridge_req_gate_tmt(address, eCmdWriteSerialNumber, 37 | &sernum, sizeof(sernum), 38 | &err, sizeof(err), 39 | REV_PI_IO_TIMEOUT + 100); 40 | if (ret < 0) 41 | return ret; 42 | 43 | if (ret < sizeof(err)) { 44 | pr_warn("Truncated WriteSerialNumber response (addr %hhu)\n", 45 | address); 46 | return -EIO; 47 | } 48 | 49 | if (err) { 50 | pr_warn("Error in WriteSerialNumber response (addr %hhu: 0x%04x)\n", 51 | address, err); 52 | return -EIO; 53 | } 54 | 55 | return 0; 56 | } 57 | 58 | 59 | int fwuEraseFlash (u8 address) 60 | { 61 | int ret; 62 | u16 err; 63 | 64 | /* 65 | * Allow 6 sec before expecting a response. 66 | * The response consists of an error code which is 0 on success. 67 | */ 68 | ret = pibridge_req_gate_tmt(address, eCmdEraseFwFlash, 69 | NULL, 0, 70 | &err, sizeof(err), 71 | 6000); 72 | if (ret < 0) 73 | return ret; 74 | 75 | if (ret < sizeof(err)) { 76 | pr_warn("Truncated EraseFwFlash response (addr %hhu)\n", 77 | address); 78 | return -EIO; 79 | } 80 | 81 | if (err) { 82 | pr_warn("Error in EraseFwFlash response (addr %hhu: 0x%04x)\n", 83 | address, err); 84 | return -EIO; 85 | } 86 | 87 | return 0; 88 | } 89 | 90 | int fwuWrite(u8 address, u32 flashAddr, char *data, u32 length) 91 | { 92 | u8 sendbuf[MAX_TELEGRAM_DATA_SIZE]; 93 | int ret; 94 | u16 err; 95 | 96 | memcpy (sendbuf, &flashAddr, sizeof (flashAddr)); 97 | if (length == 0 || length > MAX_TELEGRAM_DATA_SIZE - sizeof(flashAddr)) 98 | return -EINVAL; 99 | 100 | memcpy (sendbuf + sizeof (flashAddr), data, length); 101 | 102 | /* 103 | * Allow 1 sec before expecting a response. 104 | * The response consists of an error code which is 0 on success. 105 | */ 106 | ret = pibridge_req_gate_tmt(address, eCmdWriteFwFlash, 107 | sendbuf, sizeof(flashAddr) + length, 108 | &err, sizeof(err), 109 | 1000); 110 | if (ret < 0) 111 | return ret; 112 | 113 | if (ret < sizeof(err)) { 114 | pr_warn("Truncated WriteFwFlash response (addr %hhu)\n", 115 | address); 116 | return -EIO; 117 | } 118 | 119 | if (err) { 120 | pr_warn("Error in WriteFwFlash response (addr %hhu: 0x%04x)\n", 121 | address, err); 122 | return -EIO; 123 | } 124 | 125 | return 0; 126 | } 127 | 128 | int fwuResetModule (u8 address) 129 | { 130 | int ret; 131 | 132 | /* There is no response for this command. */ 133 | ret = pibridge_req_send_gate(address, eCmdResetModule, NULL, 0); 134 | if (ret) 135 | return ret; 136 | 137 | msleep(10000); 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /src/RS485FwuCommand.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * SPDX-FileCopyrightText: 2017-2024 KUNBUS GmbH 3 | * 4 | * Firmware update of RevPi modules using gateway protocol over RS-485 5 | */ 6 | 7 | #pragma once 8 | 9 | int fwuEnterFwuMode(u8 address); 10 | int fwuWriteSerialNum(u8 address, u32 i32uSerNum_p); 11 | int fwuEraseFlash (u8 address); 12 | int fwuWrite(u8 address, u32 flashAddr, char *data, u32 length); 13 | int fwuResetModule(u8 address); 14 | -------------------------------------------------------------------------------- /src/RevPiDevice.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | */ 4 | 5 | #pragma once 6 | 7 | #include "common_define.h" 8 | #include "ModGateComMain.h" 9 | #include "piIOComm.h" 10 | 11 | typedef struct _SRevPiProcessImage SRevPiProcessImage; 12 | 13 | #define REV_PI_DEV_UNDEF 255 14 | #define REV_PI_DEV_FIRST_RIGHT 32 15 | #define REV_PI_DEV_CNT_MAX 64 16 | #define REV_PI_DEV_DEFAULT_SERIAL 1 17 | 18 | typedef struct _SDevice 19 | { 20 | INT8U i8uAddress; 21 | INT8U i8uActive; 22 | INT8U i8uScan; // found on scan 23 | INT16U i16uInputOffset; 24 | INT16U i16uOutputOffset; 25 | INT16U i16uConfigLength; 26 | INT16U i16uConfigOffset; 27 | INT16U i16uErrorCnt; 28 | MODGATECOM_IDResp sId; 29 | INT8U i8uModuleState; 30 | INT8U i8uPriv; //used by the module privately 31 | } SDevice; 32 | 33 | 34 | typedef struct _SDeviceConfig 35 | { 36 | INT8U i8uAddressRight; 37 | INT8U i8uAddressLeft; 38 | INT8U i8uDeviceCount; 39 | INT16U i16uErrorCnt; 40 | 41 | INT8U i8uStatus; // status bitfield of RevPi 42 | unsigned int offset; // Offset in RevPi in process image 43 | SDevice dev[REV_PI_DEV_CNT_MAX+1]; 44 | } SDeviceConfig; 45 | 46 | //------------------------------------------------------------------------------------------------- 47 | 48 | TBOOL RevPiDevice_writeNextConfiguration(INT8U i8uAddress_p, MODGATECOM_IDResp *pModgateId_p); 49 | 50 | void RevPiDevice_init(void); 51 | 52 | int RevPiDevice_run(void); 53 | TBOOL RevPiDevice_writeNextConfigurationRight(void); 54 | TBOOL RevPiDevice_writeNextConfigurationLeft(void); 55 | void RevPiDevice_startDataexchange(void); 56 | void RevPiDevice_stopDataexchange(void); 57 | void RevPiDevice_checkFirmwareUpdate(void); 58 | u8 RevPiDevice_find_by_side_and_type(bool right, u16 module_type); 59 | INT8U RevPiDevice_setStatus(INT8U clr, INT8U set); 60 | INT8U RevPiDevice_getStatus(void); 61 | 62 | void RevPiDevice_resetDevCnt(void); 63 | void RevPiDevice_incDevCnt(void); 64 | INT8U RevPiDevice_getDevCnt(void); 65 | 66 | INT8U RevPiDevice_getAddrLeft(void); 67 | INT8U RevPiDevice_getAddrRight(void); 68 | 69 | INT16U RevPiDevice_getErrCnt(void); 70 | SDevice *RevPiDevice_getDev(INT8U idx); 71 | 72 | void RevPiDevice_setCoreOffset(unsigned int offset); 73 | unsigned int RevPiDevice_getCoreOffset(void); 74 | 75 | int RevPiDevice_hat_serial(void); 76 | void revpi_dev_update_state(INT8U i8uDevice, INT32U r, int *retval); 77 | 78 | -------------------------------------------------------------------------------- /src/bsp/setjmp/BspSetJmp.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | */ 4 | 5 | //+============================================================================================= 6 | //| 7 | //! \file BspSetJmp.h 8 | //! 9 | //! BSP long jump definitions. 10 | //| 11 | //+--------------------------------------------------------------------------------------------- 12 | //| 13 | //| Files required: (none) 14 | //| 15 | //+============================================================================================= 16 | #ifndef BSPSETJMP_H_INC 17 | #define BSPSETJMP_H_INC 18 | 19 | #include "../../common_define.h" 20 | 21 | #if defined (_MSC_VER) || defined(__NIOS_GENERIC__) || defined(__SF2_GENERIC__) || defined (__KUNBUSPI__) 22 | #include 23 | 24 | typedef jmp_buf BSP_TJumpBuf; 25 | 26 | #define bspSetJmp(x) setjmp (x) 27 | #define bspLongJmp(x,y) longjmp ((x), (y)) 28 | 29 | #else 30 | typedef INT32U BSP_TJumpBuf[12]; 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | extern INT32S bspSetJmp (BSP_TJumpBuf tJmpBuf_p); 37 | extern void bspLongJmp(BSP_TJumpBuf tJmpBuf_p, INT32S i32sValue_p); 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | 43 | #endif 44 | 45 | 46 | 47 | 48 | #ifdef __cplusplus 49 | extern "C" { 50 | #endif 51 | 52 | #ifdef __cplusplus 53 | } 54 | #endif 55 | 56 | 57 | #endif // BSPSETJMP_H_INC 58 | 59 | -------------------------------------------------------------------------------- /src/bsp/systick/systick.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | */ 4 | 5 | //+============================================================================================= 6 | //| 7 | //! \file systick.h 8 | //! 9 | //! Cortex M3 System tick access. 10 | //| 11 | //| Files required: (none) 12 | //| 13 | //+============================================================================================= 14 | #ifndef __SYSTICK_H 15 | #define __SYSTICK_H 16 | 17 | #include "../../common_define.h" 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | 24 | //+============================================================================================= 25 | //| Typen / types 26 | //+============================================================================================= 27 | 28 | //typedef ... 29 | 30 | 31 | //+============================================================================================= 32 | //| Prototypen / prototypes 33 | //+============================================================================================= 34 | extern void SysTickHandler ( void ); 35 | extern void ext6_handler ( void ); 36 | 37 | extern INT32U kbGetTickCount ( void ); 38 | 39 | 40 | extern void BSP_STK_init (void); 41 | 42 | 43 | #ifdef __cplusplus 44 | } 45 | #endif 46 | 47 | //-------------------- reinclude-protection -------------------- 48 | #endif//__SYSTICK_H 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/common_define.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2016-2024 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef _COMMON_DEFINE_H_ 6 | #define _COMMON_DEFINE_H_ 7 | 8 | #define _32BIT_U_CONTROLLER_ 9 | #undef _MSC_VER 10 | 11 | //+============================================================================================= 12 | //| Typen / types 13 | //+============================================================================================= 14 | 15 | //__GNUC__ 16 | 17 | // Static Types 18 | typedef unsigned char INT8U; ///< 8 Bit unsigned integer 19 | typedef signed char INT8S; ///< 8 Bit signed integer 20 | typedef unsigned short INT16U; ///< 16 Bit unsigned integer 21 | typedef signed short INT16S; ///< 16 Bit signed integer 22 | typedef unsigned int INT32U; ///< 32 Bit unsigned integer 23 | typedef signed int INT32S; ///< 32 Bit signed integer 24 | typedef unsigned long long INT64U; ///< 64 Bit unsigned integer 25 | typedef signed long long INT64S; ///< 64 Bit signed integer 26 | typedef float FLOAT32; ///< 32 Bit signed floating point 27 | typedef double FLOAT64; ///< 64 Bit signed floating point 28 | typedef unsigned char TBOOL; ///< Boolean value (bTRUE/bFALSE) 29 | typedef unsigned char CHAR8U; ///< 8 Bit unsigned character 30 | typedef signed char CHAR8S; ///< 8 Bit signed character 31 | typedef char CHAR8; ///< 8 Bit character 32 | 33 | typedef unsigned short uint16_t; ///< 16 Bit unsigned integer 34 | 35 | #ifdef __cplusplus 36 | #define bTRUE true 37 | #define bFALSE false 38 | #else /* */ 39 | #define bTRUE ((TBOOL)1) 40 | #define bFALSE ((TBOOL)0) 41 | #endif /* */ 42 | 43 | #ifndef NULL 44 | #ifdef __cplusplus 45 | #define NULL 0 46 | #define NULL_PTR 0 47 | #else /* */ 48 | #define NULL ((void *)0) 49 | #define NULL_PTR ((void *)0) 50 | #endif /* */ 51 | #endif /* */ 52 | 53 | //+============================================================================================= 54 | //| Konstanten / constant data 55 | //+============================================================================================= 56 | 57 | typedef struct S_KUNBUS_REV_NUMBER { 58 | 59 | INT8U IDENT1[9]; ///< identification String 1 "KB_SW_REV" 60 | INT8U SW_MAJOR; ///< major revision number; valid numbers 0-50, other numbers reserved 61 | INT16U SW_MINOR; ///< minor revision number; valid numbers 0-1000, other numbers reserved 62 | INT32U REVISION; ///< SVN revision number (mainly for internal use); 63 | ///< valid numbers 0-999999, other numbers reserved; typicalli SVN rev. 64 | } T_KUNBUS_REV_NUMBER; ///< Kunbus internal revision and release number 65 | 66 | #define KUNBUS_FW_DESCR_TYP_MG_CAN_OPEN 71 67 | #define KUNBUS_FW_DESCR_TYP_MG_CCLINK 72 68 | #define KUNBUS_FW_DESCR_TYP_MG_DEV_NET 73 69 | #define KUNBUS_FW_DESCR_TYP_MG_ETHERCAT 74 70 | #define KUNBUS_FW_DESCR_TYP_MG_ETHERNET_IP 75 71 | #define KUNBUS_FW_DESCR_TYP_MG_POWERLINK 76 72 | #define KUNBUS_FW_DESCR_TYP_MG_PROFIBUS 77 73 | #define KUNBUS_FW_DESCR_TYP_MG_PROFINET_RT 78 74 | #define KUNBUS_FW_DESCR_TYP_MG_PROFINET_IRT 79 75 | #define KUNBUS_FW_DESCR_TYP_MG_CAN_OPEN_MASTER 80 76 | #define KUNBUS_FW_DESCR_TYP_MG_SERCOS3 81 77 | #define KUNBUS_FW_DESCR_TYP_MG_SERIAL 82 78 | #define KUNBUS_FW_DESCR_TYP_MG_PROFINET_SITARA 83 79 | #define KUNBUS_FW_DESCR_TYP_MG_PROFINET_IRT_MASTER 84 80 | #define KUNBUS_FW_DESCR_TYP_MG_ETHERCAT_MASTER 85 81 | #define KUNBUS_FW_DESCR_TYP_MG_MODBUS_RTU 92 82 | #define KUNBUS_FW_DESCR_TYP_MG_MODBUS_TCP 93 83 | #define KUNBUS_FW_DESCR_TYP_PI_CORE 95 84 | #define KUNBUS_FW_DESCR_TYP_PI_DIO_14 96 85 | #define KUNBUS_FW_DESCR_TYP_PI_DI_16 97 86 | #define KUNBUS_FW_DESCR_TYP_PI_DO_16 98 87 | #define KUNBUS_FW_DESCR_TYP_MG_DMX 100 88 | #define KUNBUS_FW_DESCR_TYP_PI_AIO 103 89 | #define KUNBUS_FW_DESCR_TYP_PI_COMPACT 104 90 | #define KUNBUS_FW_DESCR_TYP_PI_CONNECT 105 91 | #define KUNBUS_FW_DESCR_TYP_PI_CON_CAN 109 92 | #define KUNBUS_FW_DESCR_TYP_PI_CON_MBUS 110 93 | #define KUNBUS_FW_DESCR_TYP_PI_CON_BT 111 94 | #define KUNBUS_FW_DESCR_TYP_PI_MIO 118 95 | #define KUNBUS_FW_DESCR_TYP_PI_FLAT 135 96 | #define KUNBUS_FW_DESCR_TYP_PI_CONNECT_4 136 97 | #define KUNBUS_FW_DESCR_TYP_PI_RO 137 98 | #define KUNBUS_FW_DESCR_TYP_PI_CONNECT_5 138 99 | 100 | #define KUNBUS_FW_DESCR_TYP_PI_REVPI_GENERIC_PB 0xfffe 101 | #define KUNBUS_FW_DESCR_TYP_INTERN 0xffff 102 | #define KUNBUS_FW_DESCR_TYP_UNDEFINED 0xffff 103 | 104 | #define KUNBUS_FW_DESCR_MAC_ADDR_LEN 6 //!< number of bytes in a MAC Address 105 | 106 | typedef struct S_KUNBUS_FW_DESCR { 107 | INT32U i32uLength; ///< number of bytes in struct, used to determine which elements are present 108 | /// must be always the first member of the struct 109 | INT32U i32uSerialNumber; ///< 32 Bit serial number 110 | INT16U i16uDeviceType; ///< Kunbus internal, unambiguous device type 111 | INT16U i16uHwRevision; ///< Revision of hardware, used for firmware update 112 | INT8U ai8uMacAddr[KUNBUS_FW_DESCR_MAC_ADDR_LEN]; ///< Field for manufacturer set MAC Address 113 | INT32U i32uFwuEntryAddr; //!< Entry of Firmwareupdate from application 114 | INT32U i32uApplStartAddr; //!< Startaddress of application specific flash area 115 | INT32U i32uApplEndAddr; //!< Last address of application specific flash area 116 | } __attribute__((__packed__)) T_KUNBUS_FW_DESCR; ///< Kunbus internal option bytes 117 | 118 | 119 | typedef struct S_KUNBUS_APPL_DESCR { 120 | INT32U i32uLength; //!< number of bytes in struct, used to determine which elements are present 121 | INT32U i32uVectorAddr; //!< address of vector table. 122 | INT32U i32uCrcCheckStartAddr; //!< first address of application area for crc check 123 | INT32U i32uCrcCheckEndAddr; //!< last address of application area for crc check 124 | INT32U i32uCrcAddr; //!< address of CRC Checksum over application area 125 | INT8U ai8uIdent1[9]; ///< identification String 1 "KB_SW_REV" 126 | INT8U i8uSwMajor; ///< major revision number; valid numbers 0-50, other numbers reserved 127 | INT16U i16uSwMinor; ///< minor revision number; valid numbers 0-1000, other numbers reserved 128 | INT32U i32uSvnRevision; ///< SVN revision number (mainly for internal use); 129 | INT32U i32uBootFlags; ///< Boot action flags 130 | } __attribute__((__packed__)) T_KUNBUS_APPL_DESCR; 131 | 132 | typedef struct S_KUNBUS_CNFG_DATA_HDR { 133 | INT8U ai8uIdent[4]; ///< identification String 1 "KBCD" 134 | INT32U i32uCrc; ///< address of CRC Checksum over application area 135 | INT32U i32uLength; ///< number of bytes stored directly after this header 136 | INT16U i16uDeviceType; ///< Kunbus internal, unambiguous device type 137 | INT16U i16uHwRevision; ///< Revision of hardware 138 | INT8U i8uSwMajor; ///< major revision number; valid numbers 0-50, other numbers reserved 139 | INT8U ai8uDummy[3]; ///< padding 140 | } __attribute__((__packed__)) T_KUNBUS_CNFG_DATA_HDR; 141 | 142 | 143 | //+============================================================================================= 144 | //| Prototypen / prototypes 145 | //+============================================================================================= 146 | 147 | #ifdef __cplusplus 148 | extern "C" { 149 | #endif /* 150 | */ 151 | 152 | extern const T_KUNBUS_APPL_DESCR ctKunbusApplDescription_g; 153 | 154 | extern const T_KUNBUS_FW_DESCR ctKunbusFirmwareDescription_g; 155 | 156 | extern char *sKunbusFWDescription_g; 157 | 158 | #ifdef __cplusplus 159 | } 160 | #endif /* 161 | */ 162 | #else // _COMMON_DEFINE_H_ 163 | // #pragma message "_COMMON_DEFINE_H_ already defined !" 164 | #endif // _COMMON_DEFINE_H_ 165 | -------------------------------------------------------------------------------- /src/fwuFlashFileMain.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2017-2023 KUNBUS GmbH 3 | */ 4 | 5 | //+============================================================================================= 6 | //| 7 | //! \file fwuMain.h 8 | //! 9 | //! Firmware update main routines. 10 | //| 11 | //+--------------------------------------------------------------------------------------------- 12 | //| 13 | //| Files required: (none) 14 | //| 15 | //+============================================================================================= 16 | #ifndef FWUFLASHFILEMAIN_H_INC 17 | #define FWUFLASHFILEMAIN_H_INC 18 | 19 | #if defined(__STM32GENERIC__) 20 | #define FWU_CODE_SECTION __attribute__ ((section(".firmwareUpdate"))) 21 | #else // Common Define for Visual Studio / WIN32 22 | #define FWU_CODE_SECTION 23 | #endif 24 | 25 | #include "common_define.h" 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | 32 | typedef struct StrFileHeadData { 33 | INT16U usType; 34 | INT16U usHwRev; 35 | INT16U usFwuRev; 36 | INT32U ulFlashStart; 37 | INT32U ulFlashEnd; 38 | INT32U ulFwuEntry; 39 | INT32U ulFlashCrc; 40 | } __attribute__((__packed__)) TFileHeadData; // sizeof = 22 41 | 42 | typedef struct StrFPGAHeadData { 43 | INT32U ulFPGALen; 44 | INT32U ulFPGACrc; 45 | } __attribute__((__packed__)) TFPGAHeadData; // sizeof = 8 46 | 47 | typedef struct StrFileHead { 48 | INT8U acSync[2]; 49 | INT32U ulLength; 50 | TFileHeadData dat; 51 | TFPGAHeadData fpga; 52 | } __attribute__((__packed__)) TFileHead; // sizeof = 36 53 | 54 | 55 | #define FWU_FILENAME_PATTERN "/*.kfu" 56 | #define FWU_FILENAME_PATTERN_0 '/' 57 | #define FWU_FILENAME_PATTERN_1 '*' 58 | #define FWU_FILENAME_PATTERN_2 '.' 59 | #define FWU_FILENAME_PATTERN_3 'k' 60 | #define FWU_FILENAME_PATTERN_4 'f' 61 | #define FWU_FILENAME_PATTERN_5 'u' 62 | #define FWU_FILENAME_PATTERN_6 '\0' 63 | #define FWU_FILENAME_MODE_0 'r' 64 | #define FWU_FILENAME_MODE_1 'b' 65 | #define FWU_FILENAME_MODE_2 '\0' 66 | 67 | extern void FWU_FS_main (void *file, TFileHead *header, INT8U write_fpga) FWU_CODE_SECTION; 68 | extern void FWU_BuR_main(INT32U i32uAppPrgStartAdd, INT32U i32uAppPrgSize) FWU_CODE_SECTION; 69 | extern TBOOL FWU_check_file(void *file, TFileHead *header) FWU_CODE_SECTION; 70 | extern void FWU_error (INT16U i16uErrorCode) FWU_CODE_SECTION; 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif 75 | 76 | #endif // FWUFLASHFILEMAIN_H_INC 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/json.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: LGPL-2.1-only 2 | * SPDX-FileCopyrightText: 2009 Vincent Hanquez 3 | * SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 4 | */ 5 | 6 | #ifndef JSON_H 7 | #define JSON_H 8 | 9 | #include 10 | 11 | #define JSON_MAJOR 0 12 | #define JSON_MINOR 7 13 | #define JSON_VERSION (JSON_MAJOR * 100 + JSON_MINOR) 14 | 15 | typedef enum 16 | { 17 | JSON_NONE, 18 | JSON_ARRAY_BEGIN, 19 | JSON_OBJECT_BEGIN, 20 | JSON_ARRAY_END, 21 | JSON_OBJECT_END, 22 | JSON_INT, 23 | JSON_FLOAT, 24 | JSON_STRING, 25 | JSON_KEY, 26 | JSON_TRUE, 27 | JSON_FALSE, 28 | JSON_NULL, 29 | } json_type; 30 | 31 | typedef enum 32 | { 33 | /* SUCCESS = 0 */ 34 | /* running out of memory */ 35 | JSON_ERROR_NO_MEMORY = 1, 36 | /* character < 32, except space newline tab */ 37 | JSON_ERROR_BAD_CHAR, 38 | /* trying to pop more object/array than pushed on the stack */ 39 | JSON_ERROR_POP_EMPTY, 40 | /* trying to pop wrong type of mode. popping array in object mode, vice versa */ 41 | JSON_ERROR_POP_UNEXPECTED_MODE, 42 | /* reach nesting limit on stack */ 43 | JSON_ERROR_NESTING_LIMIT, 44 | /* reach data limit on buffer */ 45 | JSON_ERROR_DATA_LIMIT, 46 | /* comment are not allowed with current configuration */ 47 | JSON_ERROR_COMMENT_NOT_ALLOWED, 48 | /* unexpected char in the current parser context */ 49 | JSON_ERROR_UNEXPECTED_CHAR, 50 | /* unicode low surrogate missing after high surrogate */ 51 | JSON_ERROR_UNICODE_MISSING_LOW_SURROGATE, 52 | /* unicode low surrogate missing without previous high surrogate */ 53 | JSON_ERROR_UNICODE_UNEXPECTED_LOW_SURROGATE, 54 | /* found a comma not in structure (array/object) */ 55 | JSON_ERROR_COMMA_OUT_OF_STRUCTURE, 56 | /* callback returns error */ 57 | JSON_ERROR_CALLBACK, 58 | } json_error; 59 | 60 | #define LIBJSON_DEFAULT_STACK_SIZE 256 61 | #define LIBJSON_DEFAULT_BUFFER_SIZE 4096 62 | 63 | typedef int (*json_parser_callback)(void *userdata, int type, const char *data, uint32_t length); 64 | 65 | typedef struct { 66 | uint32_t buffer_initial_size; 67 | uint32_t max_nesting; 68 | uint32_t max_data; 69 | int allow_c_comments; 70 | int allow_yaml_comments; 71 | void * (*user_calloc)(size_t nmemb, size_t size); 72 | void * (*user_realloc)(void *ptr, size_t size); 73 | } json_config; 74 | 75 | typedef struct json_parser { 76 | json_config config; 77 | 78 | /* SAJ callback */ 79 | json_parser_callback callback; 80 | void *userdata; 81 | 82 | /* parser state */ 83 | uint8_t state; 84 | uint8_t save_state; 85 | uint8_t expecting_key; 86 | uint16_t unicode_multi; 87 | json_type type; 88 | 89 | /* state stack */ 90 | uint8_t *stack; 91 | uint32_t stack_offset; 92 | uint32_t stack_size; 93 | 94 | /* parse buffer */ 95 | char *buffer; 96 | uint32_t buffer_size; 97 | uint32_t buffer_offset; 98 | } json_parser; 99 | 100 | 101 | /** json_parser_init initialize a parser structure taking a config, 102 | * a config and its userdata. 103 | * return JSON_ERROR_NO_MEMORY if memory allocation failed or SUCCESS. */ 104 | int json_parser_init(json_parser *parser, json_config *cfg, 105 | json_parser_callback callback, void *userdata); 106 | 107 | /** json_parser_free freed memory structure allocated by the parser */ 108 | int json_parser_free(json_parser *parser); 109 | 110 | /** json_parser_string append a string s with a specific length to the parser 111 | * return 0 if everything went ok, a JSON_ERROR_* otherwise. 112 | * the user can supplied a valid processed pointer that will 113 | * be fill with the number of processed characters before returning */ 114 | int json_parser_string(json_parser *parser, const char *string, 115 | uint32_t length, uint32_t *processed); 116 | 117 | /** json_parser_char append one single char to the parser 118 | * return 0 if everything went ok, a JSON_ERROR_* otherwise */ 119 | int json_parser_char(json_parser *parser, unsigned char next_char); 120 | 121 | /** json_parser_is_done return 0 is the parser isn't in a finish state. !0 if it is */ 122 | int json_parser_is_done(json_parser *parser); 123 | 124 | 125 | /** callback from the parser_dom callback to create object and array */ 126 | typedef void * (*json_parser_dom_create_structure)(int); 127 | 128 | /** callback from the parser_dom callback to create data values */ 129 | typedef void * (*json_parser_dom_create_data)(int, const char *, uint32_t); 130 | 131 | /** callback from the parser helper callback to append a value to an object or array value 132 | * append(parent, key, key_length, val); */ 133 | typedef int (*json_parser_dom_append)(void *, char *, uint32_t, void *); 134 | 135 | /** the json_parser_dom permits to create a DOM like tree easily through the 136 | * use of 3 callbacks where the user can choose the representation of the JSON values */ 137 | typedef struct json_parser_dom 138 | { 139 | /* object stack */ 140 | struct stack_elem { void *val; char *key; uint32_t key_length; } *stack; 141 | uint32_t stack_size; 142 | uint32_t stack_offset; 143 | 144 | /* overridable memory allocator */ 145 | void * (*user_calloc)(size_t nmemb, size_t size); 146 | void * (*user_realloc)(void *ptr, size_t size); 147 | 148 | /* returned root structure (object or array) */ 149 | void *root_structure; 150 | 151 | /* callbacks */ 152 | json_parser_dom_create_structure create_structure; 153 | json_parser_dom_create_data create_data; 154 | json_parser_dom_append append; 155 | } json_parser_dom; 156 | 157 | /** initialize a parser dom structure with the necessary callbacks */ 158 | int json_parser_dom_init(json_parser_dom *helper, 159 | json_parser_dom_create_structure create_structure, 160 | json_parser_dom_create_data create_data, 161 | json_parser_dom_append append); 162 | /** free memory allocated by the DOM callback helper */ 163 | int json_parser_dom_free(json_parser_dom *ctx); 164 | 165 | /** helper to parser callback that arrange parsing events into comprehensive JSON data structure */ 166 | int json_parser_dom_callback(void *userdata, int type, const char *data, uint32_t length); 167 | 168 | #endif /* JSON_H */ 169 | -------------------------------------------------------------------------------- /src/kbUtilities.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | 4 | #include 5 | 6 | #include "bsp/systick/systick.h" 7 | #include "common_define.h" 8 | #include "kbUtilities.h" 9 | 10 | //************************************************************************************************* 11 | //| Function: kbUT_getCurrentMs 12 | //| 13 | //! reads out the current value of the tick counter (1ms) 14 | //! 15 | //! detailed 16 | //! 17 | //! 18 | //! ingroup. Util 19 | //------------------------------------------------------------------------------------------------- 20 | INT32U kbUT_getCurrentMs ( 21 | void) //! \return tick count 22 | 23 | { 24 | INT32U i32uRv_l; 25 | 26 | i32uRv_l = kbGetTickCount (); 27 | 28 | return (i32uRv_l); 29 | } 30 | 31 | //************************************************************************************************* 32 | //| Function: kbUT_TimerInit 33 | //| 34 | //! initializes a timer variable 35 | //! 36 | //! 37 | //! ingroup. Util 38 | //------------------------------------------------------------------------------------------------- 39 | void kbUT_TimerInit ( 40 | kbUT_Timer *ptTimer_p) //!< [inout] pointer to timer struct 41 | 42 | { 43 | 44 | memset (ptTimer_p, 0, sizeof (kbUT_Timer)); 45 | } 46 | 47 | //************************************************************************************************* 48 | //| Function: kbUT_TimerStart 49 | //| 50 | //! starts a timer. 51 | //! 52 | //! 53 | //! ingroup. Util 54 | //------------------------------------------------------------------------------------------------- 55 | void kbUT_TimerStart ( 56 | kbUT_Timer *ptTimer_p, //!< [inout] pointer to timer struct 57 | INT32U i32uDuration_p) //!< [in] duration of timer to run (ms) 58 | 59 | { 60 | 61 | ptTimer_p->i32uStartTime = kbUT_getCurrentMs (); 62 | ptTimer_p->i32uDuration = i32uDuration_p; 63 | ptTimer_p->bExpired = bFALSE; 64 | ptTimer_p->bRun = bTRUE; 65 | } 66 | 67 | //************************************************************************************************* 68 | //| Function: kbUT_TimerRunning 69 | //| 70 | //! checks if a timer is actually running 71 | //! 72 | //! 73 | //! ingroup. Util 74 | //------------------------------------------------------------------------------------------------- 75 | TBOOL kbUT_TimerRunning ( 76 | kbUT_Timer *ptTimer_p) //!< [in] pointer to timer struct 77 | //! \return = bTRUE: timer is actual running, = bFALSE: timer is 78 | //! either not started or expired. 79 | 80 | { 81 | INT32U i32uTimeDiff_l; 82 | 83 | if (ptTimer_p->bRun == bTRUE) 84 | { 85 | i32uTimeDiff_l = (kbUT_getCurrentMs () - ptTimer_p->i32uStartTime); 86 | if (i32uTimeDiff_l >= ptTimer_p->i32uDuration) 87 | { // Timer expired 88 | ptTimer_p->bExpired = bTRUE; 89 | ptTimer_p->bRun = bFALSE; 90 | } 91 | } 92 | 93 | return (ptTimer_p->bRun); 94 | } 95 | 96 | //************************************************************************************************* 97 | //| Function: kbUT_TimerExpired 98 | //| 99 | //! checks if a timer is expired. 100 | //! 101 | //! Tests if the Timer just expires. Return value is true only for one call. Can be used to 102 | //! check the timer cyclically, but do an action only once. 103 | //! 104 | //! ingroup. Util 105 | //------------------------------------------------------------------------------------------------- 106 | TBOOL kbUT_TimerExpired ( 107 | kbUT_Timer *ptTimer_p) 108 | 109 | { 110 | INT32U i32uTimeDiff_l; 111 | TBOOL bRv_l = bFALSE; 112 | 113 | if (ptTimer_p->bRun == bTRUE) 114 | { 115 | i32uTimeDiff_l = (kbUT_getCurrentMs () - ptTimer_p->i32uStartTime); 116 | if (i32uTimeDiff_l >= ptTimer_p->i32uDuration) 117 | { // Timer expired 118 | ptTimer_p->bExpired = bTRUE; 119 | ptTimer_p->bRun = bFALSE; 120 | bRv_l = bTRUE; 121 | } 122 | } 123 | 124 | bRv_l = ptTimer_p->bExpired; 125 | 126 | ptTimer_p->bExpired = bFALSE; // Reset Flag, so that it is only once TRUE 127 | 128 | return (bRv_l); 129 | 130 | } 131 | 132 | //************************************************************************************************* 133 | //| Function: kbUT_TimeElapsed 134 | //| 135 | //! calculates the time since the timer was started 136 | //! 137 | //! if a timer is not started or it is expired, zero is given back 138 | //! 139 | //! 140 | //! ingroup. Util 141 | //------------------------------------------------------------------------------------------------- 142 | INT32U kbUT_TimeElapsed ( 143 | kbUT_Timer *ptTimer_p) //!< [in] pointer to timer 144 | //! \return time elapsed since timer started in ms 145 | 146 | { 147 | 148 | INT32U i32uTimeDiff_l = 0; 149 | 150 | if (ptTimer_p->bRun == bTRUE) 151 | { 152 | i32uTimeDiff_l = (kbUT_getCurrentMs () - ptTimer_p->i32uStartTime); 153 | } 154 | 155 | return (i32uTimeDiff_l); 156 | } 157 | 158 | //************************************************************************************************* 159 | //| Function: kbUT_TimerInUse 160 | //| 161 | //! checks if a timer is actually running and the expired flag has not been reset. 162 | //! if false is returned, the timer can be started again. 163 | //! 164 | //! ingroup. Util 165 | //------------------------------------------------------------------------------------------------- 166 | TBOOL kbUT_TimerInUse ( 167 | kbUT_Timer *ptTimer_p) //!< [in] pointer to timer struct 168 | //! \return = bTRUE: timer is actual running, = bFALSE: timer is 169 | //! either not started or expired and can be restarted now 170 | 171 | { 172 | return (ptTimer_p->bRun || ptTimer_p->bExpired); 173 | } 174 | 175 | //************************************************************************************************* 176 | //| Function: kbUT_crc32 177 | //| 178 | //! calculates a 32Bit CRC over a data block 179 | //! 180 | //! The Polynom is the Ethernet Polynom 0xEDB88320 181 | //! 182 | //! 183 | //! ingroup. Util 184 | //------------------------------------------------------------------------------------------------- 185 | void kbUT_crc32 ( 186 | INT8U *pi8uData_p, //!< [in] pointer to data 187 | INT16U i16uCnt_p, //!< [in] number of bytes 188 | INT32U *pi32uCrc_p) //!< [inout] CRC sum and inital value 189 | 190 | { 191 | 192 | INT16U i; 193 | INT16U j; 194 | INT32U i32uCrc_l = *pi32uCrc_p; 195 | #define CRC32_POLY 0xEDB88320 196 | 197 | for (i = 0; i < i16uCnt_p; i++) 198 | { 199 | i32uCrc_l ^= (INT32U)pi8uData_p[i]; 200 | for (j = 0; j < 8; j++) 201 | { 202 | if (i32uCrc_l & 0x00000001) 203 | { 204 | i32uCrc_l = (i32uCrc_l >> 1) ^ CRC32_POLY; 205 | } 206 | else 207 | { 208 | i32uCrc_l >>= 1; 209 | } 210 | } 211 | } 212 | 213 | *pi32uCrc_p = i32uCrc_l; 214 | } 215 | 216 | //************************************************************************************************* 217 | //| Function: kbUT_uitoa 218 | //| 219 | //! Converts an unsigned integer into a string. 220 | //! Returns true in case of success. 221 | //! 222 | //! ingroup. Util 223 | //------------------------------------------------------------------------------------------------- 224 | TBOOL kbUT_uitoa(INT32U p_value, INT8U* p_string, INT8U p_radix) 225 | { 226 | INT8U* stringPtr = p_string; 227 | INT8U* stringPtr1 = p_string; 228 | INT8U tmp_char; 229 | INT32U oldValue; 230 | 231 | // Check base 232 | if (p_radix != 2 && p_radix != 10 && p_radix != 16) 233 | { 234 | // Error, invalid base 235 | return bFALSE; 236 | } 237 | 238 | do 239 | { 240 | oldValue = p_value; 241 | p_value /= p_radix; 242 | *stringPtr++ = "0123456789abcdef" [oldValue - p_value * p_radix]; 243 | } while ( p_value ); 244 | 245 | *stringPtr-- = '\0'; 246 | 247 | // Reverse string 248 | while(stringPtr1 < stringPtr) 249 | { 250 | // Swap chars 251 | tmp_char = *stringPtr; 252 | *stringPtr-- = *stringPtr1; 253 | *stringPtr1++ = tmp_char; 254 | } 255 | 256 | return bTRUE; 257 | } 258 | 259 | //************************************************************************************************* 260 | //| Function: kbUT_atoi 261 | //| 262 | //! Converts a string into an integer. 263 | //! If the string starts with '0x', it is interpreted an hexadecimal number. 264 | //! The function is able to read negative numbers, they are returned as two's complement. 265 | //! 266 | //! 267 | //! ingroup. Util 268 | //------------------------------------------------------------------------------------------------- 269 | unsigned long kbUT_atoi(const char *start, int *success) 270 | { 271 | unsigned long val = 0; /* value we're accumulating */ 272 | unsigned long base = 10; 273 | unsigned long max; 274 | int neg=0; /* set to true if we see a minus sign */ 275 | const char *s = start; 276 | 277 | *success = 1; 278 | 279 | /* skip whitespace */ 280 | while (*s==' ' || *s=='\t') { 281 | s++; 282 | } 283 | 284 | /* check for sign */ 285 | if (*s=='-') { 286 | neg=1; 287 | s++; 288 | } else if (*s=='0') { 289 | s++; 290 | if (*s=='x' || *s=='X') { 291 | base = 16; 292 | s++; 293 | } 294 | } else if (*s=='+') { 295 | s++; 296 | } 297 | 298 | max = 0xffffffff / base; 299 | 300 | /* process each digit */ 301 | while (*s) { 302 | unsigned digit = 0; 303 | if (*s >= '0' && *s <= '9') 304 | { 305 | digit = *s - '0'; 306 | } 307 | else if (base == 16 && *s >= 'a' && *s <= 'f') 308 | { 309 | digit = *s - 'a' + 10; 310 | } 311 | else if (base == 16 && *s >= 'A' && *s <= 'F') 312 | { 313 | digit = *s - 'A' + 10; 314 | } 315 | else 316 | { 317 | // unknown character -> stop conversion 318 | break; 319 | } 320 | if (val > max) 321 | *success = 0; // overflow 322 | 323 | /* shift the number over and add in the new digit */ 324 | val = val*base + digit; 325 | 326 | /* look at the next character */ 327 | s++; 328 | } 329 | 330 | if (s == start) 331 | { 332 | // not a single character has been converted, this is not successful 333 | *success = 0; 334 | return val; 335 | } 336 | 337 | /* handle negative numbers */ 338 | if (neg) { 339 | val = ~(val-1); 340 | return val; 341 | } 342 | 343 | /* done */ 344 | return val; 345 | } 346 | 347 | //************************************************************************************************* 348 | //| Function: kbUT_itoa 349 | //| 350 | //! Converts an signed or unsigned integer into a string. 351 | //! If radix is less than 0, val is interpreted as signed value, 352 | //! otherwise, val is interpreted as unsigned value, 353 | //! 354 | //! Returns a pointer to a static buffer. The content of the buffer is only valid 355 | //! until the next call of the function. 356 | //! ingroup. Util 357 | //------------------------------------------------------------------------------------------------- 358 | char *kbUT_itoa(INT32U val, INT16S radix, INT16U len) 359 | { 360 | static char buffer[13]; 361 | unsigned int i = 0, j; 362 | char sign = 0; 363 | 364 | if (radix < 0) 365 | { 366 | radix = -radix; 367 | if ((INT32S)val < 0) 368 | { 369 | val = ~(val-1); 370 | sign = 1; 371 | } 372 | } 373 | if (radix > 16 || radix <= 7) 374 | { 375 | return 0; 376 | } 377 | 378 | while (val > 0) 379 | { 380 | j = val % radix; 381 | val = val / radix; 382 | if (j < 10) 383 | buffer[i++] = (char) ('0' + j); 384 | else 385 | buffer[i++] = (char) ('a' + j - 10); 386 | } 387 | if (i == 0) 388 | buffer[i++] = '0'; 389 | if (sign) 390 | buffer[i++] = '-'; 391 | 392 | while (i= OUTER_LOW)) 39 | 40 | // macro to check if a range is completely within another range ... 41 | #define CHECK_INSIDE(OUTER_LOW,OUTER_HIGH,INNER_LOW,INNER_HIGH)\ 42 | ((INNER_HIGH <= OUTER_HIGH) && (INNER_LOW >= OUTER_LOW )) 43 | 44 | // macro for a comlete range check for two ranges ... 45 | #define CHECK_OVERLAPPING(OUTER_LOW,OUTER_HIGH,INNER_LOW,INNER_HIGH) \ 46 | CHECK_INSIDE( OUTER_LOW, OUTER_HIGH, INNER_LOW, INNER_HIGH) || \ 47 | CHECK_LAPPING( OUTER_LOW, OUTER_HIGH, INNER_LOW ) || \ 48 | CHECK_LAPPING( OUTER_LOW, OUTER_HIGH, INNER_HIGH ) || \ 49 | CHECK_INSIDE( INNER_LOW, INNER_HIGH, OUTER_LOW, OUTER_HIGH) || \ 50 | CHECK_LAPPING( INNER_LOW, INNER_HIGH, OUTER_LOW ) || \ 51 | CHECK_LAPPING( INNER_LOW, INNER_HIGH, OUTER_HIGH ) 52 | 53 | 54 | //+============================================================================================= 55 | //| Prototypen / prototypes 56 | //+============================================================================================= 57 | #ifdef __cplusplus 58 | extern "C" { 59 | #endif 60 | 61 | 62 | extern void kbUT_TimerInit (kbUT_Timer *ptTimer_p); 63 | extern void kbUT_TimerStart (kbUT_Timer *ptTimer_p, INT32U i32uDuration_p); 64 | extern TBOOL kbUT_TimerRunning (kbUT_Timer *ptTimer_p); 65 | extern TBOOL kbUT_TimerExpired (kbUT_Timer *ptTimer_p); 66 | extern INT32U kbUT_TimeElapsed (kbUT_Timer *ptTimer_p); 67 | extern TBOOL kbUT_TimerInUse (kbUT_Timer *ptTimer_p); 68 | extern INT32U kbUT_getCurrentMs (void); 69 | extern void kbUT_crc32 (INT8U *pi8uData_p, INT16U i16uCnt_p, INT32U *pi32uCrc_p); 70 | extern void kbUT_crc8XX(INT8U *pi8uData_p, INT16U i16uCnt_p, INT8U i8uPolynom_p, INT8U *pi8uCrc_p); 71 | extern TBOOL kbUT_uitoa(INT32U p_value, INT8U* p_string, INT8U p_radix); 72 | extern unsigned long kbUT_atoi(const char *s, int *success); 73 | extern char * kbUT_itoa(INT32U val, INT16S radix, INT16U len); 74 | 75 | 76 | #ifdef __cplusplus 77 | } 78 | #endif 79 | 80 | //#else 81 | //#error "KBUTILITIES_H_INC already defined !" 82 | #endif // KBUTILITIES_H_INC 83 | 84 | -------------------------------------------------------------------------------- /src/piAIOComm.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | 4 | #include 5 | #include 6 | 7 | #include "piAIOComm.h" 8 | #include "piControlMain.h" 9 | #include "revpi_common.h" 10 | #include "RevPiDevice.h" 11 | 12 | #define AIO_MAX_DEVS 10 13 | #define AIO_OUTPUT_DATA_LEN sizeof(SAioRequest) 14 | #define AIO_INPUT_DATA_LEN sizeof(SAioResponse) 15 | #define AIO_CONFIG_DATA2_LEN sizeof(SAioInConfig) 16 | #define AIO_CONFIG_DATA3_LEN sizeof(SAioInConfig) 17 | #define AIO_CONFIG_DATA1_LEN sizeof(SAioConfig) 18 | 19 | static unsigned int num_aios = 0; 20 | static SAioConfig aioConfig_s[AIO_MAX_DEVS]; 21 | static SAioInConfig aioIn1Config_s[AIO_MAX_DEVS]; 22 | static SAioInConfig aioIn2Config_s[AIO_MAX_DEVS]; 23 | 24 | static u8 aio_dev[AIO_MAX_DEVS]; 25 | 26 | void piAIOComm_InitStart(void) 27 | { 28 | pr_info_aio("piAIOComm_InitStart\n"); 29 | num_aios = 0; 30 | } 31 | 32 | u32 piAIOComm_Config(u8 addr, u16 num_entries, SEntryInfo * pEnt) 33 | { 34 | uint16_t i; 35 | 36 | if (num_aios >= AIO_MAX_DEVS) { 37 | pr_err("max. number of AIOs reached\n"); 38 | return -1; 39 | } 40 | 41 | pr_info_aio("piAIOComm_Config addr %d entries %d num %d\n", addr, num_entries, num_aios); 42 | 43 | aio_dev[num_aios] = addr; 44 | 45 | for (i = 0; i < num_entries; i++) { 46 | pr_info_aio("addr %2d type %d len %3d offset %3d value %d 0x%x\n", 47 | pEnt[i].i8uAddress, pEnt[i].i8uType, pEnt[i].i16uBitLength, pEnt[i].i16uOffset, 48 | pEnt[i].i32uDefault, pEnt[i].i32uDefault); 49 | 50 | switch (pEnt[i].i16uOffset) { 51 | case AIO_OFFSET_InputValue_1: 52 | case AIO_OFFSET_InputValue_2: 53 | case AIO_OFFSET_InputValue_3: 54 | case AIO_OFFSET_InputValue_4: 55 | case AIO_OFFSET_InputStatus_1: 56 | case AIO_OFFSET_InputStatus_2: 57 | case AIO_OFFSET_InputStatus_3: 58 | case AIO_OFFSET_InputStatus_4: 59 | case AIO_OFFSET_RTDValue_1: 60 | case AIO_OFFSET_RTDValue_2: 61 | case AIO_OFFSET_RTDStatus_1: 62 | case AIO_OFFSET_RTDStatus_2: 63 | case AIO_OFFSET_Output_Status_1: 64 | case AIO_OFFSET_Output_Status_2: 65 | case AIO_OFFSET_OutputValue_1: 66 | case AIO_OFFSET_OutputValue_2: 67 | // nothing to do 68 | break; 69 | case AIO_OFFSET_Input1Range: 70 | aioIn1Config_s[num_aios].sAioInputConfig[0].eInputRange = pEnt[i].i32uDefault; 71 | break; 72 | case AIO_OFFSET_Input1Factor: 73 | aioIn1Config_s[num_aios].sAioInputConfig[0].i16sA1 = pEnt[i].i32uDefault; 74 | break; 75 | case AIO_OFFSET_Input1Divisor: 76 | aioIn1Config_s[num_aios].sAioInputConfig[0].i16uA2 = pEnt[i].i32uDefault; 77 | break; 78 | case AIO_OFFSET_Input1Offset: 79 | aioIn1Config_s[num_aios].sAioInputConfig[0].i16sB = pEnt[i].i32uDefault; 80 | break; 81 | case AIO_OFFSET_Input2Range: 82 | aioIn1Config_s[num_aios].sAioInputConfig[1].eInputRange = pEnt[i].i32uDefault; 83 | break; 84 | case AIO_OFFSET_Input2Factor: 85 | aioIn1Config_s[num_aios].sAioInputConfig[1].i16sA1 = pEnt[i].i32uDefault; 86 | break; 87 | case AIO_OFFSET_Input2Divisor: 88 | aioIn1Config_s[num_aios].sAioInputConfig[1].i16uA2 = pEnt[i].i32uDefault; 89 | break; 90 | case AIO_OFFSET_Input2Offset: 91 | aioIn1Config_s[num_aios].sAioInputConfig[1].i16sB = pEnt[i].i32uDefault; 92 | break; 93 | case AIO_OFFSET_Input3Range: 94 | aioIn2Config_s[num_aios].sAioInputConfig[0].eInputRange = pEnt[i].i32uDefault; 95 | break; 96 | case AIO_OFFSET_Input3Factor: 97 | aioIn2Config_s[num_aios].sAioInputConfig[0].i16sA1 = pEnt[i].i32uDefault; 98 | break; 99 | case AIO_OFFSET_Input3Divisor: 100 | aioIn2Config_s[num_aios].sAioInputConfig[0].i16uA2 = pEnt[i].i32uDefault; 101 | break; 102 | case AIO_OFFSET_Input3Offset: 103 | aioIn2Config_s[num_aios].sAioInputConfig[0].i16sB = pEnt[i].i32uDefault; 104 | break; 105 | case AIO_OFFSET_Input4Range: 106 | aioIn2Config_s[num_aios].sAioInputConfig[1].eInputRange = pEnt[i].i32uDefault; 107 | break; 108 | case AIO_OFFSET_Input4Factor: 109 | aioIn2Config_s[num_aios].sAioInputConfig[1].i16sA1 = pEnt[i].i32uDefault; 110 | break; 111 | case AIO_OFFSET_Input4Divisor: 112 | aioIn2Config_s[num_aios].sAioInputConfig[1].i16uA2 = pEnt[i].i32uDefault; 113 | break; 114 | case AIO_OFFSET_Input4Offset: 115 | aioIn2Config_s[num_aios].sAioInputConfig[1].i16sB = pEnt[i].i32uDefault; 116 | break; 117 | case AIO_OFFSET_InputSampleRate: 118 | aioConfig_s[num_aios].i8uInputSampleRate = pEnt[i].i32uDefault; 119 | break; 120 | case AIO_OFFSET_RTD1Type: 121 | aioConfig_s[num_aios].sAioRtdConfig[0].i8uSensorType = pEnt[i].i32uDefault; 122 | break; 123 | case AIO_OFFSET_RTD1Method: 124 | if (pEnt[i].i32uDefault == 1) 125 | aioConfig_s[num_aios].sAioRtdConfig[0].i8uMeasureMethod = 1; // 4 wire 126 | else 127 | aioConfig_s[num_aios].sAioRtdConfig[0].i8uMeasureMethod = 0; // 2 or 3 wire 128 | break; 129 | case AIO_OFFSET_RTD1Factor: 130 | aioConfig_s[num_aios].sAioRtdConfig[0].i16sA1 = pEnt[i].i32uDefault; 131 | break; 132 | case AIO_OFFSET_RTD1Divisor: 133 | aioConfig_s[num_aios].sAioRtdConfig[0].i16uA2 = pEnt[i].i32uDefault; 134 | break; 135 | case AIO_OFFSET_RTD1Offset: 136 | aioConfig_s[num_aios].sAioRtdConfig[0].i16sB = pEnt[i].i32uDefault; 137 | break; 138 | case AIO_OFFSET_RTD2Type: 139 | aioConfig_s[num_aios].sAioRtdConfig[1].i8uSensorType = pEnt[i].i32uDefault; 140 | break; 141 | case AIO_OFFSET_RTD2Method: 142 | if (pEnt[i].i32uDefault == 1) 143 | aioConfig_s[num_aios].sAioRtdConfig[1].i8uMeasureMethod = 1; // 4 wire 144 | else 145 | aioConfig_s[num_aios].sAioRtdConfig[1].i8uMeasureMethod = 0; // 2 or 3 wire 146 | break; 147 | case AIO_OFFSET_RTD2Factor: 148 | aioConfig_s[num_aios].sAioRtdConfig[1].i16sA1 = pEnt[i].i32uDefault; 149 | break; 150 | case AIO_OFFSET_RTD2Divisor: 151 | aioConfig_s[num_aios].sAioRtdConfig[1].i16uA2 = pEnt[i].i32uDefault; 152 | break; 153 | case AIO_OFFSET_RTD2Offset: 154 | aioConfig_s[num_aios].sAioRtdConfig[1].i16sB = pEnt[i].i32uDefault; 155 | break; 156 | case AIO_OFFSET_Output1Range: 157 | aioConfig_s[num_aios].sAioOutputConfig[0].eOutputRange = pEnt[i].i32uDefault; 158 | break; 159 | case AIO_OFFSET_Output1EnableSlew: 160 | aioConfig_s[num_aios].sAioOutputConfig[0].bSlewRateEnabled = pEnt[i].i32uDefault; 161 | break; 162 | case AIO_OFFSET_Output1SlewStepSize: 163 | aioConfig_s[num_aios].sAioOutputConfig[0].eSlewRateStepSize = pEnt[i].i32uDefault; 164 | break; 165 | case AIO_OFFSET_Output1SlewUpdateFreq: 166 | aioConfig_s[num_aios].sAioOutputConfig[0].eSlewRateFrequency = pEnt[i].i32uDefault; 167 | break; 168 | case AIO_OFFSET_Output1Factor: 169 | aioConfig_s[num_aios].sAioOutputConfig[0].i16sA1 = pEnt[i].i32uDefault; 170 | break; 171 | case AIO_OFFSET_Output1Divisor: 172 | aioConfig_s[num_aios].sAioOutputConfig[0].i16uA2 = pEnt[i].i32uDefault; 173 | break; 174 | case AIO_OFFSET_Output1Offset: 175 | aioConfig_s[num_aios].sAioOutputConfig[0].i16sB = pEnt[i].i32uDefault; 176 | break; 177 | case AIO_OFFSET_Output2Range: 178 | aioConfig_s[num_aios].sAioOutputConfig[1].eOutputRange = pEnt[i].i32uDefault; 179 | break; 180 | case AIO_OFFSET_Output2EnableSlew: 181 | aioConfig_s[num_aios].sAioOutputConfig[1].bSlewRateEnabled = pEnt[i].i32uDefault; 182 | break; 183 | case AIO_OFFSET_Output2SlewStepSize: 184 | aioConfig_s[num_aios].sAioOutputConfig[1].eSlewRateStepSize = pEnt[i].i32uDefault; 185 | break; 186 | case AIO_OFFSET_Output2SlewUpdateFreq: 187 | aioConfig_s[num_aios].sAioOutputConfig[1].eSlewRateFrequency = pEnt[i].i32uDefault; 188 | break; 189 | case AIO_OFFSET_Output2Factor: 190 | aioConfig_s[num_aios].sAioOutputConfig[1].i16sA1 = pEnt[i].i32uDefault; 191 | break; 192 | case AIO_OFFSET_Output2Divisor: 193 | aioConfig_s[num_aios].sAioOutputConfig[1].i16uA2 = pEnt[i].i32uDefault; 194 | break; 195 | case AIO_OFFSET_Output2Offset: 196 | aioConfig_s[num_aios].sAioOutputConfig[1].i16sB = pEnt[i].i32uDefault; 197 | break; 198 | default: 199 | pr_err("piAIOComm_Config: Unknown parameter %d in rsc-file\n", pEnt[i].i16uOffset); 200 | } 201 | } 202 | 203 | num_aios++; 204 | 205 | pr_info_aio("piAIOComm_Config done %d addr %d\n", num_aios, addr); 206 | 207 | return 0; 208 | } 209 | 210 | u32 piAIOComm_Init(u8 devnum) 211 | { 212 | void *snd_buf; 213 | int dev_idx; 214 | u8 addr; 215 | int ret; 216 | 217 | addr = RevPiDevice_getDev(devnum)->i8uAddress; 218 | 219 | pr_info_aio("piAIOComm_Init %d of %d addr %d\n", devnum, 220 | num_aios, addr); 221 | 222 | for (dev_idx = 0; dev_idx < num_aios; dev_idx++) { 223 | if (aio_dev[dev_idx] == addr) 224 | break; 225 | } 226 | 227 | if (dev_idx == num_aios) 228 | return 4; // unknown device 229 | 230 | snd_buf = &aioIn1Config_s[dev_idx]; 231 | 232 | pr_info_aio("piAIOComm_Init send configIn1\n"); 233 | ret = pibridge_req_io(addr, IOP_TYP1_CMD_DATA2, snd_buf, 234 | AIO_CONFIG_DATA2_LEN, NULL, 0); 235 | if (ret) 236 | return 3; 237 | 238 | snd_buf = &aioIn2Config_s[dev_idx]; 239 | 240 | pr_info_aio("piAIOComm_Init send configIn2\n"); 241 | ret = pibridge_req_io(addr, IOP_TYP1_CMD_DATA3, snd_buf, 242 | AIO_CONFIG_DATA3_LEN, NULL, 0); 243 | if (ret) 244 | return 3; 245 | 246 | snd_buf = &aioConfig_s[dev_idx]; 247 | 248 | pr_info_aio("piAIOComm_Init send config\n"); 249 | ret = pibridge_req_io(addr, IOP_TYP1_CMD_CFG, snd_buf, 250 | AIO_CONFIG_DATA1_LEN, NULL, 0); 251 | if (ret) 252 | return 3; 253 | 254 | pr_info_aio("piAIOComm_Init done config\n"); 255 | 256 | return 0; 257 | } 258 | 259 | u32 piAIOComm_sendCyclicTelegram(u8 devnum) 260 | { 261 | u8 snd_buf[AIO_OUTPUT_DATA_LEN]; 262 | u8 rcv_buf[AIO_INPUT_DATA_LEN]; 263 | SDevice *revpi_dev; 264 | u8 addr; 265 | int ret; 266 | 267 | revpi_dev = RevPiDevice_getDev(devnum); 268 | 269 | if (revpi_dev->sId.i16uFBS_OutputLength != AIO_OUTPUT_DATA_LEN) 270 | return 4; 271 | 272 | addr = revpi_dev->i8uAddress; 273 | 274 | if (!test_bit(PICONTROL_DEV_FLAG_STOP_IO, &piDev_g.flags)) { 275 | my_rt_mutex_lock(&piDev_g.lockPI); 276 | memcpy(snd_buf, piDev_g.ai8uPI + revpi_dev->i16uOutputOffset, 277 | AIO_OUTPUT_DATA_LEN); 278 | rt_mutex_unlock(&piDev_g.lockPI); 279 | } else { 280 | memset(snd_buf, 0, AIO_OUTPUT_DATA_LEN); 281 | } 282 | 283 | ret = pibridge_req_io(addr, IOP_TYP1_CMD_DATA, snd_buf, 284 | AIO_OUTPUT_DATA_LEN, rcv_buf, AIO_INPUT_DATA_LEN); 285 | if (ret != AIO_INPUT_DATA_LEN) { 286 | pr_debug("AIO addr %2d: communication failed (req:%zu,ret:%d)\n", 287 | addr, AIO_INPUT_DATA_LEN, ret); 288 | 289 | if (ret >= 0) 290 | ret = -EIO; 291 | 292 | return ret; 293 | } 294 | 295 | if (!test_bit(PICONTROL_DEV_FLAG_STOP_IO, &piDev_g.flags)) { 296 | my_rt_mutex_lock(&piDev_g.lockPI); 297 | memcpy(piDev_g.ai8uPI + revpi_dev->i16uInputOffset, rcv_buf, 298 | AIO_INPUT_DATA_LEN); 299 | rt_mutex_unlock(&piDev_g.lockPI); 300 | } 301 | 302 | return 0; 303 | } 304 | -------------------------------------------------------------------------------- /src/piAIOComm.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2017-2024 KUNBUS GmbH 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "common_define.h" 10 | #include "picontrol_intern.h" 11 | 12 | #define AIO_OFFSET_InputValue_1 0 //INT 13 | #define AIO_OFFSET_InputValue_2 2 //INT 14 | #define AIO_OFFSET_InputValue_3 4 //INT 15 | #define AIO_OFFSET_InputValue_4 6 //INT 16 | #define AIO_OFFSET_InputStatus_1 8 //BYTE 17 | #define AIO_OFFSET_InputStatus_2 9 //BYTE 18 | #define AIO_OFFSET_InputStatus_3 10 //BYTE 19 | #define AIO_OFFSET_InputStatus_4 11 //BYTE 20 | #define AIO_OFFSET_RTDValue_1 12 //INT 21 | #define AIO_OFFSET_RTDValue_2 14 //INT 22 | #define AIO_OFFSET_RTDStatus_1 16 //BYTE 23 | #define AIO_OFFSET_RTDStatus_2 17 //BYTE 24 | #define AIO_OFFSET_Output_Status_1 18 //BYTE 25 | #define AIO_OFFSET_Output_Status_2 19 //BYTE 26 | #define AIO_OFFSET_OutputValue_1 20 //INT 27 | #define AIO_OFFSET_OutputValue_2 22 //INT 28 | #define AIO_OFFSET_Input1Range 24 // ##ATTR_COMMENT## 29 | #define AIO_OFFSET_Input1Factor 25 // ##ATTR_COMMENT## 30 | #define AIO_OFFSET_Input1Divisor 27 // ##ATTR_COMMENT## 31 | #define AIO_OFFSET_Input1Offset 29 // ##ATTR_COMMENT## 32 | #define AIO_OFFSET_Input2Range 31 // ##ATTR_COMMENT## 33 | #define AIO_OFFSET_Input2Factor 32 // ##ATTR_COMMENT## 34 | #define AIO_OFFSET_Input2Divisor 34 // ##ATTR_COMMENT## 35 | #define AIO_OFFSET_Input2Offset 36 // ##ATTR_COMMENT## 36 | #define AIO_OFFSET_Input3Range 38 // ##ATTR_COMMENT## 37 | #define AIO_OFFSET_Input3Factor 39 // ##ATTR_COMMENT## 38 | #define AIO_OFFSET_Input3Divisor 41 // ##ATTR_COMMENT## 39 | #define AIO_OFFSET_Input3Offset 43 // ##ATTR_COMMENT## 40 | #define AIO_OFFSET_Input4Range 45 // ##ATTR_COMMENT## 41 | #define AIO_OFFSET_Input4Factor 46 // ##ATTR_COMMENT## 42 | #define AIO_OFFSET_Input4Divisor 48 // ##ATTR_COMMENT## 43 | #define AIO_OFFSET_Input4Offset 50 // ##ATTR_COMMENT## 44 | #define AIO_OFFSET_InputSampleRate 52 // ##ATTR_COMMENT## 45 | #define AIO_OFFSET_RTD1Type 53 // ##ATTR_COMMENT## 46 | #define AIO_OFFSET_RTD1Method 54 // ##ATTR_COMMENT## 47 | #define AIO_OFFSET_RTD1Factor 55 // ##ATTR_COMMENT## 48 | #define AIO_OFFSET_RTD1Divisor 57 // ##ATTR_COMMENT## 49 | #define AIO_OFFSET_RTD1Offset 59 // ##ATTR_COMMENT## 50 | #define AIO_OFFSET_RTD2Type 61 // ##ATTR_COMMENT## 51 | #define AIO_OFFSET_RTD2Method 62 // ##ATTR_COMMENT## 52 | #define AIO_OFFSET_RTD2Factor 63 // ##ATTR_COMMENT## 53 | #define AIO_OFFSET_RTD2Divisor 65 // ##ATTR_COMMENT## 54 | #define AIO_OFFSET_RTD2Offset 67 // ##ATTR_COMMENT## 55 | #define AIO_OFFSET_Output1Range 69 // ##ATTR_COMMENT## 56 | #define AIO_OFFSET_Output1EnableSlew 70 //Enable Slew Rate Control ##ATTR_COMMENT## 57 | #define AIO_OFFSET_Output1SlewStepSize 71 //Slew rate step size ##ATTR_COMMENT## 58 | #define AIO_OFFSET_Output1SlewUpdateFreq 72 //Slew Rate Update Clock ##ATTR_COMMENT## 59 | #define AIO_OFFSET_Output1Factor 73 // ##ATTR_COMMENT## 60 | #define AIO_OFFSET_Output1Divisor 75 // ##ATTR_COMMENT## 61 | #define AIO_OFFSET_Output1Offset 77 // ##ATTR_COMMENT## 62 | #define AIO_OFFSET_Output2Range 79 // ##ATTR_COMMENT## 63 | #define AIO_OFFSET_Output2EnableSlew 80 //Enable Slew Rate Control ##ATTR_COMMENT## 64 | #define AIO_OFFSET_Output2SlewStepSize 81 //Slew rate step size ##ATTR_COMMENT## 65 | #define AIO_OFFSET_Output2SlewUpdateFreq 82 //Slew Rate Update Clock ##ATTR_COMMENT## 66 | #define AIO_OFFSET_Output2Factor 83 // ##ATTR_COMMENT## 67 | #define AIO_OFFSET_Output2Divisor 85 // ##ATTR_COMMENT## 68 | #define AIO_OFFSET_Output2Offset 87 // ##ATTR_COMMENT## 69 | 70 | typedef enum 71 | { 72 | AIOSTATE_OFFLINE = 0x00, // Physikalisch nicht verbunden 73 | AIOSTATE_CYCLIC_IO = 0x01, // Zyklischer Datenaustausch ist aktiv 74 | } AioCommStatus; 75 | 76 | 77 | void piAIOComm_InitStart(void); 78 | 79 | u32 piAIOComm_Config(u8 addr, u16 num_entries, SEntryInfo * pEnt); 80 | 81 | u32 piAIOComm_Init(u8 devnum); 82 | 83 | u32 piAIOComm_sendCyclicTelegram(u8 devnum); 84 | -------------------------------------------------------------------------------- /src/piConfig.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2016-2024 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef PRODUCTS_PIKERNELMOD_PICONFIG_H_ 6 | #define PRODUCTS_PIKERNELMOD_PICONFIG_H_ 7 | 8 | #include 9 | 10 | #include "json.h" 11 | #include "picontrol_intern.h" 12 | 13 | typedef struct _piEntries { 14 | uint16_t i16uNumEntries; 15 | SEntryInfo ent[0]; 16 | } piEntries; 17 | 18 | typedef struct _piDevices { 19 | uint16_t i16uNumDevices; 20 | SDeviceInfo dev[0]; 21 | } piDevices; 22 | 23 | typedef struct _piCopyEntry { 24 | uint16_t i16uAddr; 25 | uint16_t i16uLength; 26 | uint8_t i8uBitMask; // bitmask for bits to copy 27 | } piCopyEntry; 28 | 29 | typedef struct _piCopylist { 30 | uint16_t i16uNumEntries; 31 | piCopyEntry ent[0]; 32 | } piCopylist; 33 | 34 | typedef struct _piConnection { 35 | uint16_t i16uSrcAddr; 36 | uint16_t i16uDestAddr; 37 | uint8_t i8uLength; // in bit: 1-7, 8, 16, 32 38 | uint8_t i8uSrcBit; // used only, if i8uLength < 8 39 | uint8_t i8uDestBit; // used only, if i8uLength < 8 40 | } piConnection; 41 | 42 | typedef struct _piConnectionlist { 43 | uint16_t i16uNumEntries; 44 | piConnection conn[0]; 45 | } piConnectionList; 46 | 47 | int piConfigParse(const char *filename, piDevices ** devs, piEntries ** ent, piCopylist ** cl, 48 | piConnectionList ** conn); 49 | 50 | struct file *open_filename(const char *filename, int flags); 51 | void close_filename(struct file *file); 52 | void revpi_set_defaults(unsigned char *mem, piEntries *entries); 53 | int process_file(json_parser * parser, struct file *input, int *retlines, int *retcols); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/piControl.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 | * SPDX-FileCopyrightText: 2016-2025 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef PICONTROL_H_ 6 | #define PICONTROL_H_ 7 | 8 | #include 9 | 10 | #define PICONTROL_DEVICE "/dev/piControl0" 11 | /* max. length of error message */ 12 | #define REV_PI_ERROR_MSG_LEN 256 13 | /* max. number of */ 14 | #define REV_PI_DEV_CNT_MAX 64 15 | 16 | /* 17 | * Module Id 18 | * 0x0001 - 0x3000 KUNBUS Modules (e.g. DIO, Gateways, ...) 19 | * 0x3001 - 0x7000 not used 20 | * 0x6001 - 0x7000 KUNBUS Software Adapters 21 | * 0x7001 - 0x8000 User defined Software Adapters 22 | * 0x8001 - 0xb000 KUNBUS Modules configured but not connected 23 | * 0xb001 - 0xffff not used 24 | */ 25 | #define PICONTROL_SW_OFFSET 0x6001 26 | #define PICONTROL_SW_MODBUS_TCP_SLAVE 0x6001 27 | #define PICONTROL_SW_MODBUS_RTU_SLAVE 0x6002 28 | #define PICONTROL_SW_MODBUS_TCP_MASTER 0x6003 29 | #define PICONTROL_SW_MODBUS_RTU_MASTER 0x6004 30 | #define PICONTROL_SW_PROFINET_CONTROLLER 0x6005 31 | #define PICONTROL_SW_PROFINET_DEVICE 0x6006 32 | #define PICONTROL_SW_REVPI_SEVEN 0x6007 33 | #define PICONTROL_SW_REVPI_CLOUD 0x6008 34 | #define PICONTROL_SW_OPCUA_REVPI_SERVER 0x6009 35 | #define PICONTROL_SW_MQTT_REVPI_CLIENT 0x600a 36 | 37 | #define PICONTROL_NOT_CONNECTED 0x8000 38 | #define PICONTROL_NOT_CONNECTED_MASK 0x7fff 39 | 40 | struct picontrol_firmware_upload { 41 | __u32 addr; 42 | #define PICONTROL_FIRMWARE_FORCE_UPLOAD 0x0001 43 | __u32 flags; 44 | /* Memory is cheap, so reserve a few bytes for future extensions */ 45 | __u32 padding[4]; 46 | }; 47 | 48 | typedef struct SDeviceInfoStr { 49 | /* Address of module in current configuration */ 50 | __u8 i8uAddress; 51 | __u8 pad[3]; 52 | /* serial number of module */ 53 | __u32 i32uSerialnumber; 54 | /* Type identifier of module */ 55 | __u16 i16uModuleType; 56 | /* hardware revision */ 57 | __u16 i16uHW_Revision; 58 | /* major software version */ 59 | __u16 i16uSW_Major; 60 | /* minor software version */ 61 | __u16 i16uSW_Minor; 62 | /* svn revision of software */ 63 | __u32 i32uSVN_Revision; 64 | /* length in bytes of all input values together */ 65 | __u16 i16uInputLength; 66 | /* length in bytes of all output values together */ 67 | __u16 i16uOutputLength; 68 | /* length in bytes of all config values together */ 69 | __u16 i16uConfigLength; 70 | /* offset in process image */ 71 | __u16 i16uBaseOffset; 72 | /* offset in process image of first input byte */ 73 | __u16 i16uInputOffset; 74 | /* offset in process image of first output byte */ 75 | __u16 i16uOutputOffset; 76 | /* offset in process image of first config byte */ 77 | __u16 i16uConfigOffset; 78 | /* index of entry */ 79 | __u16 i16uFirstEntry; 80 | /* number of entries in process image */ 81 | __u16 i16uEntries; 82 | /* fieldbus state of piGate Module */ 83 | __u8 i8uModuleState; 84 | /* 0 means that the module is not present and no data is available */ 85 | __u8 i8uActive; 86 | /* space for future extensions */ 87 | __u8 i8uReserve[30]; 88 | } SDeviceInfo; 89 | 90 | typedef struct SPIValueStr { 91 | /* Address of the byte in the process image */ 92 | __u16 i16uAddress; 93 | /* 0-7 bit position, >= 8 whole byte */ 94 | __u8 i8uBit; 95 | /* Value: 0/1 for bit access, whole byte otherwise */ 96 | __u8 i8uValue; 97 | } SPIValue; 98 | 99 | typedef struct SPIVariableStr { 100 | /* Variable name */ 101 | char strVarName[32]; 102 | /* Address of the byte in the process image */ 103 | __u16 i16uAddress; 104 | /* 0-7 bit position, >= 8 whole byte */ 105 | __u8 i8uBit; 106 | __u8 pad; 107 | /* length in bits, possible values are 1, 8, 16 and 32 */ 108 | __u16 i16uLength; 109 | } SPIVariable; 110 | 111 | #define KB_IOC_MAGIC 'K' 112 | /* reset the piControl driver including the config file */ 113 | #define KB_RESET _IO(KB_IOC_MAGIC, 12 ) 114 | /* get the device info of all detected devices */ 115 | #define KB_GET_DEVICE_INFO_LIST _IO(KB_IOC_MAGIC, 13 ) 116 | /* get the device info of one device */ 117 | #define KB_GET_DEVICE_INFO _IO(KB_IOC_MAGIC, 14 ) 118 | /* get the value of one bit in the process image */ 119 | #define KB_GET_VALUE _IO(KB_IOC_MAGIC, 15 ) 120 | /* set the value of one bit in the process image */ 121 | #define KB_SET_VALUE _IO(KB_IOC_MAGIC, 16 ) 122 | /* find a varible defined in PiCtory */ 123 | #define KB_FIND_VARIABLE _IO(KB_IOC_MAGIC, 17 ) 124 | /* copy exported outputs from application to the process image */ 125 | #define KB_SET_EXPORTED_OUTPUTS _IO(KB_IOC_MAGIC, 18 ) 126 | /* Deprecated. Use PICONTROL_UPLOAD_FIRMWARE instead. */ 127 | #define KB_UPDATE_DEVICE_FIRMWARE _IO(KB_IOC_MAGIC, 19 ) 128 | /* set a counter or endocder to 0 */ 129 | #define KB_DIO_RESET_COUNTER _IO(KB_IOC_MAGIC, 20 ) 130 | /* copy the last error message */ 131 | #define KB_GET_LAST_MESSAGE _IO(KB_IOC_MAGIC, 21 ) 132 | /* stop/start IO communication, can be used for I/O simulation */ 133 | #define KB_STOP_IO _IO(KB_IOC_MAGIC, 22 ) 134 | /* For download of configuration to Master Gateway: stop IO communication 135 | * completely. 136 | */ 137 | #define KB_CONFIG_STOP _IO(KB_IOC_MAGIC, 23 ) 138 | /* for download of configuration to Master Gateway: download config data */ 139 | #define KB_CONFIG_SEND _IO(KB_IOC_MAGIC, 24 ) 140 | /* for download of configuration to Master Gateway: restart IO communication */ 141 | #define KB_CONFIG_START _IO(KB_IOC_MAGIC, 25 ) 142 | /* Activate a watchdog. If write is not called for a given period all outputs 143 | * are set to 0. 144 | */ 145 | #define KB_SET_OUTPUT_WATCHDOG _IO(KB_IOC_MAGIC, 26 ) 146 | /* set the f_pos, the unsigned int * is used to interpret the pos value */ 147 | #define KB_SET_POS _IO(KB_IOC_MAGIC, 27 ) 148 | #define KB_AIO_CALIBRATE _IO(KB_IOC_MAGIC, 28 ) 149 | /* get counter values of a RO module */ 150 | #define KB_RO_GET_COUNTER _IO(KB_IOC_MAGIC, 29 ) 151 | 152 | /* wait for an event. This call is normally blocking */ 153 | #define KB_WAIT_FOR_EVENT _IO(KB_IOC_MAGIC, 50 ) 154 | /* piControl was reset, reload configuration */ 155 | #define KB_EVENT_RESET 1 156 | 157 | /* new ioctl to upload firmware */ 158 | #define PICONTROL_UPLOAD_FIRMWARE _IOW(KB_IOC_MAGIC, 200, struct picontrol_firmware_upload ) 159 | 160 | typedef struct SDIOResetCounterStr { 161 | /* Address of module in current configuration */ 162 | __u8 i8uAddress; 163 | __u8 pad; 164 | /* bitfield, if bit n is 1, reset counter/encoder on input */ 165 | __u16 i16uBitfield; 166 | } SDIOResetCounter; 167 | 168 | enum revpi_ro_num { 169 | RELAY_1 = 0, 170 | RELAY_2, 171 | RELAY_3, 172 | RELAY_4, 173 | REVPI_RO_NUM_RELAYS, 174 | }; 175 | 176 | /* Data for KB_RO_GET_COUNTER ioctl */ 177 | struct revpi_ro_ioctl_counters { 178 | /* Address of module in current configuration, set by userspace. */ 179 | __u8 addr; 180 | /* Data returned from kernel */ 181 | __u32 counter[REVPI_RO_NUM_RELAYS]; 182 | } __attribute__((__packed__)); 183 | 184 | #define REVPI_RO_RELAY_1_BIT BIT(0) 185 | #define REVPI_RO_RELAY_2_BIT BIT(1) 186 | #define REVPI_RO_RELAY_3_BIT BIT(2) 187 | #define REVPI_RO_RELAY_4_BIT BIT(3) 188 | 189 | struct pictl_calibrate { 190 | /* Address of module in current configuration */ 191 | __u8 address; 192 | /* bitfield: mode */ 193 | __u8 mode; 194 | /* channels to calibrate */ 195 | __u8 channels; 196 | /* point in lookupTable */ 197 | __u8 x_val; 198 | __s16 y_val; 199 | }; 200 | 201 | #define MAX_TELEGRAM_DATA_SIZE 255 202 | 203 | typedef struct SConfigDataStr { 204 | __u8 bLeft; 205 | __u8 pad; 206 | __u16 i16uLen; 207 | __u8 acData[MAX_TELEGRAM_DATA_SIZE]; 208 | } SConfigData; 209 | 210 | #endif /* PICONTROL_H_ */ 211 | -------------------------------------------------------------------------------- /src/piControlMain.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2016-2024 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef PRODUCTS_PIBASE_PIKERNELMOD_PICONTROLINTERN_H_ 6 | #define PRODUCTS_PIBASE_PIKERNELMOD_PICONTROLINTERN_H_ 7 | /******************************************************************************/ 8 | /******************************** Includes **********************************/ 9 | /******************************************************************************/ 10 | #include 11 | #include 12 | 13 | #include "common_define.h" 14 | #include "piConfig.h" 15 | #include "project.h" 16 | /******************************************************************************/ 17 | /********************************* Types ************************************/ 18 | /******************************************************************************/ 19 | typedef enum piEvent { 20 | piEvReset = 1, 21 | } enPiEvent; 22 | 23 | enum revpi_machine { 24 | REVPI_CORE = 1, 25 | REVPI_COMPACT = 2, 26 | REVPI_CONNECT = 3, 27 | REVPI_FLAT = 4, 28 | REVPI_CONNECT_SE = 5, 29 | REVPI_CORE_SE = 6, 30 | REVPI_CONNECT_4 = 7, 31 | REVPI_CONNECT_5 = 8, 32 | REVPI_GENERIC_PB = 255, 33 | }; 34 | 35 | enum { 36 | REVPI_PIBRIDGE_ETHERNET_GPIO_MPX = 0, 37 | REVPI_PIBRIDGE_ETHERNET_GPIO_DETECT 38 | }; 39 | 40 | typedef struct spiControlDev { 41 | // device driver stuff 42 | enum revpi_machine machine_type; 43 | void *machine; 44 | struct cdev cdev; // Char device structure 45 | struct device *dev; 46 | struct thermal_zone_device *thermal_zone; 47 | 48 | // supports extension modules with RS485 based communication (eg. DIO or AIO) 49 | unsigned int pibridge_supported:1; 50 | // device is only equipped on the left side with a PiBridge connector (eg. RevPi Connect) 51 | unsigned int only_left_pibridge:1; 52 | // device supports RevPi gateways (only point to point communication) 53 | unsigned int revpi_gate_supported:1; 54 | 55 | // process image stuff 56 | INT8U ai8uPI[KB_PI_LEN]; 57 | INT8U ai8uPIDefault[KB_PI_LEN]; 58 | struct rt_mutex lockPI; 59 | #define PICONTROL_DEV_FLAG_STOP_IO (1 << 0) 60 | unsigned long flags; 61 | piDevices *devs; 62 | piEntries *ent; 63 | piCopylist *cl; 64 | /* Protect internal resources, like devs, ent, cl, etc. during 65 | execution of ioctls. This is especially needed during reset. */ 66 | struct rt_mutex lockIoctl; 67 | piConnectionList *connl; 68 | ktime_t tLastOutput1, tLastOutput2; 69 | 70 | // handle open connections and notification 71 | struct list_head listCon; 72 | struct rt_mutex lockListCon; 73 | 74 | struct led_trigger power_red; 75 | struct led_trigger a1_green; 76 | struct led_trigger a1_red; 77 | struct led_trigger a2_green; 78 | struct led_trigger a2_red; 79 | struct led_trigger a3_green; 80 | struct led_trigger a3_red; 81 | /* Revpi Flat only */ 82 | struct led_trigger a4_green; 83 | struct led_trigger a4_red; 84 | struct led_trigger a5_green; 85 | struct led_trigger a5_red; 86 | /* RevPi Connect 4, 5 */ 87 | struct led_trigger a1_blue; 88 | struct led_trigger a2_blue; 89 | struct led_trigger a3_blue; 90 | struct led_trigger a4_blue; 91 | struct led_trigger a5_blue; 92 | /* Gigabit ethernet on PiBridge */ 93 | bool pibridge_mode_ethernet_left; 94 | bool pibridge_mode_ethernet_right; 95 | } tpiControlDev; 96 | 97 | typedef struct spiEventEntry { 98 | enum piEvent event; 99 | struct list_head list; 100 | } tpiEventEntry; 101 | 102 | typedef struct spiControlInst { 103 | struct device *dev; 104 | wait_queue_head_t wq; 105 | struct list_head piEventList; // head of the event list for this instance 106 | struct rt_mutex lockEventList; 107 | struct list_head list; // list of all instances 108 | ktime_t tTimeoutTS; // time stamp when the output must be set to 0 109 | unsigned long tTimeoutDurationMs; // length of the timeout in ms, 0 if not active 110 | char pcErrorMessage[REV_PI_ERROR_MSG_LEN]; // error message of last ioctl call 111 | } tpiControlInst; 112 | 113 | extern tpiControlDev piDev_g; 114 | 115 | /******************************************************************************/ 116 | /******************************* Prototypes *********************************/ 117 | /******************************************************************************/ 118 | 119 | bool isRunning(void); 120 | void printUserMsg(tpiControlInst *priv, const char *s, ...); 121 | 122 | #endif /* PRODUCTS_PIBASE_PIKERNELMOD_PICONTROLINTERN_H_ */ 123 | -------------------------------------------------------------------------------- /src/piDIOComm.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | 4 | #include 5 | 6 | #include "piDIOComm.h" 7 | #include "common_define.h" 8 | #include "revpi_core.h" 9 | 10 | #define DIO_OUTPUT_DATA_LEN 18 11 | #define DIO_MAX_COUNTERS 6 12 | #define DIO_PWM_DATA_LEN sizeof(struct pwm_data) 13 | 14 | static INT8U i8uConfigured_s = 0; 15 | static SDioConfig dioConfig_s[10]; 16 | static INT8U i8uNumCounter[64]; 17 | static INT16U i16uCounterAct[64]; 18 | 19 | void piDIOComm_InitStart(void) 20 | { 21 | i8uConfigured_s = 0; 22 | } 23 | 24 | INT32U piDIOComm_Config(uint8_t i8uAddress, uint16_t i16uNumEntries, SEntryInfo * pEnt) 25 | { 26 | uint16_t i; 27 | 28 | if (i8uConfigured_s >= sizeof(dioConfig_s) / sizeof(SDioConfig)) { 29 | pr_err("max. number of DIOs reached\n"); 30 | return -1; 31 | } 32 | 33 | pr_info_dio("piDIOComm_Config addr %d entries %d num %d\n", i8uAddress, i16uNumEntries, i8uConfigured_s); 34 | memset(&dioConfig_s[i8uConfigured_s], 0, sizeof(SDioConfig)); 35 | 36 | dioConfig_s[i8uConfigured_s].i8uAddr = i8uAddress; 37 | 38 | i8uNumCounter[i8uAddress] = 0; 39 | i16uCounterAct[i8uAddress] = 0; 40 | 41 | for (i = 0; i < i16uNumEntries; i++) { 42 | pr_info_dio("addr %2d type %d len %3d offset %3d value %d 0x%x\n", 43 | pEnt[i].i8uAddress, pEnt[i].i8uType, pEnt[i].i16uBitLength, pEnt[i].i16uOffset, 44 | pEnt[i].i32uDefault, pEnt[i].i32uDefault); 45 | 46 | if (pEnt[i].i16uOffset >= 88 && pEnt[i].i16uOffset <= 103) { 47 | dioConfig_s[i8uConfigured_s].i32uInputMode |= 48 | (pEnt[i].i32uDefault & 0x03) << ((pEnt[i].i16uOffset - 88) * 2); 49 | if ((pEnt[i].i32uDefault == 1 || pEnt[i].i32uDefault == 2) 50 | || (pEnt[i].i32uDefault == 3 && ((pEnt[i].i16uOffset - 88) % 2) == 0)) { 51 | i8uNumCounter[i8uAddress]++; 52 | i16uCounterAct[i8uAddress] |= (1 << (pEnt[i].i16uOffset - 88)); 53 | } 54 | } else { 55 | switch (pEnt[i].i16uOffset) { 56 | case 104: 57 | dioConfig_s[i8uConfigured_s].i8uInputDebounce = pEnt[i].i32uDefault; 58 | break; 59 | case 106: 60 | dioConfig_s[i8uConfigured_s].i16uOutputPushPull = pEnt[i].i32uDefault; 61 | break; 62 | case 108: 63 | dioConfig_s[i8uConfigured_s].i16uOutputOpenLoadDetect = pEnt[i].i32uDefault; 64 | break; 65 | case 110: 66 | dioConfig_s[i8uConfigured_s].i16uOutputPWM = pEnt[i].i32uDefault; 67 | break; 68 | case 112: 69 | dioConfig_s[i8uConfigured_s].i8uOutputPWMIncrement = pEnt[i].i32uDefault; 70 | break; 71 | } 72 | } 73 | } 74 | 75 | if (i8uNumCounter[i8uAddress] > DIO_MAX_COUNTERS) { 76 | pr_err("invalid number of counters: %u (max: %u)\n", 77 | i8uNumCounter[i8uAddress], DIO_MAX_COUNTERS); 78 | return -1; 79 | } 80 | 81 | pr_info_dio("piDIOComm_Config done addr %d input mode %08x numCnt %d\n", i8uAddress, 82 | dioConfig_s[i8uConfigured_s].i32uInputMode, i8uNumCounter[i8uAddress]); 83 | i8uConfigured_s++; 84 | 85 | return 0; 86 | } 87 | 88 | INT32U piDIOComm_Init(INT8U i8uDevice_p) 89 | { 90 | u8 addr = RevPiDevice_getDev(i8uDevice_p)->i8uAddress; 91 | u8 snd_len = sizeof(SDioConfig); 92 | u8 *snd_buf; 93 | int ret; 94 | int i; 95 | 96 | pr_info_dio("piDIOComm_Init %d of %d addr %d numCnt %d\n", i8uDevice_p, 97 | i8uConfigured_s, addr, i8uNumCounter[addr]); 98 | 99 | ret = 4; // unknown device 100 | 101 | for (i = 0; i < i8uConfigured_s; i++) { 102 | if (dioConfig_s[i].i8uAddr == addr) { 103 | snd_buf = (u8 *) &dioConfig_s[i].i16uOutputPushPull; 104 | 105 | ret = pibridge_req_io(addr, IOP_TYP1_CMD_CFG, snd_buf, 106 | snd_len, NULL, 0); 107 | break; 108 | } 109 | } 110 | 111 | return ret; 112 | } 113 | 114 | INT32U piDIOComm_sendCyclicTelegram(u8 devnum) 115 | { 116 | static u8 last_out[40][DIO_OUTPUT_DATA_LEN]; 117 | u8 in_buf[IOPROTOCOL_MAXDATA_LENGTH]; 118 | u8 out_buf[DIO_OUTPUT_DATA_LEN]; 119 | /* out_buf and additional 2 bytes for calculated channel mask */ 120 | u8 snd_buf[DIO_PWM_DATA_LEN]; 121 | SDevice *revpi_dev; 122 | u8 data_in[70]; 123 | u8 snd_len; 124 | u8 rcv_len; 125 | u8 addr; 126 | int ret; 127 | u8 cmd; 128 | u8 i; 129 | u8 j; 130 | 131 | revpi_dev = RevPiDevice_getDev(devnum); 132 | 133 | if (revpi_dev->sId.i16uFBS_OutputLength != DIO_OUTPUT_DATA_LEN) 134 | return 4; 135 | 136 | addr = revpi_dev->i8uAddress; 137 | 138 | if (!test_bit(PICONTROL_DEV_FLAG_STOP_IO, &piDev_g.flags)) { 139 | rt_mutex_lock(&piDev_g.lockPI); 140 | memcpy(out_buf, piDev_g.ai8uPI + revpi_dev->i16uOutputOffset, 141 | DIO_OUTPUT_DATA_LEN); 142 | rt_mutex_unlock(&piDev_g.lockPI); 143 | } else { 144 | memset(out_buf, 0, sizeof(out_buf)); 145 | } 146 | 147 | /* check if any PWM values have changed since last cycle */ 148 | if (!memcmp(out_buf + 2, last_out[addr] + 2, DIO_OUTPUT_DATA_LEN - 2)) { 149 | // only the direct output pins have changed 150 | snd_len = sizeof(u16); 151 | cmd = IOP_TYP1_CMD_DATA; 152 | memcpy(snd_buf, out_buf, snd_len); 153 | } else { 154 | struct pwm_data *pwm = (struct pwm_data *) snd_buf; 155 | 156 | memcpy(&pwm->output, out_buf, sizeof(u16)); 157 | // copy all PWM values that have changed 158 | pwm->channels = 0; 159 | j = 0; 160 | for (i = 0; i < 16; i++) { 161 | if (last_out[addr][i + 2] != out_buf[i + 2]) { 162 | pwm->channels |= 1 << i; 163 | pwm->value[j] = out_buf[i + 2]; 164 | j++; 165 | } 166 | } 167 | snd_len = j + 4; 168 | cmd = IOP_TYP1_CMD_DATA2; 169 | } 170 | 171 | memcpy(last_out[addr], out_buf, sizeof(out_buf)); 172 | 173 | rcv_len = 3 * sizeof(u16) + i8uNumCounter[addr] * sizeof(u32); 174 | 175 | ret = pibridge_req_io(addr, cmd, snd_buf, snd_len, in_buf, rcv_len); 176 | if (ret != rcv_len) { 177 | pr_debug("DIO addr %2d: communication failed (req:%u,ret:%d)\n", 178 | addr, rcv_len, ret); 179 | 180 | if (ret >= 0) 181 | ret = -EIO; 182 | 183 | return ret; 184 | } 185 | 186 | memcpy(&data_in[0], in_buf, 3 * sizeof(u16)); 187 | memset(&data_in[6], 0, 64); 188 | 189 | j = 0; 190 | for (i = 0; i < 16; i++) { 191 | if (i16uCounterAct[addr] & (1 << i)) { 192 | memcpy(&data_in[3 * sizeof(u16) + i * sizeof(u32)], 193 | &in_buf[3 * sizeof(u16) + j * sizeof(u32)], 194 | sizeof(u32)); 195 | j++; 196 | } 197 | } 198 | 199 | rt_mutex_lock(&piDev_g.lockPI); 200 | memcpy(piDev_g.ai8uPI + revpi_dev->i16uInputOffset, data_in, 201 | sizeof(data_in)); 202 | rt_mutex_unlock(&piDev_g.lockPI); 203 | 204 | return 0; 205 | } 206 | -------------------------------------------------------------------------------- /src/piDIOComm.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | */ 4 | 5 | #pragma once 6 | 7 | #include "common_define.h" 8 | #include "picontrol_intern.h" 9 | #include "piControl.h" 10 | 11 | typedef enum 12 | { 13 | DIOSTATE_OFFLINE = 0x00, // Physikalisch nicht verbunden 14 | DIOSTATE_CYCLIC_IO = 0x01, // Zyklischer Datenaustausch ist aktiv 15 | } DioCommStatus; 16 | 17 | void piDIOComm_InitStart(void); 18 | 19 | INT32U piDIOComm_Config(uint8_t i8uAddress, uint16_t i16uNumEntries, SEntryInfo * pEnt); 20 | 21 | INT32U piDIOComm_Init(INT8U i8uDevice_p); 22 | 23 | INT32U piDIOComm_sendCyclicTelegram(INT8U i8uDevice_p); 24 | -------------------------------------------------------------------------------- /src/piFirmwareUpdate.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // SPDX-FileCopyrightText: 2017-2024 KUNBUS GmbH 3 | 4 | #include 5 | #include "fwuFlashFileMain.h" 6 | #include "piFirmwareUpdate.h" 7 | #include "RS485FwuCommand.h" 8 | #include "revpi_core.h" 9 | 10 | #define TFPGA_HEAD_DATA_OFFSET 6 11 | #define CHUNK_TRANSMISSION_ATTEMPTS 100 12 | #define FLASH_ERASE_ATTEMPTS 50 13 | 14 | // ret < 0: error 15 | // ret == 0: no update needed 16 | // ret > 0: updated 17 | int FWU_update(tpiControlInst *priv, SDevice *pDev_p) 18 | { 19 | struct file *input; 20 | char *filename; 21 | char *data = 0; 22 | loff_t length; 23 | int ret = -EINVAL; 24 | TFileHead header; 25 | T_KUNBUS_APPL_DESCR *pApplDesc; 26 | int read; 27 | 28 | if (pDev_p->sId.i16uModulType >= PICONTROL_SW_OFFSET) { 29 | printUserMsg(priv, "Virtual modules don't have firmware to update"); 30 | return -EOPNOTSUPP; 31 | } 32 | 33 | if (pDev_p->i8uAddress == 0) { 34 | printUserMsg(priv, "RevPi has no firmware to update"); 35 | return -EOPNOTSUPP; 36 | } 37 | 38 | filename = kmalloc(PATH_MAX, GFP_KERNEL); 39 | 40 | sprintf(filename, FIRMWARE_PATH "/fw_%05d_%03d.fwu", pDev_p->sId.i16uModulType, pDev_p->sId.i16uHW_Revision); 41 | 42 | input = open_filename(filename, O_RDONLY); 43 | 44 | if (!input) { 45 | kfree(filename); 46 | return -ENOENT; 47 | } 48 | 49 | read = kernel_read(input, (char *)&header, sizeof(header), &input->f_pos); 50 | if (read <= 0) { 51 | pr_err("kernel_read returned %d: %s, %lld\n", read, filename, input->f_pos); 52 | ret = -EINVAL; 53 | goto laError; 54 | } 55 | 56 | if (header.dat.usType != pDev_p->sId.i16uModulType) { 57 | if (header.dat.usType == KUNBUS_FW_DESCR_TYP_PI_DIO_14 58 | && (pDev_p->sId.i16uModulType == KUNBUS_FW_DESCR_TYP_PI_DO_16 59 | || pDev_p->sId.i16uModulType == KUNBUS_FW_DESCR_TYP_PI_DI_16)) 60 | { 61 | // this exception is allowed 62 | // DIO, DI and DO use the same software 63 | } 64 | else 65 | { 66 | pr_err("Module type does not match: %d != %d\nFirmware file %s is corrupt!\n", header.dat.usType, pDev_p->sId.i16uModulType, filename); 67 | ret = -ENODEV; 68 | goto laError; 69 | } 70 | } 71 | if (header.dat.usHwRev != pDev_p->sId.i16uHW_Revision) { 72 | pr_err("HW revision does not match: %d != %d\nFirmware file %s is corrupt!\n", header.dat.usHwRev, pDev_p->sId.i16uHW_Revision, filename); 73 | ret = -ENODEV; 74 | goto laError; 75 | } 76 | 77 | length = vfs_llseek(input, 0, SEEK_END); 78 | pr_info("firmware file length: %ld\n", (long int)length); 79 | 80 | length -= header.ulLength + 6; // without header 81 | data = kmalloc(length, GFP_KERNEL); 82 | if (data == NULL) { 83 | pr_err("out of memory\n"); 84 | goto laError; 85 | } 86 | 87 | // seek to the begin of the application descriptor, if we are not already there 88 | vfs_llseek(input, header.ulLength + 6, SEEK_SET); 89 | 90 | // read the whole file 91 | read = kernel_read(input, data, length, &input->f_pos); 92 | if (read < length) { 93 | pr_err("kernel_read returned %d: %s, %lld\n", read, filename, input->f_pos); 94 | goto laError; 95 | } 96 | 97 | pApplDesc = (T_KUNBUS_APPL_DESCR *)data; 98 | 99 | if ((pApplDesc->i8uSwMajor < pDev_p->sId.i16uSW_Major) 100 | || (pApplDesc->i8uSwMajor == pDev_p->sId.i16uSW_Major && pApplDesc->i16uSwMinor <= pDev_p->sId.i16uSW_Minor)) { 101 | // firmware file is older or equal than firmware in module -> nothing to do 102 | printUserMsg(priv, "firmware is up to date: %d.%d >= %d.%d", 103 | pDev_p->sId.i16uSW_Major, pDev_p->sId.i16uSW_Minor, 104 | pApplDesc->i8uSwMajor, pApplDesc->i16uSwMinor); 105 | ret = 0; 106 | goto laError; 107 | } 108 | 109 | printUserMsg(priv, "update firmware: %d.%d --> %d.%d", 110 | pDev_p->sId.i16uSW_Major, pDev_p->sId.i16uSW_Minor, 111 | pApplDesc->i8uSwMajor, pApplDesc->i16uSwMinor); 112 | 113 | if (PiBridgeMaster_FWUModeEnter(pDev_p->i8uAddress, pDev_p->i8uScan) == 0) { 114 | msleep(500); 115 | 116 | ret = PiBridgeMaster_FWUflashErase(); 117 | if (ret) { 118 | goto laError; 119 | } 120 | 121 | ret = PiBridgeMaster_FWUflashWrite(header.dat.ulFlashStart, data, length); 122 | if (ret) { 123 | goto laError; 124 | } 125 | 126 | PiBridgeMaster_FWUReset(); 127 | printUserMsg(priv,"update firmware success"); 128 | ret = 1; // success 129 | } 130 | 131 | laError: 132 | if (data) 133 | kfree(data); 134 | kfree(filename); 135 | close_filename(input); 136 | if (ret < 0) 137 | printUserMsg(priv, "update firmware fail"); 138 | return ret; 139 | } 140 | 141 | static bool firmware_is_newer(unsigned int major_a, unsigned int minor_a, 142 | unsigned int major_b, unsigned int minor_b) 143 | { 144 | if (major_a < major_b) 145 | return false; 146 | if (major_a > major_b) 147 | return true; 148 | if (minor_a > minor_b) 149 | return true; 150 | return false; 151 | } 152 | 153 | static int flash_chunk(unsigned int dev_addr, unsigned int chunk_addr, 154 | unsigned char *chunk_data, unsigned int chunk_len, 155 | unsigned int *retrans) 156 | 157 | { 158 | unsigned int attempts = CHUNK_TRANSMISSION_ATTEMPTS; 159 | int ret; 160 | 161 | do { 162 | ret = fwuWrite(dev_addr, chunk_addr, chunk_data, chunk_len); 163 | if (ret) { 164 | /* 165 | * A failure may result from a protocol communication 166 | * error. Retry to submit the chunk a if attempts are 167 | * left. 168 | */ 169 | usleep_range(1000, 2000); 170 | attempts--; 171 | pr_debug("Error transmitting firmware for flash addr 0x%08x, len %u: %i (left attempts: %u)\n", 172 | chunk_addr, chunk_len, ret, attempts); 173 | } 174 | } while (ret && attempts); 175 | 176 | *retrans = CHUNK_TRANSMISSION_ATTEMPTS - attempts; 177 | 178 | return ret; 179 | } 180 | 181 | int flash_firmware(unsigned int dev_addr, unsigned int flash_addr, 182 | unsigned char *upload_data, unsigned int upload_len) 183 | { 184 | unsigned int upload_offset = 0; 185 | unsigned int total_retrans = 0; 186 | unsigned int chunk_len; 187 | unsigned int retrans; 188 | int ret = 0; 189 | 190 | while (upload_len) { 191 | if (upload_len > MAX_FWU_DATA_SIZE) 192 | chunk_len = MAX_FWU_DATA_SIZE; 193 | else 194 | chunk_len = upload_len; 195 | 196 | ret = flash_chunk(dev_addr, flash_addr + upload_offset, 197 | upload_data + upload_offset, chunk_len, 198 | &retrans); 199 | total_retrans += retrans; 200 | if (ret) 201 | break; 202 | 203 | upload_offset += chunk_len; 204 | upload_len -= chunk_len; 205 | } 206 | 207 | if (total_retrans) 208 | pr_warn("%u retransmissions during firmware update required\n", 209 | total_retrans); 210 | 211 | return ret; 212 | } 213 | 214 | int erase_flash(unsigned int dev_addr) 215 | { 216 | unsigned int attempts = FLASH_ERASE_ATTEMPTS; 217 | int ret; 218 | 219 | do { 220 | ret = fwuEraseFlash(dev_addr); 221 | if (ret) { 222 | /* 223 | * A failure may result from a protocol communication 224 | * error. Retry to erase flash if attempts are left. 225 | */ 226 | usleep_range(1000, 2000); 227 | attempts--; 228 | pr_debug("Error erasing flash: %i (left attempts: %u)\n", 229 | ret, attempts); 230 | } 231 | } while (ret && attempts); 232 | 233 | if (attempts != FLASH_ERASE_ATTEMPTS) 234 | pr_warn("%u attempts to erase flash required\n", 235 | FLASH_ERASE_ATTEMPTS - attempts); 236 | return ret; 237 | } 238 | 239 | int upload_firmware(SDevice *sdev, const struct firmware *fw, u32 mask) 240 | { 241 | T_KUNBUS_APPL_DESCR *desc; 242 | unsigned int flash_offset; 243 | unsigned int upload_len; 244 | unsigned int dev_addr; 245 | bool force_upload; 246 | TFileHead *hdr; 247 | bool update; 248 | int ret = 0; 249 | 250 | force_upload = !!(mask & PICONTROL_FIRMWARE_FORCE_UPLOAD); 251 | 252 | hdr = (TFileHead *) &fw->data[0]; 253 | if (hdr->dat.usType != sdev->sId.i16uModulType) { 254 | if (hdr->dat.usType != KUNBUS_FW_DESCR_TYP_PI_DIO_14) 255 | return -EIO; 256 | if ((sdev->sId.i16uModulType != KUNBUS_FW_DESCR_TYP_PI_DO_16) && 257 | (sdev->sId.i16uModulType != KUNBUS_FW_DESCR_TYP_PI_DI_16)) 258 | return -EIO; 259 | } 260 | 261 | if (hdr->dat.usHwRev != sdev->sId.i16uHW_Revision) { 262 | pr_err("HW revision %u in FW does not match HW revision %u of device\n", 263 | hdr->dat.usHwRev, sdev->sId.i16uHW_Revision); 264 | return -EIO; 265 | } 266 | /* Flashing starts with an offset to the "fpga" element of the 267 | TFileHead structure */ 268 | flash_offset = hdr->ulLength + TFPGA_HEAD_DATA_OFFSET; 269 | 270 | if (fw->size <= flash_offset) { 271 | pr_err("firmware corrupted: invalid header length %u in firmware with size %zu\n", 272 | hdr->ulLength, fw->size); 273 | return -EIO; 274 | } 275 | desc = (T_KUNBUS_APPL_DESCR *) &fw->data[flash_offset]; 276 | update = firmware_is_newer(desc->i8uSwMajor, 277 | desc->i16uSwMinor, 278 | sdev->sId.i16uSW_Major, 279 | sdev->sId.i16uSW_Minor); 280 | if (update) { 281 | pr_info("Updating firmware from %u.%u to %u.%u\n", 282 | sdev->sId.i16uSW_Major, sdev->sId.i16uSW_Minor, 283 | desc->i8uSwMajor, desc->i16uSwMinor); 284 | } else if (force_upload) { 285 | pr_info("Forced replacement of firmware %u.%u to %u.%u\n", 286 | sdev->sId.i16uSW_Major, sdev->sId.i16uSW_Minor, 287 | desc->i8uSwMajor, desc->i16uSwMinor); 288 | } else { 289 | pr_info("Firmware %u.%u already up to date. Use forced upload to override with firmware %u.%u\n", 290 | sdev->sId.i16uSW_Major, sdev->sId.i16uSW_Minor, 291 | desc->i8uSwMajor, desc->i16uSwMinor); 292 | return 1; 293 | } 294 | 295 | upload_len = fw->size - flash_offset; 296 | dev_addr = sdev->i8uAddress; 297 | 298 | if (fwuEnterFwuMode(dev_addr) < 0) { 299 | pr_err("error entering firmware update mode\n"); 300 | return -EIO; 301 | } 302 | 303 | /* Old mGates always use 2 as device address */ 304 | if (!sdev->i8uScan) 305 | dev_addr = 2; 306 | else if (dev_addr < REV_PI_DEV_FIRST_RIGHT) { 307 | dev_addr = 1; 308 | } else { 309 | if (dev_addr == RevPiDevice_getAddrRight() - 1) 310 | dev_addr = 2; 311 | else 312 | dev_addr = 1; 313 | } 314 | 315 | msleep(500); 316 | 317 | if (erase_flash(dev_addr)) { 318 | pr_err("failed to erase flash\n"); 319 | ret = -EIO; 320 | goto reset; 321 | } 322 | if (flash_firmware(dev_addr, hdr->dat.ulFlashStart, 323 | (unsigned char *) desc, upload_len) < 0) { 324 | pr_err("Errors while flashing firmware\n"); 325 | ret = -EIO; 326 | goto reset; 327 | } 328 | 329 | pr_info("Firmware upload successful."); 330 | reset: 331 | if (fwuResetModule(dev_addr) < 0) { 332 | pr_err("failed to reset after firmware update\n"); 333 | ret = -EIO; 334 | } 335 | 336 | return ret; 337 | } 338 | -------------------------------------------------------------------------------- /src/piFirmwareUpdate.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2017-2024 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef PIFIRMWAREUPDATE_H 6 | #define PIFIRMWAREUPDATE_H 7 | 8 | #include 9 | #include "piControlMain.h" 10 | #include "RevPiDevice.h" 11 | 12 | #define FIRMWARE_PATH "/lib/firmware/revpi" 13 | 14 | 15 | int FWU_update(tpiControlInst *priv, SDevice *pDev_p); 16 | int flash_firmware(unsigned int dev_addr, unsigned int flash_addr, 17 | unsigned char *upload_data, unsigned int upload_len); 18 | int erase_flash(unsigned int dev_addr); 19 | int upload_firmware(SDevice *sdev, const struct firmware *fw, 20 | u32 mask); 21 | 22 | #endif // PIFIRMWAREUPDATE_H 23 | -------------------------------------------------------------------------------- /src/piIOComm.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | 4 | #include 5 | 6 | #include "piIOComm.h" 7 | #include "common_define.h" 8 | #include "revpi_core.h" 9 | 10 | #include "picontrol_trace.h" 11 | 12 | 13 | int piIoComm_send(INT8U * buf_p, INT16U i16uLen_p) 14 | { 15 | int written; 16 | 17 | #ifdef DEBUG_SERIALCOMM 18 | if (i16uLen_p == 1) { 19 | pr_info("send %d: %02x\n", i16uLen_p, buf_p[0]); 20 | } else if (i16uLen_p == 2) { 21 | pr_info("send %d: %02x %02x\n", i16uLen_p, buf_p[0], buf_p[1]); 22 | } else if (i16uLen_p == 3) { 23 | pr_info("send %d: %02x %02x %02x\n", i16uLen_p, buf_p[0], buf_p[1], buf_p[2]); 24 | } else if (i16uLen_p == 4) { 25 | pr_info("send %d: %02x %02x %02x %02x\n", i16uLen_p, buf_p[0], buf_p[1], buf_p[2], buf_p[3]); 26 | } else if (i16uLen_p == 5) { 27 | pr_info("send %d: %02x %02x %02x %02x %02x\n", i16uLen_p, buf_p[0], buf_p[1], buf_p[2], buf_p[3], buf_p[4]); 28 | } else if (i16uLen_p == 6) { 29 | pr_info("send %d: %02x %02x %02x %02x %02x %02x\n", i16uLen_p, buf_p[0], buf_p[1], buf_p[2], buf_p[3], buf_p[4], buf_p[5]); 30 | } else if (i16uLen_p == 7) { 31 | pr_info("send %d: %02x %02x %02x %02x %02x %02x %02x\n", i16uLen_p, buf_p[0], buf_p[1], buf_p[2], buf_p[3], buf_p[4], buf_p[5], buf_p[6]); 32 | } else if (i16uLen_p == 8) { 33 | pr_info("send %d: %02x %02x %02x %02x %02x %02x %02x %02x\n", i16uLen_p, buf_p[0], buf_p[1], buf_p[2], 34 | buf_p[3], buf_p[4], buf_p[5], buf_p[6], buf_p[7]); 35 | } else { 36 | pr_info("send %d: %02x %02x %02x %02x %02x %02x %02x %02x %02x ...\n", i16uLen_p, buf_p[0], buf_p[1], 37 | buf_p[2], buf_p[3], buf_p[4], buf_p[5], buf_p[6], buf_p[7], buf_p[8]); 38 | } 39 | //pr_info("vfs_write(%d, %d, %d)\n", (int)piIoComm_fd_m, i16uLen_p, (int)piIoComm_fd_m->f_pos); 40 | #endif 41 | /* First clear receive FIFO to remove stale data */ 42 | pibridge_clear_fifo(); 43 | 44 | written = pibridge_send(buf_p, i16uLen_p); 45 | if (written < 0) { 46 | pr_info_serial("pibridge_send error: %i\n", written); 47 | return written; 48 | } else if (written != i16uLen_p) { 49 | pr_info_serial("pibridge_send error: not all data written (%i/%i)\n", 50 | written, i16uLen_p); 51 | return -ETIMEDOUT; 52 | } 53 | 54 | return 0; 55 | } 56 | 57 | 58 | INT8U piIoComm_Crc8(INT8U * pi8uFrame_p, INT16U i16uLen_p) 59 | { 60 | INT8U i8uRv_l = 0; 61 | 62 | while (i16uLen_p--) { 63 | i8uRv_l = i8uRv_l ^ pi8uFrame_p[i16uLen_p]; 64 | } 65 | return i8uRv_l; 66 | } 67 | 68 | void piIoComm_writeSniff1A(EGpioValue eVal_p, EGpioMode eMode_p) 69 | { 70 | #ifdef DEBUG_GPIO 71 | pr_info("sniff1A: mode %d value %d\n", (int)eMode_p, (int)eVal_p); 72 | #endif 73 | piIoComm_writeSniff(piCore_g.gpio_sniff1a, eVal_p, eMode_p); 74 | if (eMode_p == enGpioMode_Output) 75 | trace_picontrol_sniffpin_1a_set(eVal_p); 76 | } 77 | 78 | void piIoComm_writeSniff1B(EGpioValue eVal_p, EGpioMode eMode_p) 79 | { 80 | if (!piDev_g.only_left_pibridge) { 81 | piIoComm_writeSniff(piCore_g.gpio_sniff1b, eVal_p, eMode_p); 82 | if (eMode_p == enGpioMode_Output) 83 | trace_picontrol_sniffpin_1b_set(eVal_p); 84 | #ifdef DEBUG_GPIO 85 | pr_info("sniff1B: mode %d value %d\n", (int)eMode_p, (int)eVal_p); 86 | #endif 87 | } 88 | } 89 | 90 | void piIoComm_writeSniff2A(EGpioValue eVal_p, EGpioMode eMode_p) 91 | { 92 | #ifdef DEBUG_GPIO 93 | pr_info("sniff2A: mode %d value %d\n", (int)eMode_p, (int)eVal_p); 94 | #endif 95 | piIoComm_writeSniff(piCore_g.gpio_sniff2a, eVal_p, eMode_p); 96 | if (eMode_p == enGpioMode_Output) 97 | trace_picontrol_sniffpin_2a_set(eVal_p); 98 | } 99 | 100 | void piIoComm_writeSniff2B(EGpioValue eVal_p, EGpioMode eMode_p) 101 | { 102 | if (!piDev_g.only_left_pibridge) { 103 | piIoComm_writeSniff(piCore_g.gpio_sniff2b, eVal_p, eMode_p); 104 | if (eMode_p == enGpioMode_Output) 105 | trace_picontrol_sniffpin_2b_set(eVal_p); 106 | #ifdef DEBUG_GPIO 107 | pr_info("sniff2B: mode %d value %d\n", (int)eMode_p, (int)eVal_p); 108 | #endif 109 | } 110 | } 111 | 112 | void piIoComm_writeSniff(struct gpio_desc *pGpio, EGpioValue eVal_p, EGpioMode eMode_p) 113 | { 114 | if (eMode_p == enGpioMode_Input) { 115 | gpiod_direction_input(pGpio); 116 | } else { 117 | gpiod_direction_output(pGpio, eVal_p); 118 | gpiod_set_value_cansleep(pGpio, eVal_p); 119 | } 120 | } 121 | 122 | EGpioValue piIoComm_readSniff1A() 123 | { 124 | EGpioValue v = piIoComm_readSniff(piCore_g.gpio_sniff1a); 125 | trace_picontrol_sniffpin_1a_read(v); 126 | #ifdef DEBUG_GPIO 127 | pr_info("sniff1A: input value %d\n", (int)v); 128 | #endif 129 | return v; 130 | } 131 | 132 | EGpioValue piIoComm_readSniff1B() 133 | { 134 | if (!piDev_g.only_left_pibridge) { 135 | EGpioValue v = piIoComm_readSniff(piCore_g.gpio_sniff1b); 136 | trace_picontrol_sniffpin_1b_read(v); 137 | #ifdef DEBUG_GPIO 138 | pr_info("sniff1B: input value %d\n", (int)v); 139 | #endif 140 | return v; 141 | } 142 | return enGpioValue_Low; 143 | } 144 | 145 | EGpioValue piIoComm_readSniff2A() 146 | { 147 | EGpioValue v = piIoComm_readSniff(piCore_g.gpio_sniff2a); 148 | trace_picontrol_sniffpin_2a_read(v); 149 | #ifdef DEBUG_GPIO 150 | pr_info("sniff2A: input value %d\n", (int)v); 151 | #endif 152 | return v; 153 | } 154 | 155 | EGpioValue piIoComm_readSniff2B() 156 | { 157 | if (!piDev_g.only_left_pibridge) { 158 | EGpioValue v = piIoComm_readSniff(piCore_g.gpio_sniff2b); 159 | trace_picontrol_sniffpin_2b_read(v); 160 | #ifdef DEBUG_GPIO 161 | pr_info("sniff2B: input value %d\n", (int)v); 162 | #endif 163 | return v; 164 | } 165 | return enGpioValue_Low; 166 | } 167 | 168 | EGpioValue piIoComm_readSniff(struct gpio_desc * pGpio) 169 | { 170 | EGpioValue ret = enGpioValue_Low; 171 | 172 | if (gpiod_get_value_cansleep(pGpio)) 173 | ret = enGpioValue_High; 174 | 175 | return ret; 176 | } 177 | 178 | INT32S piIoComm_sendRS485Tel(INT16U i16uCmd_p, INT8U i8uAddress_p, 179 | INT8U * pi8uSendData_p, INT8U i8uSendDataLen_p, INT8U * pi8uRecvData_p, INT16U * pi16uRecvDataLen_p) 180 | { 181 | u16 timeout = REV_PI_IO_TIMEOUT; 182 | unsigned int rcvlen; 183 | int ret; 184 | 185 | rcvlen = (pi16uRecvDataLen_p != NULL) ? *pi16uRecvDataLen_p : 0; 186 | 187 | // this starts a flash erase on the master module 188 | // this usually take longer than the normal timeout 189 | // -> increase the timeout value to 1s 190 | if (i8uSendDataLen_p > 0 && pi8uSendData_p[0] == 'F') 191 | timeout = 1000; // ms 192 | 193 | ret = pibridge_req_gate_tmt(i8uAddress_p, i16uCmd_p, pi8uSendData_p, 194 | i8uSendDataLen_p, pi8uRecvData_p, 195 | rcvlen, timeout); 196 | if (ret != rcvlen) { 197 | if (ret >= 0) 198 | ret = -EIO; 199 | pr_info_serial("Error sending gate request: %i\n", ret); 200 | return ret; 201 | } 202 | 203 | return 0; 204 | } 205 | 206 | INT32S piIoComm_gotoGateProtocol(void) 207 | { 208 | SIOGeneric sRequest_l; 209 | int ret; 210 | 211 | sRequest_l.uHeader.sHeaderTyp2.bitCommand = IOP_TYP2_CMD_GOTO_GATE_PROTOCOL; 212 | sRequest_l.uHeader.sHeaderTyp2.bitIoHeaderType = 1; 213 | sRequest_l.uHeader.sHeaderTyp2.bitReqResp = 0; 214 | sRequest_l.uHeader.sHeaderTyp2.bitLength = 0; 215 | sRequest_l.uHeader.sHeaderTyp2.bitDataPart1 = 0; 216 | 217 | sRequest_l.ai8uData[0] = piIoComm_Crc8((INT8U *) &sRequest_l, 218 | IOPROTOCOL_HEADER_LENGTH); 219 | 220 | ret = piIoComm_send((INT8U *) &sRequest_l, IOPROTOCOL_HEADER_LENGTH + 1); 221 | if (ret == 0) { 222 | // there is no reply 223 | } else { 224 | #ifdef DEBUG_DEVICE_IO 225 | pr_info("dev all: send ioprotocol send error %d\n", ret); 226 | #endif 227 | } 228 | return 0; 229 | } 230 | 231 | void revpi_io_build_header(UIoProtocolHeader *hdr, 232 | unsigned char addr, unsigned char len, unsigned char cmd) 233 | { 234 | hdr->sHeaderTyp1.bitAddress = addr; 235 | hdr->sHeaderTyp1.bitIoHeaderType = 0; 236 | hdr->sHeaderTyp1.bitReqResp = 0; 237 | hdr->sHeaderTyp1.bitLength = len; 238 | hdr->sHeaderTyp1.bitCommand = cmd; 239 | } 240 | -------------------------------------------------------------------------------- /src/piIOComm.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include "common_define.h" 10 | #include "IoProtocol.h" 11 | 12 | #define REV_PI_IO_TIMEOUT 10 // msec 13 | 14 | enum IOSTATE { 15 | /* physically not connected */ 16 | IOSTATE_OFFLINE = 0x00, 17 | /* cyclical data exchange is active */ 18 | IOSTATE_CYCLIC_IO = 0x01, 19 | }; 20 | 21 | 22 | typedef enum _EGpioValue 23 | { 24 | enGpioValue_Low = 0, 25 | enGpioValue_High = 1 26 | } EGpioValue; 27 | typedef enum _EGpioMode 28 | { 29 | enGpioMode_Input, 30 | enGpioMode_Output, 31 | } EGpioMode; 32 | 33 | INT8U piIoComm_Crc8(INT8U *pi8uFrame_p, INT16U i16uLen_p); 34 | 35 | void piIoComm_writeSniff1A(EGpioValue eVal_p, EGpioMode eMode_p); 36 | void piIoComm_writeSniff1B(EGpioValue eVal_p, EGpioMode eMode_p); 37 | void piIoComm_writeSniff2A(EGpioValue eVal_p, EGpioMode eMode_p); 38 | void piIoComm_writeSniff2B(EGpioValue eVal_p, EGpioMode eMode_p); 39 | void piIoComm_writeSniff(struct gpio_desc *, EGpioValue eVal_p, EGpioMode eMode_p); 40 | EGpioValue piIoComm_readSniff1A(void); 41 | EGpioValue piIoComm_readSniff1B(void); 42 | EGpioValue piIoComm_readSniff2A(void); 43 | EGpioValue piIoComm_readSniff2B(void); 44 | EGpioValue piIoComm_readSniff(struct gpio_desc *); 45 | 46 | INT32S piIoComm_sendRS485Tel(INT16U i16uCmd_p, INT8U i8uAdress_p, 47 | INT8U *pi8uSendData_p, INT8U i8uSendDataLen_p, 48 | INT8U *pi8uRecvData_p, INT16U *pi16uRecvDataLen_p); 49 | 50 | INT32S piIoComm_gotoGateProtocol(void); 51 | 52 | void revpi_io_build_header(UIoProtocolHeader *hdr, 53 | unsigned char addr, unsigned char len, unsigned char cmd); 54 | int piIoComm_send(INT8U * buf_p, INT16U i16uLen_p); 55 | -------------------------------------------------------------------------------- /src/picontrol_intern.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2018-2024 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef PICONTROL_INTERN_H 6 | #define PICONTROL_INTERN_H 7 | 8 | #include "piControl.h" 9 | #include 10 | #include 11 | 12 | #define PICONFIG_FILE "/etc/revpi/config.rsc" 13 | /* address of first module on the right side of the RevPi Core */ 14 | #define REV_PI_DEV_FIRST_RIGHT 32 15 | #define PICONTROL_FIRMWARE_FORCE_UPLOAD 0x0001 16 | 17 | // the following call are for KUNBUS internal use only. 18 | /* set serial num in piDIO, piDI or piDO (can be made only once) */ 19 | #define KB_INTERN_SET_SERIAL_NUM _IO(KB_IOC_MAGIC, 100 ) 20 | /* send an I/O-Protocol message and return response */ 21 | #define KB_INTERN_IO_MSG _IO(KB_IOC_MAGIC, 101 ) 22 | 23 | typedef struct SEntryInfoStr 24 | { 25 | /* Address of module in current configuration */ 26 | u8 i8uAddress; 27 | #define ENTRY_INFO_TYPE_INPUT 1 28 | #define ENTRY_INFO_TYPE_OUTPUT 2 29 | #define ENTRY_INFO_TYPE_MEMORY 3 30 | #define ENTRY_INFO_TYPE_MASK 0x7F 31 | /* + 0x80 if exported */ 32 | u8 i8uType; 33 | /* index of I/O value for this module */ 34 | u16 i16uIndex; 35 | /* length of value in bits */ 36 | u16 i16uBitLength; 37 | /* 0-7 bit position, 0 also for whole byte */ 38 | u8 i8uBitPos; 39 | /* offset in process image */ 40 | u16 i16uOffset; 41 | /* default value */ 42 | u32 i32uDefault; 43 | /* Variable name */ 44 | char strVarName[32]; 45 | } SEntryInfo; 46 | 47 | 48 | #define PICONTROL_CONFIG_ERROR_WRONG_MODULE_TYPE -10 49 | #define PICONTROL_CONFIG_ERROR_WRONG_INPUT_LENGTH -11 50 | #define PICONTROL_CONFIG_ERROR_WRONG_OUTPUT_LENGTH -12 51 | #define PICONTROL_CONFIG_ERROR_WRONG_CONFIG_LENGTH -13 52 | #define PICONTROL_CONFIG_ERROR_WRONG_INPUT_OFFSET -14 53 | #define PICONTROL_CONFIG_ERROR_WRONG_OUTPUT_OFFSET -15 54 | #define PICONTROL_CONFIG_ERROR_WRONG_CONFIG_OFFSET -16 55 | 56 | #define PICONTROL_STATUS_RUNNING 0x01 57 | #define PICONTROL_STATUS_EXTRA_MODULE 0x02 58 | #define PICONTROL_STATUS_MISSING_MODULE 0x04 59 | #define PICONTROL_STATUS_SIZE_MISMATCH 0x08 60 | #define PICONTROL_STATUS_LEFT_GATEWAY 0x10 61 | #define PICONTROL_STATUS_RIGHT_GATEWAY 0x20 62 | /* RevPi Connect only */ 63 | #define PICONTROL_STATUS_X2_DIN 0x40 64 | #define PICONTROL_LED_A1_GREEN 0x0001 65 | #define PICONTROL_LED_A1_RED 0x0002 66 | #define PICONTROL_LED_A2_GREEN 0x0004 67 | #define PICONTROL_LED_A2_RED 0x0008 68 | /* Revpi Connect and Flat */ 69 | #define PICONTROL_LED_A3_GREEN 0x0010 70 | #define PICONTROL_LED_A3_RED 0x0020 71 | /* RevPi Connect only */ 72 | #define PICONTROL_X2_DOUT 0x0040 73 | #define PICONTROL_X2_DOUT_CONNECT4 0x0001 74 | #define PICONTROL_WD_TRIGGER 0x0080 75 | /* Revpi Flat only */ 76 | #define PICONTROL_LED_A4_GREEN 0x0040 77 | #define PICONTROL_LED_A4_RED 0x0080 78 | #define PICONTROL_LED_A5_GREEN 0x0100 79 | #define PICONTROL_LED_A5_RED 0x0200 80 | /* RevPi Connect 4, 5 */ 81 | #define PICONTROL_LED_RGB_A1_RED 0x0001 82 | #define PICONTROL_LED_RGB_A1_GREEN 0x0002 83 | #define PICONTROL_LED_RGB_A1_BLUE 0x0004 84 | #define PICONTROL_LED_RGB_A2_RED 0x0008 85 | #define PICONTROL_LED_RGB_A2_GREEN 0x0010 86 | #define PICONTROL_LED_RGB_A2_BLUE 0x0020 87 | #define PICONTROL_LED_RGB_A3_RED 0x0040 88 | #define PICONTROL_LED_RGB_A3_GREEN 0x0080 89 | #define PICONTROL_LED_RGB_A3_BLUE 0x0100 90 | #define PICONTROL_LED_RGB_A4_RED 0x0200 91 | #define PICONTROL_LED_RGB_A4_GREEN 0x0400 92 | #define PICONTROL_LED_RGB_A4_BLUE 0x0800 93 | #define PICONTROL_LED_RGB_A5_RED 0x1000 94 | #define PICONTROL_LED_RGB_A5_GREEN 0x2000 95 | #define PICONTROL_LED_RGB_A5_BLUE 0x4000 96 | 97 | #endif /* PICONTROL_INTERN_H */ 98 | -------------------------------------------------------------------------------- /src/picontrol_ioctl.4: -------------------------------------------------------------------------------- 1 | .TH PICONTROL_IOCTL 4 2018-01-23 "Linux" "Revolution Pi Programmer's Manual" 2 | .SH NAME 3 | picontrol_ioctl \- ioctls for piControl driver 4 | .SH SYNOPSIS 5 | .B "#include " 6 | .sp 7 | .BI "int ioctl(int " fd ", int " cmd ", ...);" 8 | .SH DESCRIPTION 9 | The 10 | .BR ioctl (2) 11 | call for piControl accepts many possible command arguments. 12 | Most require a third argument, of varying type, here called 13 | .I argp 14 | or 15 | .IR arg . 16 | .LP 17 | .SS Get information from the configuration file 18 | The web application PiCtory is used to configure the I/O- and virtual modules. It writes the configuration to the file 19 | .IR /etc/revpi/config.rsc . 20 | The piControl driver reads this file when it is started. Most applications can use the following commands to retrieve 21 | some information from configuration. Then they do not need to parse the configuration file themself. 22 | .TP 23 | .BI "KB_GET_DEVICE_INFO_LIST SDeviceInfo *" argp 24 | This call provides information about the configured and connected modules. A pointer to an array of 64 entries of type 25 | .I SDeviceInfo 26 | must be passed as argument. 27 | .br 28 | The return value is the number of entries filled the array. 29 | .TP 30 | .BI "KB_GET_DEVICE_INFO SDeviceInfo *" argp 31 | This call provides information about one module. A pointer to a structure of type 32 | .I SDeviceInfo 33 | must be passed as argument. Ether the element 34 | .I i8uAddress 35 | must be set to the address of the module which information should be returned. Or the element 36 | .I i16uModuleType 37 | must be set to the type of the module. In this case it it only possible to 38 | retireve informations about the first module of that typ in the confiugration. 39 | The available types are defined in with the defines 40 | .I KUNBUS_FW_DESCR_TYP_... 41 | in the file 42 | .IR common_define.h . 43 | 44 | 45 | .LP 46 | The struct 47 | .I SDeviceInfo 48 | used by this ioctls is defined as 49 | 50 | .in +4n 51 | .nf 52 | typedef struct SDeviceInfoStr { 53 | uint8_t i8uAddress; // Address of module in current configuration 54 | uint32_t i32uSerialnumber; // serial number of module 55 | uint16_t i16uModuleType; // Type identifier of module 56 | uint16_t i16uHW_Revision; // hardware revision 57 | uint16_t i16uSW_Major; // major software version 58 | uint16_t i16uSW_Minor; // minor software version 59 | uint32_t i32uSVN_Revision; // svn revision of software 60 | uint16_t i16uInputLength; // length in bytes of all input values together 61 | uint16_t i16uOutputLength; // length in bytes of all output values together 62 | uint16_t i16uConfigLength; // length in bytes of all config values together 63 | uint16_t i16uBaseOffset; // offset in process image 64 | uint16_t i16uInputOffset; // offset in process image of first input byte 65 | uint16_t i16uOutputOffset; // offset in process image of first output byte 66 | uint16_t i16uConfigOffset; // offset in process image of first config byte 67 | uint16_t i16uFirstEntry; // index of entry 68 | uint16_t i16uEntries; // number of entries in process image 69 | uint8_t i8uModuleState; // fieldbus state of piGate Module 70 | uint8_t i8uActive; // == 0 means that the module is not present and no data is available 71 | uint8_t i8uReserve[30]; // space for future extensions without changing the size of the struct 72 | } SDeviceInfo; 73 | .fi 74 | .in 75 | 76 | .TP 77 | .BI "KB_FIND_VARIABLE SPIVariable *" argp 78 | Find a variable in the process image by its name. A pointer to a structure of type 79 | .I SPIVariable 80 | must be passed as argument. Before the call the name of the variable must be written as zero terminated 81 | string to the element 82 | .IR strVarName . 83 | After a successful call 84 | .I i16uAddress 85 | is set to the offset of the variable in the process image. 86 | .I i16uLength 87 | if the length of the value in bits. It can be 1, 8, 16 or 32. 88 | If it is one, 89 | .I i8uBit 90 | tells the bit position [0..7] in the byte. For other values of 91 | .I i16uLength 92 | , 93 | .I i8uBit 94 | is not used. 2 and 4 byte values are stored in little endian byte order. 95 | The address is not alligned to a mutiple of the variable length. 96 | 97 | The struct 98 | .I SPIVariable 99 | used by this ioctl is defined as 100 | 101 | .in +4n 102 | .nf 103 | typedef struct SPIVariableStr 104 | { 105 | char strVarName[32]; // Variable name 106 | uint16_t i16uAddress; // Address of the byte in the process image 107 | uint8_t i8uBit; // 0-7 bit position, >= 8 whole byte 108 | uint16_t i16uLength; // length of the variable in bits. 109 | // Possible values are 1, 8, 16 and 32 110 | } SPIVariable; 111 | .fi 112 | .in 113 | 114 | .LP 115 | .SS Set and get values of the process image 116 | .TP 117 | .BI "KB_GET_VALUE SPIValue *" argp 118 | Read one bit or one byte from the process image. 119 | Before the call the elements 120 | .I i16uAddress 121 | and 122 | .I i8uBit 123 | must be set to the address of the value. 124 | This call is more efficient than the usual calls of seek and read because only one function call is necessary. 125 | .TP 126 | .BI "KB_SET_VALUE SPIValue *" argp 127 | Write one bit or one byte to the process image. 128 | Before the call the elements 129 | .I i16uAddress 130 | and 131 | .I i8uBit 132 | must be set to the address of the value. 133 | .I i8uValue 134 | must set to the value to write. 135 | This call is more efficient than the usual calls of seek and write because only one function call is necessary. 136 | If more than on application are writing bits in one output byte, this call is the only safe way to set a bit 137 | without overwriting the other bits because this call is doning a read-modify-write-cycle. 138 | 139 | The struct 140 | .I SPIValue 141 | used by this ioctl is defined as 142 | 143 | .in +4n 144 | .nf 145 | typedef struct SPIValueStr 146 | { 147 | uint16_t i16uAddress; // Address of the byte in the process image 148 | uint8_t i8uBit; // 0-7 bit position, >= 8 whole byte 149 | uint8_t i8uValue; // Value: 0/1 for bit access, whole byte otherwise 150 | } SPIValue; 151 | .fi 152 | .in 153 | 154 | .TP 155 | .BI "KB_SET_EXPORTED_OUTPUTS const void *" argp 156 | Write all output values to the hardware at once. 157 | .br 158 | This call is used by the main application controlling the outputs. 159 | The application must have a complete copy of the process image. It sets the output values in its own copy and calls 160 | this ioctl with a pointer to the image as arguments. This call locks the process image and copies all output values where the 161 | .BR export 162 | checkmark is set in PiCtory. Afterwards piControl transfers the values to the I/O modules. 163 | In the current version only one application should call this ioctl. 164 | 165 | .TP 166 | .BI "KB_DIO_RESET_COUNTER SDIOResetCounter *" argp 167 | Reset counters and encoders to 0 in an input module. 168 | .br 169 | Inputs of DIO and DI modules can be configured as counters or encoders in PiCtroy. After a reset they start at 0. 170 | This call can be used set one or more of the values to 0 at the same time. 171 | .br 172 | The argument must be pointer of a structure of type 173 | .IR SDIOResetCounter . 174 | The element 175 | .I i8uAddress 176 | must be set to the address of DIO or DI module as shown in PiCtory, e. g. 32 if the DIO is on the right of the RevPi. 177 | .br 178 | The element 179 | .I i16uBitfield 180 | defines which counters will be reset. If a counter is configured on input I_3 and an encoder is configured on the 181 | inputs I_6 and I_7, the bits 2 and 5 must be set to 1, i16uBitfield must be set to the value 0x0024. 182 | 183 | The struct 184 | .I SDIOResetCounter 185 | used by this ioctl is defined as 186 | 187 | .in +4n 188 | .nf 189 | typedef struct SDIOResetCounterStr 190 | { 191 | uint8_t i8uAddress; // Address of module in current configuration 192 | uint16_t i16uBitfield; // bitfield, if bit n is 1, reset counter/encoder on input n 193 | } SDIOResetCounter; 194 | .fi 195 | .in 196 | 197 | .TP 198 | .BI "KB_SET_EXPORTED_OUTPUTS const void *" argp 199 | Write exported output values to the real outputs. 200 | .br 201 | The main control application (e.g. a SoftPLC) running on the RevPi can use this function to set all output values at one 202 | time. It is very similar to write, but only the values for which the export checkmark is set in PiCtory are set. This allows 203 | other applications to control other outputs. 204 | The argument pointer must point to a copy of the complete process image. 205 | The typical usage is to read the complete process image with read(), change to output values for this cycle and write the 206 | marked output values back to the process image. 207 | 208 | .in +4n 209 | .nf 210 | unsigned char PI[4096]; 211 | \.\.\. 212 | read(fd, PI, 4096); 213 | /* change PI */ 214 | ioctl(fd, KB_SET_EXPORTED_OUTPUTS, PI); 215 | .fi 216 | .in 217 | 218 | .TP 219 | .BI "KB_SET_OUTPUT_WATCHDOG unsigned int *" argp 220 | Activate an application watchdog. 221 | .br 222 | The argument is a pointer to the watchdog period in milliseconds. After setting this period value, the write function must be called in 223 | shorter periods for this file handle. If it is called within the period, all output value are set to 0 in the piControl driver. 224 | .br 225 | The watchdog can be deactivated by setting the period to 0 or closing the file handle. 226 | 227 | .TP 228 | .BI "KB_RO_GET_COUNTER struct revpi_ro_ioctl_counters *" argp 229 | .br 230 | Get the counter values for all relays of an RO module. The address of the RO module must be set in the 231 | .I addr 232 | field in the 233 | .I revpi_ro_ioctl_counters 234 | struct before calling. All relay counters will be written to the 235 | .I counter 236 | field of the struct. 237 | 238 | The struct 239 | .I revpi_ro_ioctl_counters 240 | used by this ioctl is defined as 241 | 242 | .in +4n 243 | .nf 244 | struct revpi_ro_ioctl_counters { 245 | /* Address of module in current configuration, set by userspace. */ 246 | uint8_t addr; 247 | /* Data returned from kernel */ 248 | uint32_t counter[REVPI_RO_NUM_RELAYS]; 249 | } __attribute__((__packed__)); 250 | .fi 251 | .in 252 | 253 | 254 | .LP 255 | .SS Driver Control 256 | 257 | .TP 258 | .BI "KB_WAIT_FOR_EVENT int *" argp 259 | Wait for a event from the piControl driver. 260 | .br 261 | This is a blocking call. It waits until an event occurs in the piControl driver. The number of the event is writte to the arument pointer. 262 | .br 263 | At the moment there is only a reset event. It is sent to all waiting applications, if a client calls the ioctl 264 | .BR KB_RESET . 265 | The application has to stop its execution and update the offsets of the variables in the process image. Here is a small example: 266 | 267 | .in +4n 268 | .nf 269 | int event; 270 | while (1) 271 | { 272 | // get variable offsets from piControl 273 | // start application 274 | do { 275 | if (ioctl(fd, KB_WAIT_FOR_EVENT, &event) < 0) 276 | exit(-1); 277 | } while (event != KB_EVENT_RESET); 278 | //stop application 279 | } 280 | .fi 281 | .in 282 | 283 | 284 | .TP 285 | .BI "KB_RESET void" 286 | Stop the communication with the I/O modules, reset to all of them, scan for the connected modules, read the configuration file created with 287 | .BR PiCtory , 288 | initialize the modules with the new configuration and restart the communication. All counters are set to 0 and all the outputs to their default 289 | values as defined in 290 | .BR PiCtory . 291 | 292 | .TP 293 | .BI "KB_STOP_IO int *" argp 294 | Stop or start the I/O communication. 295 | .br 296 | This ioctl stops, starts or toggles the update of I/Os. If the I/O updates are stopped, 297 | piControls writes 0 to the outputs instead of the values from the process image. 298 | The input values are not written to the process images. The I/O communication is 299 | runnging as normal. On the update of DIO, DI, DO, AIO, Gate modules and the RevPi 300 | itself is stopped. There is no change in the handling of virtual modules. 301 | The function can used for simulation of I/Os. A simulation application can be started 302 | additionally to the other control and application processes. It stops the I/O update 303 | and simulates the hardware by setting and reading the values in the process image. 304 | The application does not notice this. 305 | .br 306 | The argument is a pointer to an integer variable. 307 | .br 308 | If stop has the value 0, the communication is started. 309 | .br 310 | If it is 1, the communicatio is stopped. 311 | .br 312 | 2 will toggle the current mode. 313 | .br 314 | The return value is the new mode, 0 for running, 1 for stopped, <0 in case of an error. 315 | 316 | 317 | .TP 318 | .BI "KB_UPDATE_DEVICE_FIRMWARE unsigned int" arg 319 | Update firmware 320 | .br 321 | KUNBUS provides 322 | .BR .fwu 323 | files with new firmware for RevPi I/O and RevPi Gate modules. 324 | These are provided in the debian packet revpi-firmware. Use 325 | .IR "sudo apt-get install revpi-firmware" 326 | to get the latest firmware files. Afterwards you can update the firmware with this ioctl call. 327 | Unforunatelly old modules hang or block the piBridge communication, if a modules is updated. 328 | Therefore the update is only possible when only one module is connected to the RevPi. 329 | The module must be on the right side of the RevPi Core and on the left side of the RevPi Connect. 330 | This ioctl reads the version number from the module and compares it to the lastet available 331 | firmware file. If a new firmware is available, it is flashed to the module. 332 | .br 333 | The argument is the address of module to update. If it is 0, the module to update will be selected automatically. 334 | 335 | 336 | .TP 337 | .BI "KB_GET_LAST_MESSAGE char *" argp 338 | Get a message from the last ioctl call. 339 | .br 340 | Check, if the last ioctl() call produced a message and copy it to the buffer. 341 | 342 | .in +4n 343 | .nf 344 | char cMsg[REV_PI_ERROR_MSG_LEN]; 345 | 346 | if (ioctl(PiControlHandle_g, KB_GET_LAST_MESSAGE, cMsg) == 0 && cMsg[0]) 347 | puts(cMsg); 348 | .fi 349 | .in 350 | 351 | 352 | .SH SEE ALSO 353 | .BR ioctl (2) 354 | .SH COLOPHON 355 | A description of the project 356 | and further information can be found at 357 | \%https://revolution.kunbus.de/forum/ 358 | 359 | -------------------------------------------------------------------------------- /src/picontrol_trace.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2024 KUNBUS GmbH 3 | */ 4 | 5 | #undef TRACE_SYSTEM 6 | #define TRACE_SYSTEM picontrol 7 | 8 | #include 9 | 10 | #if !defined(_PICONTROL_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) 11 | #define _PICONTROL_TRACE_H 12 | 13 | 14 | /* 15 | * picontrol_cycle_start 16 | * 17 | * Info: The beginning of a cycle of data exchange with all devices. 18 | * cycle: The current cycle. 19 | * Time: At the beginning of a cycle of data exchange with all devices. 20 | */ 21 | TRACE_EVENT(picontrol_cycle_start, 22 | TP_PROTO(u64 cycle), 23 | TP_ARGS(cycle), 24 | TP_STRUCT__entry( 25 | __field(u64, cycle) 26 | ), 27 | TP_fast_assign( 28 | __entry->cycle = cycle; 29 | ), 30 | TP_printk( 31 | "cycle=%llu ", 32 | __entry->cycle 33 | ) 34 | ); 35 | 36 | /* 37 | * picontrol_cycle_end 38 | * 39 | * Info: The end of a cycle of data exchange with all devices. 40 | * cycle: The current cycle. 41 | * duration: The duration of the current cycle. 42 | * Time: At the end of a cycle of data exchange with all devices. 43 | */ 44 | TRACE_EVENT(picontrol_cycle_end, 45 | TP_PROTO(u64 cycle, unsigned int duration), 46 | TP_ARGS(cycle, duration), 47 | TP_STRUCT__entry( 48 | __field(u64, cycle) 49 | __field(unsigned int, duration) 50 | ), 51 | TP_fast_assign( 52 | __entry->cycle = cycle; 53 | __entry->duration = duration; 54 | ), 55 | TP_printk( 56 | "cycle=%llu, duration=%u usecs", 57 | __entry->cycle, 58 | __entry->duration 59 | ) 60 | ); 61 | 62 | /* 63 | * picontrol_cycle_exceeded 64 | * 65 | * Info: The cycle equals a certain time span. 66 | * exceeded: The current cycle length. 67 | * min: The current (measured) min cycle length. 68 | * Time: At the end of a data cycle if the current cycle exceeds a certain time 69 | * span. 70 | */ 71 | TRACE_EVENT(picontrol_cycle_exceeded, 72 | TP_PROTO(unsigned int exceeded, unsigned int min), 73 | TP_ARGS(exceeded, min), 74 | TP_STRUCT__entry( 75 | __field(unsigned int, exceeded) 76 | __field(unsigned int, min) 77 | ), 78 | TP_fast_assign( 79 | __entry->exceeded = exceeded; 80 | __entry->min = min; 81 | ), 82 | TP_printk("Cycle time exceeded: %u usecs (min: %u usecs)", 83 | __entry->exceeded, 84 | __entry->min 85 | ) 86 | ); 87 | 88 | /* 89 | * picontrol_cyclic_device_data_class 90 | * 91 | * Print the address of the device involved in data exchange. 92 | * 93 | * addr: the device address. 94 | */ 95 | DECLARE_EVENT_CLASS(picontrol_cyclic_device_data_class, 96 | TP_PROTO(unsigned int addr), 97 | TP_ARGS(addr), 98 | TP_STRUCT__entry( 99 | __field(unsigned int, addr) 100 | ), 101 | TP_fast_assign( 102 | __entry->addr = addr; 103 | ), 104 | TP_printk( 105 | "Data exchange dev %u", 106 | __entry->addr 107 | ) 108 | ); 109 | 110 | /* 111 | * picontrol_cyclic_device_data_start 112 | * 113 | * Info: The device starting to exchange data. 114 | * Time: Before a device starts to exchange data in this cycle. 115 | */ 116 | DEFINE_EVENT(picontrol_cyclic_device_data_class, picontrol_cyclic_device_data_start, 117 | TP_PROTO(unsigned int addr), 118 | TP_ARGS(addr) 119 | ); 120 | 121 | /* 122 | * picontrol_cyclic_device_data_stop 123 | * 124 | * Info: The device stopping to exchange data 125 | * Time: After a device stopped to exchange data in this cycle. 126 | */ 127 | DEFINE_EVENT(picontrol_cyclic_device_data_class, picontrol_cyclic_device_data_stop, 128 | TP_PROTO(unsigned int addr), 129 | TP_ARGS(addr) 130 | ); 131 | 132 | DECLARE_EVENT_CLASS(picontrol_sniffpin_value_class, 133 | TP_PROTO(unsigned int value), 134 | TP_ARGS(value), 135 | TP_STRUCT__entry( 136 | __field(unsigned int, value) 137 | ), 138 | TP_fast_assign( 139 | __entry->value = value; 140 | ), 141 | TP_printk("Sniff-Pin value: %u", 142 | __entry->value 143 | ) 144 | ); 145 | 146 | DEFINE_EVENT(picontrol_sniffpin_value_class, picontrol_sniffpin_1a_read, 147 | TP_PROTO(unsigned int value), 148 | TP_ARGS(value) 149 | ); 150 | 151 | DEFINE_EVENT(picontrol_sniffpin_value_class, picontrol_sniffpin_2a_read, 152 | TP_PROTO(unsigned int value), 153 | TP_ARGS(value) 154 | ); 155 | 156 | DEFINE_EVENT(picontrol_sniffpin_value_class, picontrol_sniffpin_1b_read, 157 | TP_PROTO(unsigned int value), 158 | TP_ARGS(value) 159 | ); 160 | 161 | DEFINE_EVENT(picontrol_sniffpin_value_class, picontrol_sniffpin_2b_read, 162 | TP_PROTO(unsigned int value), 163 | TP_ARGS(value) 164 | ); 165 | 166 | DEFINE_EVENT(picontrol_sniffpin_value_class, picontrol_sniffpin_1a_set, 167 | TP_PROTO(unsigned int value), 168 | TP_ARGS(value) 169 | ); 170 | 171 | DEFINE_EVENT(picontrol_sniffpin_value_class, picontrol_sniffpin_2a_set, 172 | TP_PROTO(unsigned int value), 173 | TP_ARGS(value) 174 | ); 175 | 176 | DEFINE_EVENT(picontrol_sniffpin_value_class, picontrol_sniffpin_1b_set, 177 | TP_PROTO(unsigned int value), 178 | TP_ARGS(value) 179 | ); 180 | 181 | DEFINE_EVENT(picontrol_sniffpin_value_class, picontrol_sniffpin_2b_set, 182 | TP_PROTO(unsigned int value), 183 | TP_ARGS(value) 184 | ); 185 | 186 | #endif /* _PICONTROL_TRACE_H */ 187 | 188 | /* This part must be outside protection */ 189 | #undef TRACE_INCLUDE_PATH 190 | #define TRACE_INCLUDE_PATH . 191 | #undef TRACE_INCLUDE_FILE 192 | #define TRACE_INCLUDE_FILE picontrol_trace 193 | #include 194 | -------------------------------------------------------------------------------- /src/process_image.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2017-2024 KUNBUS GmbH 3 | * 4 | * process_image.h - cyclic update of the process image 5 | */ 6 | 7 | #ifndef _PROCESS_IMAGE_H 8 | #define _PROCESS_IMAGE_H 9 | 10 | #define REVPI_COMPACT_WARN_MISSED_CYCLES 10 11 | 12 | #include 13 | #include 14 | #include 15 | #include "revpi_compact.h" 16 | 17 | struct cycletimer { 18 | struct hrtimer timer; 19 | ktime_t cycletime; 20 | struct completion timer_expired; 21 | }; 22 | 23 | static inline enum hrtimer_restart wake_up_sleeper(struct hrtimer *timer) 24 | { 25 | struct cycletimer *ct = container_of(timer, struct cycletimer, timer); 26 | complete(&ct->timer_expired); 27 | return HRTIMER_NORESTART; 28 | } 29 | 30 | static inline void cycletimer_sleep(struct cycletimer *ct, 31 | struct revpi_compact_stats *stats) 32 | { 33 | struct hrtimer *timer = &ct->timer; 34 | u64 missed_cycles = hrtimer_forward_now(timer, ct->cycletime); 35 | 36 | if (missed_cycles == 0) /* should never happen (TM) */ 37 | pr_warn("%s: premature cycle\n", current->comm); 38 | else if (missed_cycles > 1) { 39 | write_seqlock(&stats->lock); 40 | stats->lost_cycles += (missed_cycles - 1); 41 | write_sequnlock(&stats->lock); 42 | 43 | if (missed_cycles > REVPI_COMPACT_WARN_MISSED_CYCLES) { 44 | pr_warn("%s: missed %lld cycles\n", current->comm, 45 | missed_cycles - 1); 46 | } 47 | } 48 | 49 | reinit_completion(&ct->timer_expired); 50 | hrtimer_start_expires(timer, HRTIMER_MODE_ABS_HARD); 51 | wait_for_completion(&ct->timer_expired); 52 | } 53 | 54 | static inline void cycletimer_change(struct cycletimer *ct, u32 cycletime) 55 | { 56 | struct hrtimer *timer = &ct->timer; 57 | 58 | ct->cycletime = ns_to_ktime(cycletime); 59 | hrtimer_cancel(timer); 60 | hrtimer_set_expires(timer, hrtimer_cb_get_time(timer)); 61 | } 62 | 63 | static inline void cycletimer_init_on_stack(struct cycletimer *ct, u32 cycletime) 64 | { 65 | struct hrtimer *timer = &ct->timer; 66 | 67 | hrtimer_init_on_stack(timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD); 68 | timer->function = wake_up_sleeper; 69 | init_completion(&ct->timer_expired); 70 | cycletimer_change(ct, cycletime); 71 | } 72 | 73 | static inline void cycletimer_destroy(struct cycletimer *ct) 74 | { 75 | hrtimer_cancel(&ct->timer); 76 | destroy_hrtimer_on_stack(&ct->timer); 77 | } 78 | 79 | static __always_inline void assign_bit_in_byte(u8 nr, u8 * addr, bool value) 80 | { 81 | if (value) 82 | *addr |= BIT(nr); 83 | else 84 | *addr &= ~BIT(nr); 85 | } 86 | 87 | static __always_inline int test_bit_in_byte(u8 nr, u8 * addr) 88 | { 89 | return (*addr >> nr) & 1; 90 | } 91 | 92 | #define flip_process_image(shadow, offset) \ 93 | { \ 94 | if (!test_bit(PICONTROL_DEV_FLAG_STOP_IO, &piDev_g.flags)) { \ 95 | if (((typeof(shadow))(piDev_g.ai8uPI + (offset))) == 0 || (shadow) == 0) \ 96 | pr_err("NULL pointer: %p %p\n", ((typeof(shadow))(piDev_g.ai8uPI + (offset))), (shadow)); \ 97 | my_rt_mutex_lock(&piDev_g.lockPI); \ 98 | ((typeof(shadow))(piDev_g.ai8uPI + (offset)))->drv = (shadow)->drv; \ 99 | (shadow)->usr = ((typeof(shadow))(piDev_g.ai8uPI + (offset)))->usr; \ 100 | rt_mutex_unlock(&piDev_g.lockPI); \ 101 | } \ 102 | } 103 | #endif /* _PROCESS_IMAGE_H */ 104 | -------------------------------------------------------------------------------- /src/project.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2016-2024 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef BSPCONFIG_H_INC 6 | #define BSPCONFIG_H_INC 7 | 8 | //#define ENDTEST_DIO 9 | 10 | #if 0 11 | #define DEBUG_CONFIG 12 | #define pr_info_config(fmt, ...) pr_info(fmt, ##__VA_ARGS__) 13 | #else 14 | #define pr_info_config(fmt, ...) 15 | #endif 16 | 17 | #if 0 18 | #define DEBUG_MASTER_STATE 19 | #define pr_info_master(fmt, ...) pr_info(fmt, ##__VA_ARGS__) 20 | #define pr_info_master2(fmt, ...) pr_info(fmt, ##__VA_ARGS__) 21 | #elif 1 22 | #define DEBUG_MASTER_STATE 23 | #define pr_info_master(fmt, ...) pr_info(fmt, ##__VA_ARGS__) 24 | #define pr_info_master2(fmt, ...) 25 | #else 26 | #define pr_info_master(fmt, ...) 27 | #define pr_info_master2(fmt, ...) 28 | #endif 29 | 30 | #if 0 31 | #define DEBUG_DEVICE_DIO 32 | #define pr_info_dio(fmt, ...) pr_info(fmt, ##__VA_ARGS__) 33 | #else 34 | #define pr_info_dio(fmt, ...) 35 | #endif 36 | 37 | #if 0 38 | #define DEBUG_DEVICE_AIO 39 | #define pr_info_aio(fmt, ...) pr_info(fmt, ##__VA_ARGS__) 40 | #else 41 | #define pr_info_aio(fmt, ...) 42 | #endif 43 | 44 | #if 0 45 | #define DEBUG_LINUX_DRV 46 | #define pr_info_drv(fmt, ...) pr_info(fmt, ##__VA_ARGS__) 47 | #else 48 | #define pr_info_drv(fmt, ...) 49 | #endif 50 | 51 | #if 0 52 | #define DEBUG_SERIALCOMM 53 | #define pr_info_serial(fmt, ...) pr_info(fmt, ##__VA_ARGS__) 54 | #else 55 | #define pr_info_serial(fmt, ...) pr_info(fmt, ##__VA_ARGS__) 56 | #endif 57 | 58 | //#define DEBUG_GPIO 59 | #define DEBUG_DEVICE 60 | #define DEBUG_DEVICE_IO 61 | 62 | #ifdef DEBUG_SERIALCOMM 63 | // use longer intervals to reduce the number of messages 64 | #define INTERVAL_RS485 ( 1*1000*1000) // 100 ms piRs485 65 | #define INTERVAL_IO_COMM ( 5*1000*1000) // 500 ms piIoComm 66 | #define INTERVAL_ADDITIONAL ( 500*1000) // 500 ms piIoComm 67 | #else 68 | #define INTERVAL_RS485 ( 1*1000*1000) // 1 ms piRs485 69 | #define INTERVAL_IO_COMM ( 5*1000*1000) // 5 ms piIoComm 70 | #define INTERVAL_ADDITIONAL ( 500*1000) // 0.5 ms piIoComm 71 | #endif 72 | 73 | #define KB_PD_LEN (u16)512 74 | #define KB_PI_LEN 4096 75 | 76 | //#define VERBOSE 77 | 78 | #undef pr_fmt 79 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 80 | 81 | #endif // BSPCONFIG_H_INC 82 | -------------------------------------------------------------------------------- /src/pt100.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // SPDX-FileCopyrightText: 2017 KUNBUS GmbH 3 | 4 | // pt100.c - PT100 resistance to temperature conversion 5 | 6 | #include "pt100.h" 7 | #include 8 | 9 | static const u16 ai16uPtTable_l[] = 10 | { 11 | #include "pt100_table.inc" 12 | }; 13 | 14 | int GetPt100Temperature(unsigned int i16uResistance_p, 15 | signed int *pi16sTemperature_p) 16 | { 17 | int i16uLow_l = 0; 18 | int i16uHigh_l = ARRAY_SIZE(ai16uPtTable_l) - 1; 19 | int i16uSearchIndex_l; 20 | int i16uFullDiff_l; 21 | int i16uDiff_l; 22 | 23 | // resistance too low?? 24 | if (i16uResistance_p < ai16uPtTable_l[i16uLow_l]) 25 | { 26 | *pi16sTemperature_p = -2000; 27 | return -1; 28 | } 29 | 30 | // resistance too high?? 31 | if (i16uResistance_p > ai16uPtTable_l[i16uHigh_l]) 32 | { 33 | *pi16sTemperature_p = 8500; 34 | return 1; 35 | } 36 | 37 | // binary search in PT100 table 38 | i16uSearchIndex_l = (i16uLow_l + i16uHigh_l) / 2; 39 | while (i16uLow_l != i16uSearchIndex_l) 40 | { 41 | if (i16uResistance_p < ai16uPtTable_l[i16uSearchIndex_l]) 42 | { 43 | i16uHigh_l = i16uSearchIndex_l; 44 | } 45 | else 46 | { 47 | i16uLow_l = i16uSearchIndex_l; 48 | } 49 | i16uSearchIndex_l = (i16uLow_l + i16uHigh_l) / 2; 50 | } 51 | 52 | // Interpolation 53 | i16uFullDiff_l = ai16uPtTable_l[i16uSearchIndex_l + 1] - ai16uPtTable_l[i16uSearchIndex_l]; 54 | i16uDiff_l = i16uResistance_p - ai16uPtTable_l[i16uSearchIndex_l]; 55 | *pi16sTemperature_p = (i16uSearchIndex_l - 200) * 10 + i16uDiff_l * 10 / i16uFullDiff_l; 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /src/pt100.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2017 KUNBUS GmbH 3 | */ 4 | 5 | int GetPt100Temperature(unsigned int i16uResistance_p, 6 | signed int *pi16sTemperature_p); 7 | -------------------------------------------------------------------------------- /src/pt100_table.inc: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2017 KUNBUS GmbH 3 | */ 4 | 5 | // Berechnung: 6 | // 7 | // for temperature range t = 0 … 850 °C 8 | // R(t) = R0 * ( 1 + At + Bt^2 ) 9 | // 10 | // for temperature range t = −200 … 0 °C 11 | // R(t) = R0 * ( 1 + At + Bt^2 + C(t − 100)t^3 ) 12 | // mit 13 | // A = 3,9083e−3 14 | // B = −5,775e−7 15 | // C = −4,183e−12 16 | // 17 | // table values are round(R(t)) * 100 18 | // 19 | 1852, 1895, 1938, 1982, 2025, 2068, 2111, 2154, 2197, 2240, // -200 °C 20 | 2283, 2325, 2368, 2411, 2454, 2497, 2539, 2582, 2624, 2667, // -190 °C 21 | 2710, 2752, 2795, 2837, 2880, 2922, 2964, 3007, 3049, 3091, // -180 °C 22 | 3134, 3176, 3218, 3260, 3302, 3344, 3386, 3428, 3470, 3512, // -170 °C 23 | 3554, 3596, 3638, 3680, 3722, 3764, 3805, 3847, 3889, 3931, // -160 °C 24 | 3972, 4014, 4056, 4097, 4139, 4180, 4222, 4263, 4305, 4346, // -150 °C 25 | 4388, 4429, 4470, 4512, 4553, 4594, 4636, 4677, 4718, 4759, // -140 °C 26 | 4800, 4842, 4883, 4924, 4965, 5006, 5047, 5088, 5129, 5170, // -130 °C 27 | 5211, 5252, 5293, 5334, 5375, 5415, 5456, 5497, 5538, 5579, // -120 °C 28 | 5619, 5660, 5701, 5741, 5782, 5823, 5863, 5904, 5944, 5985, // -110 °C 29 | 6026, 6066, 6107, 6147, 6188, 6228, 6268, 6309, 6349, 6390, // -100 °C 30 | 6430, 6470, 6511, 6551, 6591, 6631, 6672, 6712, 6752, 6792, // -90 °C 31 | 6833, 6873, 6913, 6953, 6993, 7033, 7073, 7113, 7153, 7193, // -80 °C 32 | 7233, 7273, 7313, 7353, 7393, 7433, 7473, 7513, 7553, 7593, // -70 °C 33 | 7633, 7673, 7712, 7752, 7792, 7832, 7872, 7911, 7951, 7991, // -60 °C 34 | 8031, 8070, 8110, 8150, 8189, 8229, 8269, 8308, 8348, 8387, // -50 °C 35 | 8427, 8467, 8506, 8546, 8585, 8625, 8664, 8704, 8743, 8783, // -40 °C 36 | 8822, 8862, 8901, 8940, 8980, 9019, 9059, 9098, 9137, 9177, // -30 °C 37 | 9216, 9255, 9295, 9334, 9373, 9412, 9452, 9491, 9530, 9569, // -20 °C 38 | 9609, 9648, 9687, 9726, 9765, 9804, 9844, 9883, 9922, 9961, // -10 °C 39 | 10000, 10039, 10078, 10117, 10156, 10195, 10234, 10273, 10312, 10351, // 0 °C 40 | 10390, 10429, 10468, 10507, 10546, 10585, 10624, 10663, 10702, 10740, // 10 °C 41 | 10779, 10818, 10857, 10896, 10935, 10973, 11012, 11051, 11090, 11129, // 20 °C 42 | 11167, 11206, 11245, 11283, 11322, 11361, 11400, 11438, 11477, 11515, // 30 °C 43 | 11554, 11593, 11631, 11670, 11708, 11747, 11786, 11824, 11863, 11901, // 40 °C 44 | 11940, 11978, 12017, 12055, 12094, 12132, 12171, 12209, 12247, 12286, // 50 °C 45 | 12324, 12363, 12401, 12439, 12478, 12516, 12554, 12593, 12631, 12669, // 60 °C 46 | 12708, 12746, 12784, 12822, 12861, 12899, 12937, 12975, 13013, 13052, // 70 °C 47 | 13090, 13128, 13166, 13204, 13242, 13280, 13318, 13357, 13395, 13433, // 80 °C 48 | 13471, 13509, 13547, 13585, 13623, 13661, 13699, 13737, 13775, 13813, // 90 °C 49 | 13851, 13888, 13926, 13964, 14002, 14040, 14078, 14116, 14154, 14191, // 100 °C 50 | 14229, 14267, 14305, 14343, 14380, 14418, 14456, 14494, 14531, 14569, // 110 °C 51 | 14607, 14644, 14682, 14720, 14757, 14795, 14833, 14870, 14908, 14946, // 120 °C 52 | 14983, 15021, 15058, 15096, 15133, 15171, 15208, 15246, 15283, 15321, // 130 °C 53 | 15358, 15396, 15433, 15471, 15508, 15546, 15583, 15620, 15658, 15695, // 140 °C 54 | 15733, 15770, 15807, 15845, 15882, 15919, 15956, 15994, 16031, 16068, // 150 °C 55 | 16105, 16143, 16180, 16217, 16254, 16291, 16329, 16366, 16403, 16440, // 160 °C 56 | 16477, 16514, 16551, 16589, 16626, 16663, 16700, 16737, 16774, 16811, // 170 °C 57 | 16848, 16885, 16922, 16959, 16996, 17033, 17070, 17107, 17143, 17180, // 180 °C 58 | 17217, 17254, 17291, 17328, 17365, 17402, 17438, 17475, 17512, 17549, // 190 °C 59 | 17586, 17622, 17659, 17696, 17733, 17769, 17806, 17843, 17879, 17916, // 200 °C 60 | 17953, 17989, 18026, 18063, 18099, 18136, 18172, 18209, 18246, 18282, // 210 °C 61 | 18319, 18355, 18392, 18428, 18465, 18501, 18538, 18574, 18611, 18647, // 220 °C 62 | 18684, 18720, 18756, 18793, 18829, 18866, 18902, 18938, 18975, 19011, // 230 °C 63 | 19047, 19084, 19120, 19156, 19192, 19229, 19265, 19301, 19337, 19374, // 240 °C 64 | 19410, 19446, 19482, 19518, 19555, 19591, 19627, 19663, 19699, 19735, // 250 °C 65 | 19771, 19807, 19843, 19879, 19915, 19951, 19987, 20023, 20059, 20095, // 260 °C 66 | 20131, 20167, 20203, 20239, 20275, 20311, 20347, 20383, 20419, 20455, // 270 °C 67 | 20490, 20526, 20562, 20598, 20634, 20670, 20705, 20741, 20777, 20813, // 280 °C 68 | 20848, 20884, 20920, 20956, 20991, 21027, 21063, 21098, 21134, 21170, // 290 °C 69 | 21205, 21241, 21276, 21312, 21348, 21383, 21419, 21454, 21490, 21525, // 300 °C 70 | 21561, 21596, 21632, 21667, 21703, 21738, 21774, 21809, 21844, 21880, // 310 °C 71 | 21915, 21951, 21986, 22021, 22057, 22092, 22127, 22163, 22198, 22233, // 320 °C 72 | 22268, 22304, 22339, 22374, 22409, 22445, 22480, 22515, 22550, 22585, // 330 °C 73 | 22621, 22656, 22691, 22726, 22761, 22796, 22831, 22866, 22902, 22937, // 340 °C 74 | 22972, 23007, 23042, 23077, 23112, 23147, 23182, 23217, 23252, 23287, // 350 °C 75 | 23321, 23356, 23391, 23426, 23461, 23496, 23531, 23566, 23600, 23635, // 360 °C 76 | 23670, 23705, 23740, 23774, 23809, 23844, 23879, 23913, 23948, 23983, // 370 °C 77 | 24018, 24052, 24087, 24122, 24156, 24191, 24226, 24260, 24295, 24329, // 380 °C 78 | 24364, 24399, 24433, 24468, 24502, 24537, 24571, 24606, 24640, 24675, // 390 °C 79 | 24709, 24744, 24778, 24813, 24847, 24881, 24916, 24950, 24985, 25019, // 400 °C 80 | 25053, 25088, 25122, 25156, 25191, 25225, 25259, 25293, 25328, 25362, // 410 °C 81 | 25396, 25430, 25465, 25499, 25533, 25567, 25601, 25635, 25670, 25704, // 420 °C 82 | 25738, 25772, 25806, 25840, 25874, 25908, 25942, 25976, 26010, 26044, // 430 °C 83 | 26078, 26112, 26146, 26180, 26214, 26248, 26282, 26316, 26350, 26384, // 440 °C 84 | 26418, 26452, 26486, 26520, 26553, 26587, 26621, 26655, 26689, 26722, // 450 °C 85 | 26756, 26790, 26824, 26857, 26891, 26925, 26959, 26992, 27026, 27060, // 460 °C 86 | 27093, 27127, 27161, 27194, 27228, 27261, 27295, 27329, 27362, 27396, // 470 °C 87 | 27429, 27463, 27496, 27530, 27563, 27597, 27630, 27664, 27697, 27731, // 480 °C 88 | 27764, 27798, 27831, 27864, 27898, 27931, 27964, 27998, 28031, 28064, // 490 °C 89 | 28098, 28131, 28164, 28198, 28231, 28264, 28297, 28331, 28364, 28397, // 500 °C 90 | 28430, 28463, 28497, 28530, 28563, 28596, 28629, 28662, 28695, 28729, // 510 °C 91 | 28762, 28795, 28828, 28861, 28894, 28927, 28960, 28993, 29026, 29059, // 520 °C 92 | 29092, 29125, 29158, 29191, 29224, 29256, 29289, 29322, 29355, 29388, // 530 °C 93 | 29421, 29454, 29486, 29519, 29552, 29585, 29618, 29650, 29683, 29716, // 540 °C 94 | 29749, 29781, 29814, 29847, 29880, 29912, 29945, 29978, 30010, 30043, // 550 °C 95 | 30075, 30108, 30141, 30173, 30206, 30238, 30271, 30303, 30336, 30369, // 560 °C 96 | 30401, 30434, 30466, 30498, 30531, 30563, 30596, 30628, 30661, 30693, // 570 °C 97 | 30725, 30758, 30790, 30823, 30855, 30887, 30920, 30952, 30984, 31016, // 580 °C 98 | 31049, 31081, 31113, 31145, 31178, 31210, 31242, 31274, 31306, 31339, // 590 °C 99 | 31371, 31403, 31435, 31467, 31499, 31531, 31564, 31596, 31628, 31660, // 600 °C 100 | 31692, 31724, 31756, 31788, 31820, 31852, 31884, 31916, 31948, 31980, // 610 °C 101 | 32012, 32043, 32075, 32107, 32139, 32171, 32203, 32235, 32267, 32298, // 620 °C 102 | 32330, 32362, 32394, 32426, 32457, 32489, 32521, 32553, 32584, 32616, // 630 °C 103 | 32648, 32679, 32711, 32743, 32774, 32806, 32838, 32869, 32901, 32932, // 640 °C 104 | 32964, 32996, 33027, 33059, 33090, 33122, 33153, 33185, 33216, 33248, // 650 °C 105 | 33279, 33311, 33342, 33374, 33405, 33436, 33468, 33499, 33531, 33562, // 660 °C 106 | 33593, 33625, 33656, 33687, 33718, 33750, 33781, 33812, 33844, 33875, // 670 °C 107 | 33906, 33937, 33969, 34000, 34031, 34062, 34093, 34124, 34156, 34187, // 680 °C 108 | 34218, 34249, 34280, 34311, 34342, 34373, 34404, 34435, 34466, 34497, // 690 °C 109 | 34528, 34559, 34590, 34621, 34652, 34683, 34714, 34745, 34776, 34807, // 700 °C 110 | 34838, 34869, 34899, 34930, 34961, 34992, 35023, 35054, 35084, 35115, // 710 °C 111 | 35146, 35177, 35208, 35238, 35269, 35300, 35330, 35361, 35392, 35422, // 720 °C 112 | 35453, 35484, 35514, 35545, 35576, 35606, 35637, 35667, 35698, 35728, // 730 °C 113 | 35759, 35790, 35820, 35851, 35881, 35912, 35942, 35972, 36003, 36033, // 740 °C 114 | 36064, 36094, 36125, 36155, 36185, 36216, 36246, 36276, 36307, 36337, // 750 °C 115 | 36367, 36398, 36428, 36458, 36489, 36519, 36549, 36579, 36610, 36640, // 760 °C 116 | 36670, 36700, 36730, 36760, 36791, 36821, 36851, 36881, 36911, 36941, // 770 °C 117 | 36971, 37001, 37031, 37061, 37091, 37121, 37151, 37181, 37211, 37241, // 780 °C 118 | 37271, 37301, 37331, 37361, 37391, 37421, 37451, 37481, 37511, 37541, // 790 °C 119 | 37570, 37600, 37630, 37660, 37690, 37719, 37749, 37779, 37809, 37839, // 800 °C 120 | 37868, 37898, 37928, 37957, 37987, 38017, 38046, 38076, 38106, 38135, // 810 °C 121 | 38165, 38195, 38224, 38254, 38283, 38313, 38342, 38372, 38401, 38431, // 820 °C 122 | 38460, 38490, 38519, 38549, 38578, 38608, 38637, 38667, 38696, 38725, // 830 °C 123 | 38755, 38784, 38814, 38843, 38872, 38902, 38931, 38960, 38990, 39019, // 840 °C 124 | 39048, // 850 °C 125 | -------------------------------------------------------------------------------- /src/revpi_common.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // SPDX-FileCopyrightText: 2017-2024 KUNBUS GmbH 3 | 4 | // revpi_common.c - common routines for RevPi machines 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "piControlMain.h" 12 | #include "revpi_common.h" 13 | #include "RevPiDevice.h" 14 | 15 | #define VCMSG_ID_ARM_CLOCK 0x000000003 /* Clock/Voltage ID's */ 16 | 17 | void revpi_rgb_led_trigger_event(u16 led_prev, u16 led) 18 | { 19 | u16 changed = led_prev ^ led; 20 | if (changed == 0) 21 | return; 22 | 23 | // A1 24 | if (changed & PICONTROL_LED_RGB_A1_RED) { 25 | led_trigger_event(&piDev_g.a1_red, (led & PICONTROL_LED_RGB_A1_RED) ? LED_FULL : LED_OFF); 26 | } 27 | if (changed & PICONTROL_LED_RGB_A1_GREEN) { 28 | led_trigger_event(&piDev_g.a1_green, (led & PICONTROL_LED_RGB_A1_GREEN) ? LED_FULL : LED_OFF); 29 | } 30 | if (changed & PICONTROL_LED_RGB_A1_BLUE) { 31 | led_trigger_event(&piDev_g.a1_blue, (led & PICONTROL_LED_RGB_A1_BLUE) ? LED_FULL : LED_OFF); 32 | } 33 | // A2 34 | if (changed & PICONTROL_LED_RGB_A2_RED) { 35 | led_trigger_event(&piDev_g.a2_red, (led & PICONTROL_LED_RGB_A2_RED) ? LED_FULL : LED_OFF); 36 | } 37 | if (changed & PICONTROL_LED_RGB_A2_GREEN) { 38 | led_trigger_event(&piDev_g.a2_green, (led & PICONTROL_LED_RGB_A2_GREEN) ? LED_FULL : LED_OFF); 39 | } 40 | if (changed & PICONTROL_LED_RGB_A2_BLUE) { 41 | led_trigger_event(&piDev_g.a2_blue, (led & PICONTROL_LED_RGB_A2_BLUE) ? LED_FULL : LED_OFF); 42 | } 43 | // A3 44 | if (changed & PICONTROL_LED_RGB_A3_RED) { 45 | led_trigger_event(&piDev_g.a3_red, (led & PICONTROL_LED_RGB_A3_RED) ? LED_FULL : LED_OFF); 46 | } 47 | if (changed & PICONTROL_LED_RGB_A3_GREEN) { 48 | led_trigger_event(&piDev_g.a3_green, (led & PICONTROL_LED_RGB_A3_GREEN) ? LED_FULL : LED_OFF); 49 | } 50 | if (changed & PICONTROL_LED_RGB_A3_BLUE) { 51 | led_trigger_event(&piDev_g.a3_blue, (led & PICONTROL_LED_RGB_A3_BLUE) ? LED_FULL : LED_OFF); 52 | } 53 | // A4 54 | if (changed & PICONTROL_LED_RGB_A4_RED) { 55 | led_trigger_event(&piDev_g.a4_red, (led & PICONTROL_LED_RGB_A4_RED) ? LED_FULL : LED_OFF); 56 | } 57 | if (changed & PICONTROL_LED_RGB_A4_GREEN) { 58 | led_trigger_event(&piDev_g.a4_green, (led & PICONTROL_LED_RGB_A4_GREEN) ? LED_FULL : LED_OFF); 59 | } 60 | if (changed & PICONTROL_LED_RGB_A4_BLUE) { 61 | led_trigger_event(&piDev_g.a4_blue, (led & PICONTROL_LED_RGB_A4_BLUE) ? LED_FULL : LED_OFF); 62 | } 63 | // A5 64 | if (changed & PICONTROL_LED_RGB_A5_RED) { 65 | led_trigger_event(&piDev_g.a5_red, (led & PICONTROL_LED_RGB_A5_RED) ? LED_FULL : LED_OFF); 66 | } 67 | if (changed & PICONTROL_LED_RGB_A5_GREEN) { 68 | led_trigger_event(&piDev_g.a5_green, (led & PICONTROL_LED_RGB_A5_GREEN) ? LED_FULL : LED_OFF); 69 | } 70 | if (changed & PICONTROL_LED_RGB_A5_BLUE) { 71 | led_trigger_event(&piDev_g.a5_blue, (led & PICONTROL_LED_RGB_A5_BLUE) ? LED_FULL : LED_OFF); 72 | } 73 | } 74 | 75 | void revpi_led_trigger_event(u16 led_prev, u16 led) 76 | { 77 | u16 changed = led_prev ^ led; 78 | if (changed == 0) 79 | return; 80 | 81 | if (changed & PICONTROL_LED_A1_GREEN) { 82 | led_trigger_event(&piDev_g.a1_green, (led & PICONTROL_LED_A1_GREEN) ? LED_FULL : LED_OFF); 83 | } 84 | if (changed & PICONTROL_LED_A1_RED) { 85 | led_trigger_event(&piDev_g.a1_red, (led & PICONTROL_LED_A1_RED) ? LED_FULL : LED_OFF); 86 | } 87 | if (changed & PICONTROL_LED_A2_GREEN) { 88 | led_trigger_event(&piDev_g.a2_green, (led & PICONTROL_LED_A2_GREEN) ? LED_FULL : LED_OFF); 89 | } 90 | if (changed & PICONTROL_LED_A2_RED) { 91 | led_trigger_event(&piDev_g.a2_red, (led & PICONTROL_LED_A2_RED) ? LED_FULL : LED_OFF); 92 | } 93 | 94 | if ((piDev_g.machine_type == REVPI_CONNECT) || 95 | (piDev_g.machine_type == REVPI_CONNECT_SE) || 96 | (piDev_g.machine_type == REVPI_FLAT)) { 97 | if (changed & PICONTROL_LED_A3_GREEN) { 98 | led_trigger_event(&piDev_g.a3_green, (led & PICONTROL_LED_A3_GREEN) ? LED_FULL : LED_OFF); 99 | } 100 | if (changed & PICONTROL_LED_A3_RED) { 101 | led_trigger_event(&piDev_g.a3_red, (led & PICONTROL_LED_A3_RED) ? LED_FULL : LED_OFF); 102 | } 103 | } 104 | 105 | if (piDev_g.machine_type == REVPI_FLAT) { 106 | if (changed & PICONTROL_LED_A4_GREEN) { 107 | led_trigger_event(&piDev_g.a4_green, (led & PICONTROL_LED_A4_GREEN) ? LED_FULL : LED_OFF); 108 | } 109 | if (changed & PICONTROL_LED_A4_RED) { 110 | led_trigger_event(&piDev_g.a4_red, (led & PICONTROL_LED_A4_RED) ? LED_FULL : LED_OFF); 111 | } 112 | if (changed & PICONTROL_LED_A5_GREEN) { 113 | led_trigger_event(&piDev_g.a5_green, (led & PICONTROL_LED_A5_GREEN) ? LED_FULL : LED_OFF); 114 | } 115 | if (changed & PICONTROL_LED_A5_RED) { 116 | led_trigger_event(&piDev_g.a5_red, (led & PICONTROL_LED_A5_RED) ? LED_FULL : LED_OFF); 117 | } 118 | } 119 | } 120 | 121 | static enum revpi_power_led_mode power_led_mode_s = 255; 122 | static unsigned long power_led_timer_s; 123 | static bool power_led_red_state_s; 124 | char *lock_file; 125 | int lock_line; 126 | 127 | void revpi_power_led_red_set(enum revpi_power_led_mode mode) 128 | { 129 | switch (mode) { 130 | case REVPI_POWER_LED_OFF: 131 | if (power_led_mode_s == REVPI_POWER_LED_OFF 132 | || power_led_mode_s == REVPI_POWER_LED_ON_500MS 133 | || power_led_mode_s == REVPI_POWER_LED_ON_1000MS) 134 | return; // nothing to do 135 | //pr_info("power led green\n"); 136 | power_led_red_state_s = false; 137 | led_trigger_event(&piDev_g.power_red, LED_OFF); 138 | break; 139 | default: 140 | case REVPI_POWER_LED_ON: 141 | if (power_led_mode_s == REVPI_POWER_LED_ON) 142 | return; // nothing to do 143 | //pr_info("power led red\n"); 144 | power_led_red_state_s = true; 145 | led_trigger_event(&piDev_g.power_red, LED_FULL); 146 | break; 147 | case REVPI_POWER_LED_FLICKR: 148 | // just set the mode variable, anything else is done in the run function 149 | if (jiffies_to_msecs(jiffies - power_led_timer_s) > 10000) { 150 | //pr_info("power led flickr\n"); 151 | power_led_timer_s = jiffies; 152 | } 153 | break; 154 | case REVPI_POWER_LED_ON_500MS: 155 | case REVPI_POWER_LED_ON_1000MS: 156 | //pr_info("power led pulse\n"); 157 | power_led_red_state_s = true; 158 | led_trigger_event(&piDev_g.power_red, LED_FULL); 159 | power_led_timer_s = jiffies; 160 | break; 161 | } 162 | power_led_mode_s = mode; 163 | } 164 | 165 | 166 | void revpi_check_timeout(void) 167 | { 168 | ktime_t now = ktime_get(); 169 | struct list_head *pCon; 170 | 171 | my_rt_mutex_lock(&piDev_g.lockListCon); 172 | list_for_each(pCon, &piDev_g.listCon) { 173 | tpiControlInst *pos_inst; 174 | pos_inst = list_entry(pCon, tpiControlInst, list); 175 | 176 | if (pos_inst->tTimeoutDurationMs != 0) { 177 | if (ktime_compare(now, pos_inst->tTimeoutTS) > 0) { 178 | // set all outputs to 0 179 | int i; 180 | my_rt_mutex_lock(&piDev_g.lockPI); 181 | for (i = 0; i < RevPiDevice_getDevCnt(); i++) { 182 | if (RevPiDevice_getDev(i)->i8uActive) { 183 | memset(piDev_g.ai8uPI + RevPiDevice_getDev(i)->i16uOutputOffset, 0, RevPiDevice_getDev(i)->sId.i16uFBS_OutputLength); 184 | } 185 | } 186 | rt_mutex_unlock(&piDev_g.lockPI); 187 | pos_inst->tTimeoutTS = ktime_add_ms(ktime_get(), pos_inst->tTimeoutDurationMs); 188 | 189 | // this must only be done for one connection 190 | rt_mutex_unlock(&piDev_g.lockListCon); 191 | return; 192 | } 193 | } 194 | } 195 | rt_mutex_unlock(&piDev_g.lockListCon); 196 | } 197 | 198 | void revpi_power_led_red_run(void) 199 | { 200 | switch (power_led_mode_s) { 201 | case REVPI_POWER_LED_FLICKR: 202 | if (power_led_red_state_s && jiffies_to_msecs(jiffies - power_led_timer_s) > 10) { 203 | power_led_red_state_s = false; 204 | led_trigger_event(&piDev_g.power_red, LED_OFF); 205 | power_led_timer_s = jiffies; 206 | } else if (!power_led_red_state_s && jiffies_to_msecs(jiffies - power_led_timer_s) > 90) { 207 | power_led_red_state_s = true; 208 | led_trigger_event(&piDev_g.power_red, LED_FULL); 209 | power_led_timer_s = jiffies; 210 | } 211 | break; 212 | case REVPI_POWER_LED_ON_500MS: 213 | if (jiffies_to_msecs(jiffies - power_led_timer_s) > 500) { 214 | power_led_red_state_s = false; 215 | led_trigger_event(&piDev_g.power_red, LED_OFF); 216 | power_led_mode_s = REVPI_POWER_LED_OFF; 217 | } 218 | break; 219 | case REVPI_POWER_LED_ON_1000MS: 220 | if (jiffies_to_msecs(jiffies - power_led_timer_s) > 1000) { 221 | power_led_red_state_s = false; 222 | led_trigger_event(&piDev_g.power_red, LED_OFF); 223 | power_led_mode_s = REVPI_POWER_LED_OFF; 224 | } 225 | break; 226 | default: 227 | ; // nothing to do 228 | } 229 | } 230 | 231 | int set_rt_priority(struct task_struct *task, int priority) 232 | { 233 | struct sched_attr attr; 234 | 235 | if (!task || priority < 0 || priority > MAX_RT_PRIO - 1) 236 | return -EINVAL; 237 | 238 | memset(&attr, 0, sizeof(attr)); 239 | attr.sched_policy = SCHED_FIFO; 240 | attr.sched_priority = priority; 241 | 242 | return sched_setattr_nocheck(task, &attr); 243 | } 244 | 245 | /** 246 | * set_kthread_prios - assign realtime priority to specific kthreads 247 | * @ktprios: null-terminated array of kthread/priority tuples 248 | * 249 | * Walk the children of kthreadd and compare the command name to the ones 250 | * specified in @ktprios. Upon finding a match, assign the given priority 251 | * with SCHED_FIFO policy. 252 | * 253 | * Return 0 on success or a negative errno on failure. 254 | * Normally failure only occurs because an invalid priority was specified. 255 | */ 256 | int set_kthread_prios(const struct kthread_prio *ktprios) 257 | { 258 | const struct kthread_prio *ktprio; 259 | struct task_struct *child; 260 | int ret = 0; 261 | 262 | read_lock(&tasklist_lock); 263 | for (ktprio = ktprios; ktprio->comm[0]; ktprio++) { 264 | bool found = false; 265 | 266 | list_for_each_entry(child, &kthreadd_task->children, sibling) 267 | if (!strncmp(child->comm, ktprio->comm, 268 | TASK_COMM_LEN)) { 269 | found = true; 270 | ret = set_rt_priority(child, ktprio->prio); 271 | if (ret) { 272 | pr_err("cannot set priority of %s\n", 273 | ktprio->comm); 274 | goto out; 275 | } else { 276 | pr_info("set priority of %s to %d\n", 277 | ktprio->comm, ktprio->prio); 278 | } 279 | break; 280 | } 281 | 282 | if (!found) { 283 | pr_err("cannot find kthread %s\n", ktprio->comm); 284 | ret = -ENOENT; 285 | goto out; 286 | } 287 | } 288 | out: 289 | read_unlock(&tasklist_lock); 290 | return ret; 291 | } 292 | -------------------------------------------------------------------------------- /src/revpi_common.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2017-2024 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef _REVPI_COMMON_H 6 | #define _REVPI_COMMON_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | enum revpi_power_led_mode { 14 | REVPI_POWER_LED_OFF = 0, 15 | REVPI_POWER_LED_ON = 1, 16 | REVPI_POWER_LED_FLICKR = 2, 17 | REVPI_POWER_LED_ON_500MS = 3, 18 | REVPI_POWER_LED_ON_1000MS = 4, 19 | }; 20 | 21 | void revpi_rgb_led_trigger_event(u16 led_prev, u16 led); 22 | void revpi_led_trigger_event(u16 led_prev, u16 led); 23 | void revpi_power_led_red_set(enum revpi_power_led_mode mode); 24 | void revpi_power_led_red_run(void); 25 | void revpi_check_timeout(void); 26 | 27 | extern char *lock_file; 28 | extern int lock_line; 29 | 30 | #if 0 31 | #define my_rt_mutex_lock(P) if (rt_mutex_is_locked(P)) { \ 32 | ktime_t t0, t1; \ 33 | s64 diff; \ 34 | t0 = ktime_get(); \ 35 | rt_mutex_lock(P); \ 36 | t1 = ktime_get(); \ 37 | diff = ktime_to_us(ktime_sub(t1, t0)); \ 38 | pr_err("mutex already locked by %d %s\nnow: delay%5dus %d " __FILE__ "\n", lock_line, lock_file, (int)diff, __LINE__); \ 39 | } else { \ 40 | rt_mutex_lock(P); \ 41 | } \ 42 | lock_file = __FILE__; lock_line = __LINE__; 43 | 44 | #else 45 | #define my_rt_mutex_lock(P) rt_mutex_lock(P) 46 | #endif 47 | 48 | struct kthread_prio { 49 | const char comm[TASK_COMM_LEN]; 50 | int prio; 51 | }; 52 | 53 | int set_kthread_prios(const struct kthread_prio *ktprios); 54 | int set_rt_priority(struct task_struct *task, int priority); 55 | #endif /* _REVPI_COMMON_H */ 56 | -------------------------------------------------------------------------------- /src/revpi_compact.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2017-2024 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef _REVPI_COMPACT_H 6 | #define _REVPI_COMPACT_H 7 | 8 | #include 9 | 10 | #include "common_define.h" 11 | #include "piControl.h" 12 | 13 | #define RevPi_Compact_OFFSET_CoreTemperatur 0 //BYTE 14 | #define RevPi_Compact_OFFSET_CoreFrequency 1 //BYTE 15 | #define RevPi_Compact_OFFSET_DIn 2 //BYTE 16 | #define RevPi_Compact_OFFSET_AIn1 3 //INT 17 | #define RevPi_Compact_OFFSET_AIn2 5 //INT 18 | #define RevPi_Compact_OFFSET_AIn3 7 //INT 19 | #define RevPi_Compact_OFFSET_AIn4 9 //INT 20 | #define RevPi_Compact_OFFSET_AIn5 11 //INT 21 | #define RevPi_Compact_OFFSET_AIn6 13 //INT 22 | #define RevPi_Compact_OFFSET_AIn7 15 //INT 23 | #define RevPi_Compact_OFFSET_AIn8 17 //INT 24 | #define RevPi_Compact_OFFSET_DIn_Status 19 //BYTE 25 | #define RevPi_Compact_OFFSET_DOut_Status 20 //BYTE 26 | #define RevPi_Compact_OFFSET_AIn_Status 21 //BYTE 27 | #define RevPi_Compact_OFFSET_AOut_Status 22 //BYTE 28 | #define RevPi_Compact_OFFSET_RevPiLED 23 //BYTE 29 | #define RevPi_Compact_OFFSET_DOut 24 //BYTE 30 | #define RevPi_Compact_OFFSET_AOut1 25 //BYTE 31 | #define RevPi_Compact_OFFSET_AOut2 27 //BYTE 32 | #define RevPi_Compact_OFFSET_DInDebounce 29 //BYTE 33 | #define RevPi_Compact_OFFSET_AInMode1 30 //INT 34 | #define RevPi_Compact_OFFSET_AInMode2 31 //INT 35 | #define RevPi_Compact_OFFSET_AInMode3 32 //INT 36 | #define RevPi_Compact_OFFSET_AInMode4 33 //INT 37 | #define RevPi_Compact_OFFSET_AInMode5 34 //INT 38 | #define RevPi_Compact_OFFSET_AInMode6 35 //INT 39 | #define RevPi_Compact_OFFSET_AInMode7 36 //INT 40 | #define RevPi_Compact_OFFSET_AInMode8 37 //INT 41 | 42 | typedef struct _SRevPiCompactConfig { 43 | unsigned int offset; 44 | u16 din_debounce; /* usec */ 45 | u8 ain[8]; 46 | #define AIN_ENABLED 0 /* bit number */ 47 | #define AIN_RTD 1 48 | #define AIN_PT1K 2 49 | } SRevPiCompactConfig; 50 | 51 | typedef struct _SRevPiCompactImage { 52 | struct { 53 | u8 i8uCPUTemperature; 54 | u8 i8uCPUFrequency; 55 | u8 din; 56 | s16 ain[8]; 57 | u8 din_status; /* identical layout as SDioModuleStatus */ 58 | u8 dout_status; 59 | u8 ain_status; 60 | #define AIN_TX_ERR 7 /* bit number */ 61 | u8 aout_status; 62 | #define AOUT_TX_ERR 7 63 | } __attribute__ ((__packed__)) drv; // 23 bytes 64 | struct { 65 | u8 led; 66 | u8 dout; 67 | u16 aout[2]; 68 | } __attribute__ ((__packed__)) usr; // 6 bytes 69 | } __attribute__ ((__packed__)) SRevPiCompactImage; 70 | 71 | struct revpi_compact_stats { 72 | u64 lost_cycles; 73 | seqlock_t lock; 74 | }; 75 | 76 | INT32U revpi_compact_config(uint8_t i8uAddress, uint16_t i16uNumEntries, SEntryInfo * pEnt); 77 | int revpi_compact_init(void); 78 | int revpi_compact_reset(void); 79 | void revpi_compact_fini(void); 80 | void revpi_compact_adjust_config(void); 81 | #endif /*_REVPI_COMPACT_H */ 82 | -------------------------------------------------------------------------------- /src/revpi_core.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2017-2024 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef _REVPI_CORE_H 6 | #define _REVPI_CORE_H 7 | 8 | #include 9 | #include 10 | 11 | #include "ModGateRS485.h" 12 | #include "piControlMain.h" 13 | #include "PiBridgeMaster.h" 14 | #include "RevPiDevice.h" 15 | 16 | typedef enum { 17 | piBridgeStop = 0, 18 | piBridgeInit = 1, // MGate Protocol 19 | piBridgeRun = 2, // IO Protocol 20 | piBridgeDummy = 99 // dummy value to force update of led state 21 | } enPiBridgeState; 22 | 23 | typedef struct _SRevPiProcessImage { 24 | struct { 25 | u8 i8uStatus; 26 | u8 i8uIOCycle; 27 | u16 i16uRS485ErrorCnt; 28 | u8 i8uCPUTemperature; 29 | u8 i8uCPUFrequency; 30 | } __attribute__ ((__packed__)) drv; // 6 bytes 31 | struct { 32 | union { 33 | u8 outputs; 34 | u8 leds; 35 | }; 36 | u16 i16uRS485ErrorLimit1; 37 | u16 i16uRS485ErrorLimit2; 38 | u16 rgb_leds; 39 | } __attribute__ ((__packed__)) usr; // 7 bytes (if device has no rgb_leds, only 5 bytes are used in process image) 40 | } __attribute__ ((__packed__)) SRevPiProcessImage; 41 | 42 | typedef struct _SRevPiCore { 43 | SRevPiProcessImage image; 44 | 45 | // piGate stuff 46 | INT8U i8uLeftMGateIdx; // index of left GateModule in RevPiDevice_asDevice_m 47 | INT8U i8uRightMGateIdx; // index of right GateModule in RevPiDevice_asDevice_m 48 | INT8U ai8uInput[KB_PD_LEN * MODGATECOM_MAX_MODULES]; 49 | INT8U ai8uOutput[KB_PD_LEN * MODGATECOM_MAX_MODULES]; 50 | 51 | // piBridge stuff 52 | struct rt_mutex lockBridgeState; 53 | enPiBridgeState eBridgeState; // 0=stopped, 1=init, 2=running 54 | struct gpio_desc *gpio_sniff1a; 55 | struct gpio_desc *gpio_sniff1b; 56 | struct gpio_desc *gpio_sniff2a; 57 | struct gpio_desc *gpio_sniff2b; 58 | 59 | // watchdog stuff, Connect only 60 | struct gpio_desc *gpio_x2di; 61 | struct gpio_desc *gpio_x2do; 62 | struct gpio_desc *gpio_wdtrigger; 63 | 64 | // piBridge multiplex, Connect 5 only 65 | struct gpio_desc *gpio_pbswitch_mpx_left; 66 | struct gpio_desc *gpio_pbswitch_detect_left; 67 | struct gpio_desc *gpio_pbswitch_mpx_right; 68 | struct gpio_desc *gpio_pbswitch_detect_right; 69 | 70 | // handle user telegrams 71 | struct rt_mutex lockUserTel; 72 | struct semaphore semUserTel; 73 | bool pendingUserTel; 74 | SIOGeneric requestUserTel; 75 | SIOGeneric responseUserTel; 76 | int statusUserTel; 77 | 78 | // handle mGate telegrams 79 | struct rt_mutex lockGateTel; 80 | struct semaphore semGateTel; 81 | bool pendingGateTel; 82 | INT16U i16uCmdGateTel; 83 | INT8U i8uAddressGateTel; 84 | INT8U ai8uSendDataGateTel[MAX_TELEGRAM_DATA_SIZE]; 85 | INT8U i8uSendDataLenGateTel; 86 | INT8U ai8uRecvDataGateTel[MAX_TELEGRAM_DATA_SIZE]; 87 | INT16U i16uRecvDataLenGateTel; 88 | int statusGateTel; 89 | 90 | // piIO thread 91 | struct task_struct *pIoThread; 92 | struct hrtimer ioTimer; 93 | struct semaphore ioSem; 94 | 95 | // cycle time measurement 96 | unsigned int cycle_min; /* usecs */ 97 | unsigned int cycle_max; /* usecs */ 98 | u64 cycle_num; 99 | /* Number of communication errors */ 100 | u32 comm_errors; 101 | bool data_exchange_running; 102 | } SRevPiCore; 103 | 104 | extern SRevPiCore piCore_g; 105 | extern unsigned int picontrol_max_cycle_deviation; 106 | 107 | u8 revpi_core_find_gate(struct net_device *netdev, u16 module_type); 108 | void revpi_core_gate_connected(SDevice *revpi_dev, bool connected); 109 | int revpi_core_init(void); 110 | void revpi_core_fini(void); 111 | #endif /* _REVPI_CORE_H */ 112 | -------------------------------------------------------------------------------- /src/revpi_flat.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // SPDX-FileCopyrightText: 2020-2024 KUNBUS GmbH 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "piControlMain.h" 15 | #include "process_image.h" 16 | #include "revpi_common.h" 17 | #include "revpi_flat.h" 18 | #include "RevPiDevice.h" 19 | 20 | #if KERNEL_VERSION(6, 6, 0) > LINUX_VERSION_CODE 21 | /* kernel before 6.6 has a custom RPi patch, so offset started at 0 */ 22 | #define GPIOCHIP0_OFFSET 0 23 | #else 24 | #define GPIOCHIP0_OFFSET 512 25 | #endif 26 | 27 | /* relais gpio num */ 28 | #define REVPI_FLAT_RELAIS_GPIO (28 + GPIOCHIP0_OFFSET) 29 | 30 | /* button gpio num */ 31 | #define REVPI_FLAT_BUTTON_GPIO (13 + GPIOCHIP0_OFFSET) 32 | #define REVPI_FLAT_S_BUTTON_GPIO (23 + GPIOCHIP0_OFFSET) 33 | 34 | #define REVPI_FLAT_DOUT_THREAD_PRIO (MAX_RT_PRIO / 2 + 8) 35 | #define REVPI_FLAT_AIN_THREAD_PRIO (MAX_RT_PRIO / 2 + 6) 36 | /* ain resistor (Ohm) */ 37 | #define REVPI_FLAT_AIN_RESISTOR 240 38 | /* This value is a correction factor which takes the currency loss caused 39 | by resistors into account. See the flat schematics for details. */ 40 | #define REVPI_FLAT_AIN_CORRECTION 1986582478 41 | #define REVPI_FLAT_AIN_POLL_INTERVAL 85 42 | 43 | #define REVPI_FLAT_CONFIG_OFFSET(member) offsetof(struct revpi_flat_image, usr.member) 44 | 45 | #define REVPI_FLAT_CONFIG_OFFSET_LEDS REVPI_FLAT_CONFIG_OFFSET(leds) 46 | #define REVPI_FLAT_CONFIG_OFFSET_AOUT REVPI_FLAT_CONFIG_OFFSET(aout) 47 | #define REVPI_FLAT_CONFIG_OFFSET_DOUT REVPI_FLAT_CONFIG_OFFSET(dout) 48 | #define REVPI_FLAT_CONFIG_OFFSET_AIN_MODE REVPI_FLAT_CONFIG_OFFSET(ain_mode_current) 49 | /* 50 | * Delay in usecs before the next AIN value can safely be retrieved (see tCSHSD 51 | * in MCP datasheet) 52 | */ 53 | #define REVPI_FLAT_AIN_DELAY 10 54 | 55 | struct revpi_flat_image { 56 | struct { 57 | s16 ain; 58 | #define REVPI_FLAT_AIN_TX_ERR 7 59 | u8 ain_status; 60 | #define REVPI_FLAT_AOUT_TX_ERR 7 61 | u8 aout_status; 62 | u8 cpu_temp; 63 | u8 cpu_freq; 64 | u8 button; 65 | } __attribute__ ((__packed__)) drv; 66 | struct { 67 | u16 leds; 68 | u16 aout; 69 | u8 dout; 70 | u8 ain_mode_current; 71 | } __attribute__ ((__packed__)) usr; 72 | } __attribute__ ((__packed__)); 73 | 74 | struct revpi_flat { 75 | struct revpi_flat_image image; 76 | struct task_struct *dout_thread; 77 | struct task_struct *ain_thread; 78 | struct device *din_dev; 79 | struct gpio_desc *dout_fault; 80 | struct gpio_desc *digout; 81 | struct gpio_desc *button_desc; 82 | struct gpio_descs *dout; 83 | struct iio_channel ain; 84 | struct iio_channel aout; 85 | }; 86 | 87 | static int revpi_flat_poll_dout(void *data) 88 | { 89 | struct revpi_flat *flat = (struct revpi_flat *) data; 90 | struct revpi_flat_image *image = &flat->image; 91 | struct revpi_flat_image *usr_image; 92 | int dout_val = -1; 93 | int aout_val = -1; 94 | int raw_out; 95 | 96 | usr_image = (struct revpi_flat_image *) piDev_g.ai8uPI; 97 | while (!kthread_should_stop()) { 98 | my_rt_mutex_lock(&piDev_g.lockPI); 99 | image->drv.button = gpiod_get_value_cansleep(flat->button_desc); 100 | usr_image->drv = image->drv; 101 | 102 | if (usr_image->usr.dout != image->usr.dout) 103 | dout_val = usr_image->usr.dout; 104 | 105 | if (usr_image->usr.aout != image->usr.aout) 106 | aout_val = usr_image->usr.aout; 107 | 108 | image->usr = usr_image->usr; 109 | rt_mutex_unlock(&piDev_g.lockPI); 110 | 111 | if (dout_val != -1) { 112 | gpiod_set_value_cansleep(flat->digout, !!dout_val); 113 | dout_val = -1; 114 | } 115 | 116 | if (aout_val != -1) { 117 | int ret; 118 | 119 | raw_out = (image->usr.aout * 4095) / 10000; 120 | 121 | ret = iio_write_channel_raw(&flat->aout, 122 | min(raw_out, 4095)); 123 | if (ret) 124 | dev_err(piDev_g.dev, "failed to write value to " 125 | "analog ouput: %i\n", ret); 126 | 127 | assign_bit_in_byte(REVPI_FLAT_AOUT_TX_ERR, 128 | &image->drv.aout_status, ret < 0); 129 | aout_val = -1; 130 | } 131 | 132 | usleep_range(100, 150); 133 | } 134 | 135 | return 0; 136 | } 137 | 138 | static int revpi_flat_handle_ain(struct revpi_flat *flat, bool mode_current) 139 | { 140 | struct revpi_flat_image *image = &flat->image; 141 | unsigned long long ain_val; 142 | int raw_val; 143 | int ret; 144 | 145 | 146 | ret = iio_read_channel_raw(&flat->ain, &raw_val); 147 | assign_bit_in_byte(REVPI_FLAT_AIN_TX_ERR, &image->drv.ain_status, 148 | ret < 0); 149 | if (ret < 0) { 150 | dev_err(piDev_g.dev, "failed to read from analog " 151 | "channel: %i\n", ret); 152 | return ret; 153 | } 154 | /* AIN value in mV = ((raw * 12.5V) >> 21 bit) + 6.25V */ 155 | ain_val = shift_right((s64) raw_val * 12500, 21) + 6250; 156 | 157 | if (mode_current) { 158 | ain_val *= 1000000000; 159 | ain_val = div_s64(ain_val, REVPI_FLAT_AIN_RESISTOR); 160 | } else 161 | ain_val *= REVPI_FLAT_AIN_CORRECTION; 162 | 163 | ain_val = (int) div_s64(ain_val, 1000000000LL); 164 | 165 | my_rt_mutex_lock(&piDev_g.lockPI); 166 | image->drv.ain = ain_val; 167 | rt_mutex_unlock(&piDev_g.lockPI); 168 | 169 | return 0; 170 | } 171 | 172 | static int revpi_flat_poll_ain(void *data) 173 | { 174 | struct revpi_flat *flat = (struct revpi_flat *) data; 175 | struct revpi_flat_image *image = &flat->image; 176 | bool ain_mode_current = false; 177 | u16 prev_leds = 0; 178 | int temperature; 179 | int freq; 180 | u16 leds; 181 | int ret; 182 | 183 | while (!kthread_should_stop()) { 184 | ret = revpi_flat_handle_ain(flat, ain_mode_current); 185 | if (ret) 186 | msleep(REVPI_FLAT_AIN_POLL_INTERVAL); 187 | /* 188 | * Wait a minimum timespan before requesting the next AIN 189 | * value. 190 | */ 191 | usleep_range(REVPI_FLAT_AIN_DELAY, REVPI_FLAT_AIN_DELAY + 10); 192 | /* read cpu temperature */ 193 | if (piDev_g.thermal_zone != NULL) { 194 | ret = thermal_zone_get_temp(piDev_g.thermal_zone, 195 | &temperature); 196 | if (ret) 197 | dev_err(piDev_g.dev,"Failed to get cpu " 198 | "temperature"); 199 | } 200 | 201 | /* 202 | Get the CPU clock from CPU0 in kHz 203 | and divide it down to MHz. 204 | */ 205 | freq = cpufreq_quick_get(0); 206 | 207 | my_rt_mutex_lock(&piDev_g.lockPI); 208 | if ((piDev_g.thermal_zone != NULL) && !ret) 209 | image->drv.cpu_temp = temperature / 1000; 210 | image->drv.cpu_freq = freq / 10; 211 | leds = image->usr.leds; 212 | ain_mode_current = !!image->usr.ain_mode_current; 213 | rt_mutex_unlock(&piDev_g.lockPI); 214 | 215 | if (prev_leds != leds) 216 | revpi_led_trigger_event(prev_leds, leds); 217 | 218 | prev_leds = leds; 219 | } 220 | 221 | return 0; 222 | } 223 | 224 | static int revpi_flat_match_iio_name(struct device *dev, const void *data) 225 | { 226 | return !strcmp(data, dev_to_iio_dev(dev)->name); 227 | } 228 | 229 | static void revpi_flat_adjust_config(void) 230 | { 231 | SDeviceInfo *dev_info = piDev_g.devs->dev; 232 | SDevice *dev; 233 | int i; 234 | 235 | /* Check if there are any valid parsing results at all. This might 236 | not be the case if an invalid config file was provided. */ 237 | if (piDev_g.devs == NULL) 238 | return; 239 | 240 | /* Add all virtual devices to list of known devices. The first device is 241 | the flat, so skip it. */ 242 | for (i = 1; i < piDev_g.devs->i16uNumDevices; i++) { 243 | dev_info = &piDev_g.devs->dev[i]; 244 | dev = RevPiDevice_getDev(i); 245 | 246 | if (dev_info->i16uModuleType >= PICONTROL_SW_OFFSET) { 247 | /* virtual device are always active */ 248 | dev->i8uActive = 1; 249 | dev->sId.i16uModulType = dev_info->i16uModuleType; 250 | } else { 251 | pr_err("Additional module type %d is not allowed on " 252 | "RevPi Flat. Only sw modules are allowed.\n", 253 | dev_info->i16uModuleType); 254 | 255 | RevPiDevice_setStatus(0, PICONTROL_STATUS_MISSING_MODULE); 256 | dev->i8uActive = 0; 257 | dev->sId.i16uModulType = dev_info->i16uModuleType | 258 | PICONTROL_NOT_CONNECTED; 259 | } 260 | dev->i8uAddress = dev_info->i8uAddress; 261 | dev->i8uScan = 0; 262 | dev->i16uInputOffset = dev_info->i16uInputOffset; 263 | dev->i16uOutputOffset = dev_info->i16uOutputOffset; 264 | dev->i16uConfigOffset = dev_info->i16uConfigOffset; 265 | dev->i16uConfigLength = dev_info->i16uConfigLength; 266 | dev->sId.i32uSerialnumber = dev_info->i32uSerialnumber; 267 | dev->sId.i16uHW_Revision = dev_info->i16uHW_Revision; 268 | dev->sId.i16uSW_Major = dev_info->i16uSW_Major; 269 | dev->sId.i16uSW_Minor = dev_info->i16uSW_Minor; 270 | dev->sId.i32uSVN_Revision = dev_info->i32uSVN_Revision; 271 | dev->sId.i16uFBS_InputLength = dev_info->i16uInputLength; 272 | dev->sId.i16uFBS_OutputLength = dev_info->i16uOutputLength; 273 | dev->sId.i16uFeatureDescriptor = 0; 274 | 275 | RevPiDevice_incDevCnt(); 276 | } 277 | } 278 | 279 | static void revpi_flat_set_defaults(void) 280 | { 281 | my_rt_mutex_lock(&piDev_g.lockPI); 282 | memset(piDev_g.ai8uPI, 0, sizeof(piDev_g.ai8uPI)); 283 | if (piDev_g.ent) 284 | revpi_set_defaults(piDev_g.ai8uPI, piDev_g.ent); 285 | rt_mutex_unlock(&piDev_g.lockPI); 286 | } 287 | 288 | int revpi_flat_reset() 289 | { 290 | dev_dbg(piDev_g.dev, "Resetting RevPi Flat control\n"); 291 | 292 | RevPiDevice_init(); 293 | 294 | revpi_flat_adjust_config(); 295 | revpi_flat_set_defaults(); 296 | 297 | return 0; 298 | } 299 | 300 | int revpi_flat_init(void) 301 | { 302 | struct revpi_flat *flat; 303 | unsigned int button_gpio; 304 | struct device *dev; 305 | int ret; 306 | 307 | flat = devm_kzalloc(piDev_g.dev, sizeof(*flat), GFP_KERNEL); 308 | if (!flat) 309 | return -ENOMEM; 310 | 311 | piDev_g.machine = flat; 312 | 313 | flat->digout = gpio_to_desc(REVPI_FLAT_RELAIS_GPIO); 314 | if (!flat->digout) { 315 | dev_err(piDev_g.dev, "no gpio desc for digital output found\n"); 316 | return -ENXIO; 317 | } 318 | 319 | ret = gpiod_direction_output(flat->digout, 0); 320 | if (ret) { 321 | dev_err(piDev_g.dev, "Failed to set direction for relais " 322 | "gpio %i\n", ret); 323 | return -ENXIO; 324 | } 325 | 326 | button_gpio = of_machine_is_compatible("kunbus,revpi-flat-s-2022") ? 327 | REVPI_FLAT_S_BUTTON_GPIO : REVPI_FLAT_BUTTON_GPIO; 328 | 329 | flat->button_desc = gpio_to_desc(button_gpio); 330 | if (!flat->button_desc) { 331 | dev_err(piDev_g.dev, "no gpio desc for button found\n"); 332 | return -ENXIO; 333 | } 334 | 335 | ret = gpiod_direction_input(flat->button_desc); 336 | if (ret) { 337 | dev_err(piDev_g.dev, "Failed to set direction for button " 338 | "gpio %i\n", ret); 339 | return -ENXIO; 340 | } 341 | 342 | dev = bus_find_device(&iio_bus_type, NULL, "mcp3550-50", 343 | revpi_flat_match_iio_name); 344 | if (!dev) { 345 | dev_err(piDev_g.dev, "cannot find analog input device\n"); 346 | return -ENODEV; 347 | } 348 | 349 | flat->ain.indio_dev = dev_to_iio_dev(dev); 350 | flat->ain.channel = &(flat->ain.indio_dev->channels[0]); 351 | 352 | 353 | dev = bus_find_device(&iio_bus_type, NULL, "dac7512", 354 | revpi_flat_match_iio_name); 355 | if (!dev) { 356 | dev_err(piDev_g.dev, "cannot find analog output device\n"); 357 | ret = -ENODEV; 358 | goto err_put_ain; 359 | } 360 | 361 | flat->aout.indio_dev = dev_to_iio_dev(dev); 362 | flat->aout.channel = &(flat->aout.indio_dev->channels[0]); 363 | 364 | flat->dout_thread = kthread_create(&revpi_flat_poll_dout, flat, 365 | "piControl dout"); 366 | if (IS_ERR(flat->dout_thread)) { 367 | dev_err(piDev_g.dev, "cannot create dout thread\n"); 368 | ret = PTR_ERR(flat->dout_thread); 369 | goto err_put_aout; 370 | } 371 | 372 | ret = set_rt_priority(flat->dout_thread, REVPI_FLAT_DOUT_THREAD_PRIO); 373 | if (ret) { 374 | dev_err(piDev_g.dev, "cannot upgrade dout thread priority\n"); 375 | goto err_stop_dout_thread; 376 | } 377 | 378 | flat->ain_thread = kthread_create(&revpi_flat_poll_ain, flat, 379 | "piControl ain"); 380 | if (IS_ERR(flat->ain_thread)) { 381 | dev_err(piDev_g.dev, "cannot create ain thread\n"); 382 | ret = PTR_ERR(flat->ain_thread); 383 | goto err_stop_dout_thread; 384 | } 385 | 386 | ret = set_rt_priority(flat->ain_thread, REVPI_FLAT_AIN_THREAD_PRIO); 387 | if (ret) { 388 | dev_err(piDev_g.dev, "cannot upgrade ain thread priority\n"); 389 | goto err_stop_ain_thread; 390 | } 391 | 392 | revpi_flat_reset(); 393 | 394 | wake_up_process(flat->dout_thread); 395 | wake_up_process(flat->ain_thread); 396 | 397 | return 0; 398 | 399 | err_stop_ain_thread: 400 | kthread_stop(flat->ain_thread); 401 | err_stop_dout_thread: 402 | kthread_stop(flat->dout_thread); 403 | err_put_aout: 404 | iio_device_put(flat->aout.indio_dev); 405 | err_put_ain: 406 | iio_device_put(flat->ain.indio_dev); 407 | 408 | return ret; 409 | } 410 | 411 | void revpi_flat_fini(void) 412 | { 413 | struct revpi_flat *flat = (struct revpi_flat *) piDev_g.machine; 414 | 415 | kthread_stop(flat->ain_thread); 416 | kthread_stop(flat->dout_thread); 417 | iio_device_put(flat->aout.indio_dev); 418 | iio_device_put(flat->ain.indio_dev); 419 | } 420 | -------------------------------------------------------------------------------- /src/revpi_flat.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2020-2023 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef _REVPI_FLAT_H 6 | #define _REVPI_FLAT_H 7 | 8 | int revpi_flat_init(void); 9 | void revpi_flat_fini(void); 10 | int revpi_flat_reset(void); 11 | 12 | #endif /* _REVPI_FLAT_H */ 13 | -------------------------------------------------------------------------------- /src/revpi_gate.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2018-2023 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef _REVPI_GATE_H 6 | #define _REVPI_GATE_H 7 | void revpi_gate_init(void); 8 | void revpi_gate_fini(void); 9 | #endif /* _REVPI_GATE_H */ 10 | -------------------------------------------------------------------------------- /src/revpi_mio.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // SPDX-FileCopyrightText: 2020-2024 KUNBUS GmbH 3 | 4 | #include 5 | 6 | #include "revpi_common.h" 7 | #include "revpi_core.h" 8 | #include "revpi_mio.h" 9 | 10 | /* configurations of MIO modules */ 11 | static struct mio_config mio_list[REVPI_MIO_MAX]; 12 | /* the counter of the MIO module */ 13 | static int mio_cnt; 14 | /* store the sent analog request. 15 | the field i8uChannels of struct SMioAnalogRequestData takes no function here, 16 | but it could be used for the debuging purpose */ 17 | static SMioAnalogRequestData mio_aio_request_last[REVPI_MIO_MAX]; 18 | 19 | static int revpi_mio_cycle_dio(SDevice *dev, SMioDigitalRequestData *req_data, 20 | SMioDigitalResponseData *resp_data) 21 | { 22 | SMioDigitalResponseData resp; 23 | SMioDigitalRequestData req; 24 | int ret; 25 | 26 | /*copy: from process image:output to request*/ 27 | rt_mutex_lock(&piDev_g.lockPI); 28 | memcpy(&req, req_data, sizeof(req)); 29 | rt_mutex_unlock(&piDev_g.lockPI); 30 | 31 | ret = pibridge_req_io(dev->i8uAddress, IOP_TYP1_CMD_DATA, 32 | &req, sizeof(req), &resp, sizeof(resp)); 33 | if (ret != sizeof(resp)) { 34 | pr_debug("MIO addr %2d: dio communication failed (req:%zu,ret:%d)\n", 35 | dev->i8uAddress, sizeof(resp), ret); 36 | 37 | if (ret >= 0) 38 | ret = -EIO; 39 | 40 | return ret; 41 | } 42 | 43 | /*copy: from response to process image:input*/ 44 | rt_mutex_lock(&piDev_g.lockPI); 45 | memcpy(resp_data, &resp, sizeof(*resp_data)); 46 | rt_mutex_unlock(&piDev_g.lockPI); 47 | 48 | return 0; 49 | } 50 | 51 | static int revpi_mio_cycle_aio(SDevice *dev, SMioAnalogRequestData *req_data, 52 | size_t ch_cnt, SMioAnalogResponseData *resp_data) 53 | { 54 | size_t compressed = (MIO_AIO_PORT_CNT - ch_cnt) * sizeof(INT16U); 55 | SMioAnalogResponseData resp; 56 | int ret; 57 | 58 | ret = pibridge_req_io(dev->i8uAddress, IOP_TYP1_CMD_DATA2, 59 | req_data, sizeof(*req_data) - compressed, 60 | &resp, sizeof(resp)); 61 | if (ret != sizeof(resp)) { 62 | pr_debug("MIO addr %2d: aio communication failed (req:%zd,ret:%d)\n", 63 | dev->i8uAddress, sizeof(resp), ret); 64 | 65 | if (ret >= 0) 66 | ret = -EIO; 67 | 68 | return ret; 69 | } 70 | 71 | /*copy: from response to process image*/ 72 | rt_mutex_lock(&piDev_g.lockPI); 73 | memcpy(resp_data, &resp, sizeof(*resp_data)); 74 | rt_mutex_unlock(&piDev_g.lockPI); 75 | 76 | return 0; 77 | } 78 | 79 | /* 80 | compare to get the changed values of channels 81 | input parameters: 82 | a, b: data of two messages 83 | count: count of channels 84 | step: number of bytes for a channel 85 | return: bit map of changed channels 86 | */ 87 | static unsigned long revpi_chnl_cmp(void *a, void *b, int count, int step) 88 | { 89 | unsigned char *pa, *pb; 90 | unsigned long ret = 0; 91 | int i; 92 | 93 | pa = (unsigned char *) a; 94 | pb = (unsigned char *) b; 95 | 96 | for (i = 0; i < count; i++) { 97 | if (memcmp(pa + i * step, pb + i * step, step)) 98 | __set_bit(i, &ret); 99 | } 100 | return ret; 101 | } 102 | 103 | /* 104 | compress the channel, only data of changed channel will be taken 105 | input parameters: 106 | dst, dst: compress from src to dst 107 | bitmap: compress according to 108 | step: number of bytes for a channel 109 | return: the count of channels has been taken. 110 | */ 111 | static unsigned int revpi_chnl_compress(void *dst, void *src, 112 | unsigned long bitmap, int step) 113 | { 114 | unsigned char *d, *s; 115 | unsigned int i, last = __fls(bitmap); 116 | 117 | s = (unsigned char *) src; 118 | d = (unsigned char *) dst; 119 | 120 | for (i = 0; i <= last; i++) { 121 | if (test_bit(i, &bitmap)) { 122 | memcpy(d, s + i * step, step); 123 | d += step; 124 | } 125 | } 126 | return (d - (unsigned char *)dst) / step; 127 | } 128 | 129 | int revpi_mio_cycle(unsigned char devno) 130 | { 131 | SMioAnalogRequestData io_req_ex; 132 | struct mio_img_out *img_out; 133 | SMioAnalogRequestData *last; 134 | struct mio_img_in *img_in; 135 | unsigned int ch_cnt = 0; 136 | SDevice *dev; 137 | int ret; 138 | 139 | dev = RevPiDevice_getDev(devno); 140 | last = &mio_aio_request_last[dev->i8uPriv]; 141 | 142 | img_out = (struct mio_img_out *)(piDev_g.ai8uPI + 143 | dev->i16uOutputOffset); 144 | img_in = (struct mio_img_in *)(piDev_g.ai8uPI + dev->i16uInputOffset); 145 | 146 | ret = revpi_mio_cycle_dio(dev, &img_out->dio, &img_in->dio); 147 | if (ret) 148 | return ret; 149 | 150 | /* for the AIO cycle */ 151 | my_rt_mutex_lock(&piDev_g.lockPI); 152 | io_req_ex.i8uLogicLevel = img_out->aio.i8uLogicLevel; 153 | 154 | io_req_ex.i8uChannels = revpi_chnl_cmp(&last->i16uOutputVoltage, 155 | &img_out->aio.i16uOutputVoltage, 156 | MIO_AIO_PORT_CNT, 2); 157 | /* force to update from process image */ 158 | io_req_ex.i8uChannels |= img_out->aio.i8uChannels; 159 | 160 | if (io_req_ex.i8uChannels) { 161 | memcpy(&last->i16uOutputVoltage, 162 | &img_out->aio.i16uOutputVoltage, 163 | sizeof(unsigned short) * MIO_AIO_PORT_CNT); 164 | 165 | ch_cnt = revpi_chnl_compress(&io_req_ex.i16uOutputVoltage, 166 | &img_out->aio.i16uOutputVoltage, 167 | io_req_ex.i8uChannels, 2); 168 | } 169 | rt_mutex_unlock(&piDev_g.lockPI); 170 | return revpi_mio_cycle_aio(dev, &io_req_ex, ch_cnt, &img_in->aio); 171 | } 172 | 173 | void revpi_mio_reset() 174 | { 175 | mio_cnt = 0; 176 | memset(mio_aio_request_last, 0, sizeof(mio_aio_request_last)); 177 | } 178 | 179 | int revpi_mio_config(unsigned char addr, unsigned short e_cnt, SEntryInfo *ent) 180 | { 181 | struct mio_config *conf; 182 | int arr_idx; 183 | int offset; 184 | int i; 185 | 186 | if (mio_cnt >= REVPI_MIO_MAX) { 187 | pr_err("max. of MIOs(%d) reached(%d)\n", REVPI_MIO_MAX, 188 | mio_cnt); 189 | return -ERANGE; 190 | } 191 | 192 | conf = &mio_list[mio_cnt]; 193 | memset(conf, 0, sizeof(struct mio_config)); 194 | 195 | conf->addr = addr; 196 | 197 | /*0=input (InputThreshold)*/ 198 | conf->aio_i.i8uDirection = 0; 199 | /*1=output(Fixed Output)*/ 200 | conf->aio_o.i8uDirection = 1; 201 | 202 | pr_info("MIO configured(addr:%d, ent-cnt:%d, conf-no:%d, conf-base:%zd)\n", 203 | addr, e_cnt, mio_cnt, MIO_CONF_BASE); 204 | 205 | for (i = 0; i < e_cnt; i++) { 206 | offset = ent[i].i16uOffset; 207 | switch (offset) { 208 | case 0 ... MIO_CONF_BASE - 1: 209 | /*nothing to do for input and output */ 210 | break; 211 | case MIO_CONF_EMOD: 212 | conf->dio.i8uEncoderMode = ent[i].i32uDefault; 213 | break; 214 | case MIO_CONF_IOMOD ... MIO_CONF_PUL -1: 215 | arr_idx = (offset - MIO_CONF_IOMOD) / sizeof(INT8U); 216 | conf->dio.i8uIoMode[arr_idx] = ent[i].i32uDefault; 217 | break; 218 | case MIO_CONF_PUL: 219 | conf->dio.i8uPullup = ent[i].i32uDefault; 220 | break; 221 | case MIO_CONF_PMOD: 222 | conf->dio.i8uPulseRetrigMode = ent[i].i32uDefault; 223 | break; 224 | case MIO_CONF_FPWM ... MIO_CONF_PLEN - 1: 225 | arr_idx = (offset - MIO_CONF_FPWM) / sizeof(INT16U); 226 | conf->dio.i16uPwmFrequency[arr_idx] 227 | = ent[i].i32uDefault; 228 | break; 229 | case MIO_CONF_PLEN ... MIO_CONF_AIM - 1: 230 | arr_idx = (offset - MIO_CONF_PLEN) / sizeof(INT16U); 231 | conf->dio.i16uPulseLength[arr_idx] 232 | = ent[i].i32uDefault; 233 | break; 234 | case MIO_CONF_AIM: 235 | conf->aio_i.i8uIoMode 236 | |= ent[i].i32uDefault << ent[i].i8uBitPos; 237 | break; 238 | case MIO_CONF_THR ... MIO_CONF_WSIZE - 1: 239 | arr_idx = (offset - MIO_CONF_THR) / sizeof(INT16U); 240 | conf->aio_i.i16uVolt[arr_idx] = ent[i].i32uDefault; 241 | break; 242 | case MIO_CONF_WSIZE: 243 | conf->aio_i.i8uMvgAvgWindow = ent[i].i32uDefault; 244 | break; 245 | case MIO_CONF_AOM: 246 | conf->aio_o.i8uIoMode 247 | |= ent[i].i32uDefault << ent[i].i8uBitPos; 248 | break; 249 | case MIO_CONF_OUTV ... MIO_CONF_END - 1: 250 | arr_idx = (offset - MIO_CONF_OUTV) / sizeof(INT16U); 251 | conf->aio_o.i16uVolt[arr_idx] = ent[i].i32uDefault; 252 | break; 253 | default: 254 | pr_warn("unknown config entry(offset:%d)\n", offset); 255 | break; 256 | } 257 | } 258 | 259 | pr_info("dio :%*ph\n", (int) sizeof(conf->dio), &conf->dio); 260 | pr_info("aio-i:%*ph\n", (int) sizeof(conf->aio_i), &conf->aio_i); 261 | pr_info("aio-o:%*ph\n", (int) sizeof(conf->aio_o), &conf->aio_o); 262 | 263 | mio_cnt++; 264 | 265 | return 0; 266 | } 267 | 268 | int revpi_mio_init(unsigned char devno) 269 | { 270 | struct mio_config *conf = NULL; 271 | unsigned char addr; 272 | int ret; 273 | int i; 274 | 275 | addr = RevPiDevice_getDev(devno)->i8uAddress; 276 | 277 | pr_info("MIO Initializing...(devno:%d, addr:%d, conf-base:%zd)\n", 278 | devno, addr, MIO_CONF_BASE); 279 | 280 | for (i = 0; i < mio_cnt; i++) { 281 | pr_info("search mio conf(index:%d, addr:%d)\n", i, 282 | mio_list[i].addr); 283 | 284 | if (mio_list[i].addr == addr) { 285 | conf = &mio_list[i]; 286 | RevPiDevice_getDev(devno)->i8uPriv = (unsigned char) i; 287 | break; 288 | } 289 | } 290 | 291 | if (!conf) { 292 | pr_err("fail to find the mio module(devno:%d)\n", devno); 293 | return -ENODATA; 294 | } 295 | 296 | /*dio*/ 297 | ret = pibridge_req_io(addr, IOP_TYP1_CMD_CFG, 298 | &conf->dio, sizeof(conf->dio), NULL, 0); 299 | if (ret) { 300 | pr_err("talk with mio for conf dio err(devno:%d, ret:%d)\n", 301 | devno, ret); 302 | } 303 | 304 | /*aio in*/ 305 | ret = pibridge_req_io(addr, IOP_TYP1_CMD_DATA4, 306 | &conf->aio_i, sizeof(conf->aio_i), NULL, 0); 307 | if (ret) 308 | pr_err("talk with mio for conf aio_i err(devno:%d, ret:%d)\n", 309 | devno, ret); 310 | 311 | /*aio out*/ 312 | ret = pibridge_req_io(addr, IOP_TYP1_CMD_DATA4, 313 | &conf->aio_o, sizeof(conf->aio_o), NULL, 0); 314 | if (ret) 315 | pr_err("talk with mio for conf aio_o err(devno:%d, ret:%d)\n", 316 | devno, ret); 317 | 318 | pr_info("MIO Initializing finished(devno:%d, addr:%d)\n", devno, addr); 319 | 320 | return 0; 321 | } 322 | -------------------------------------------------------------------------------- /src/revpi_mio.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2020-2023 KUNBUS GmbH 3 | */ 4 | 5 | #ifndef _REVPI_MIO_H_ 6 | #define _REVPI_MIO_H_ 7 | 8 | #include 9 | 10 | #include "IoProtocol.h" 11 | 12 | /************************************************/ 13 | 14 | #define REVPI_MIO_MAX 10 15 | 16 | #define MIO_CONF_BASE sizeof(SMioDigitalRequestData) + \ 17 | sizeof(SMioAnalogRequestData) + \ 18 | sizeof(SMioDigitalResponseData) + \ 19 | sizeof(SMioAnalogResponseData) 20 | 21 | #define MIO_CONF_EMOD MIO_CONF_BASE 22 | #define MIO_CONF_IOMOD (MIO_CONF_EMOD + 1) 23 | #define MIO_CONF_PUL (MIO_CONF_IOMOD + sizeof(INT8U) * MIO_DIO_PORT_CNT) 24 | #define MIO_CONF_PMOD (MIO_CONF_PUL + 1) 25 | #define MIO_CONF_FPWM (MIO_CONF_PMOD + 1) 26 | #define MIO_CONF_PLEN (MIO_CONF_FPWM + sizeof(INT16U) * MIO_PWM_TMR_CNT) 27 | #define MIO_CONF_AIM (MIO_CONF_PLEN + sizeof(INT16U) * MIO_DIO_PORT_CNT) 28 | #define MIO_CONF_THR (MIO_CONF_AIM + 1) 29 | #define MIO_CONF_WSIZE (MIO_CONF_THR + sizeof(INT16U) * MIO_AIO_PORT_CNT) 30 | #define MIO_CONF_AOM (MIO_CONF_WSIZE + 1) 31 | #define MIO_CONF_OUTV (MIO_CONF_AOM + 1) 32 | #define MIO_CONF_END (MIO_CONF_OUTV + sizeof(INT16U) * MIO_AIO_PORT_CNT) 33 | 34 | /* 35 | because of the limitation of length field in UIoProtocolHeader, which has 5 bits, 36 | only maximum 31 bytes of data can be transmitted once, so the mio configurations 37 | is transmitted in 3 times. 38 | 39 | to make the struct mio_config can be directly used as the buffer for the 40 | request IOP_TYP1_CMD_CFG(digital) and IOP_TYP1_CMD_DATA4(analog), complete 41 | structs for digital, analog input and analog output configurations are put here, 42 | despite of the repeated headers 43 | */ 44 | struct mio_config { 45 | u8 addr; 46 | SMioDIOConfigData dio; /*digital configuration*/ 47 | SMioAIOConfigData aio_i; /*analog configuration for input*/ 48 | SMioAIOConfigData aio_o; /*analog configuration for output*/ 49 | }; 50 | 51 | struct mio_img_out { 52 | SMioDigitalRequestData dio; 53 | SMioAnalogRequestData aio; 54 | }; 55 | 56 | struct mio_img_in { 57 | SMioDigitalResponseData dio; 58 | SMioAnalogResponseData aio; 59 | }; 60 | 61 | 62 | int revpi_mio_init(unsigned char devno); 63 | int revpi_mio_config(unsigned char addr, unsigned short ent_cnt, SEntryInfo *ent); 64 | void revpi_mio_reset(void); 65 | int revpi_mio_cycle(unsigned char devno); 66 | #endif /* _REVPI_MIO_H_ */ 67 | -------------------------------------------------------------------------------- /src/revpi_ro.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // SPDX-FileCopyrightText: 2023 KUNBUS GmbH 3 | 4 | // RevPi RO module (Relais Output) 5 | 6 | #include 7 | 8 | #include "piControlMain.h" 9 | #include "revpi_common.h" 10 | #include "revpi_ro.h" 11 | #include "RevPiDevice.h" 12 | 13 | #define REVPI_RO_MAX 10 14 | 15 | struct revpi_ro_img_out { 16 | struct revpi_ro_target_state target_state; 17 | u32 thresh[REVPI_RO_NUM_RELAYS]; 18 | } __attribute__((__packed__)); 19 | 20 | struct revpi_ro_img_in { 21 | struct revpi_ro_status status; 22 | } __attribute__((__packed__)); 23 | 24 | /* Number of registered RO devices */ 25 | static unsigned int num_devices; 26 | 27 | struct ro_config_list_item { 28 | u8 addr; 29 | struct revpi_ro_config config; 30 | }; 31 | 32 | static struct ro_config_list_item ro_config_list[REVPI_RO_MAX]; 33 | 34 | void revpi_ro_reset(void) 35 | { 36 | num_devices = 0; 37 | } 38 | 39 | int revpi_ro_config(u8 addr, int num_entries, SEntryInfo *pEnt) 40 | { 41 | const unsigned int ENTRY_THRESH_FIRST = 2; 42 | const unsigned int ENTRY_THRESH_LAST = 14; 43 | struct ro_config_list_item *itm; 44 | unsigned int thr_idx; 45 | SEntryInfo *entry; 46 | int i; 47 | 48 | if (num_devices >= REVPI_RO_MAX) { 49 | pr_err("max. number of ROs (%u) exceeded\n", REVPI_RO_MAX); 50 | return -1; 51 | } 52 | 53 | itm = &ro_config_list[num_devices]; 54 | memset(itm, 0, sizeof(*itm)); 55 | itm->addr = addr; 56 | 57 | pr_info_dio("%s: RO config done for addr %d \n", __func__, addr); 58 | 59 | for (i = 0; i < num_entries; i++) { 60 | entry = &pEnt[i]; 61 | 62 | /* 63 | * Set initial thresholds for wearout warning (0 means wearout 64 | * warning is deactivated). 65 | */ 66 | if ((entry->i16uOffset >= ENTRY_THRESH_FIRST) && 67 | (entry->i16uOffset <= ENTRY_THRESH_LAST)) { 68 | thr_idx = (entry->i16uOffset - ENTRY_THRESH_FIRST) / 4; 69 | itm->config.thresh[thr_idx] = entry->i32uDefault; 70 | } 71 | } 72 | 73 | num_devices++; 74 | 75 | return 0; 76 | } 77 | 78 | int revpi_ro_init(unsigned int devnum) 79 | { 80 | u8 addr = RevPiDevice_getDev(devnum)->i8uAddress; 81 | struct ro_config_list_item *itm; 82 | int i; 83 | 84 | for (i = 0; i < num_devices; i++) { 85 | itm = &ro_config_list[i]; 86 | 87 | if (itm->addr == addr) 88 | break; 89 | } 90 | 91 | if (i == num_devices) 92 | return 4; // unknown device 93 | 94 | return pibridge_req_io(addr, IOP_TYP1_CMD_CFG, &itm->config, 95 | sizeof(struct revpi_ro_config), NULL, 0); 96 | } 97 | 98 | int revpi_ro_cycle(unsigned int devnum) 99 | { 100 | struct revpi_ro_target_state state_out; 101 | struct revpi_ro_status status_in; 102 | struct revpi_ro_img_out *img_out; 103 | struct revpi_ro_img_in *img_in; 104 | SDevice *dev; 105 | int ret; 106 | 107 | dev = RevPiDevice_getDev(devnum); 108 | 109 | img_out = (struct revpi_ro_img_out *) (piDev_g.ai8uPI + 110 | dev->i16uOutputOffset); 111 | img_in = (struct revpi_ro_img_in *) (piDev_g.ai8uPI + 112 | dev->i16uInputOffset); 113 | 114 | rt_mutex_lock(&piDev_g.lockPI); 115 | state_out = img_out->target_state; 116 | rt_mutex_unlock(&piDev_g.lockPI); 117 | 118 | ret = pibridge_req_io(dev->i8uAddress, IOP_TYP1_CMD_DATA, 119 | &state_out, sizeof(state_out), 120 | &status_in, sizeof(status_in)); 121 | 122 | if (ret != sizeof(status_in)) { 123 | pr_debug("RO addr %2d: communication failed (req:%zu,ret:%d)\n", 124 | dev->i8uAddress, sizeof(status_in), ret); 125 | 126 | if (ret >= 0) 127 | ret = -EIO; 128 | 129 | return ret; 130 | } 131 | 132 | rt_mutex_lock(&piDev_g.lockPI); 133 | img_in->status = status_in; 134 | rt_mutex_unlock(&piDev_g.lockPI); 135 | 136 | return 0; 137 | } 138 | -------------------------------------------------------------------------------- /src/revpi_ro.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-only 2 | * SPDX-FileCopyrightText: 2023 KUNBUS GmbH 3 | * 4 | * RevPi RO module (Relais Output) 5 | */ 6 | 7 | #ifndef _REVPI_RO_H_ 8 | #define _REVPI_RO_H_ 9 | 10 | #include 11 | #include "piControl.h" 12 | 13 | int revpi_ro_init(unsigned int devnum); 14 | void revpi_ro_reset(void); 15 | int revpi_ro_config(u8 addr, int num_entries, SEntryInfo *pEnt); 16 | int revpi_ro_cycle(unsigned int devnum); 17 | 18 | #endif /* REVPI_RO_H_ */ 19 | -------------------------------------------------------------------------------- /src/systick.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | // SPDX-FileCopyrightText: 2016-2023 KUNBUS GmbH 3 | 4 | #include 5 | 6 | #include "bsp/systick/systick.h" 7 | #include "common_define.h" 8 | 9 | //+============================================================================================= 10 | //| Konstanten / constants 11 | //+============================================================================================= 12 | 13 | //+============================================================================================= 14 | //| Variablen / variables 15 | //+============================================================================================= 16 | 17 | static INT32U i32uCounter_s = 0; 18 | //static INT32U i32uCounterStart_s = 0; 19 | 20 | static TBOOL hrtime_initialized; 21 | struct hrtimer hrtime_g; 22 | 23 | 24 | //------------------------------------------------------------------------------------------------- 25 | INT32U kbGetTickCount(void) 26 | { 27 | ktime_t ktime_l; 28 | if (!hrtime_initialized) { 29 | hrtimer_init(&hrtime_g, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); 30 | hrtime_initialized = bTRUE; 31 | } 32 | 33 | ktime_l = hrtimer_cb_get_time(&hrtime_g); 34 | i32uCounter_s = ktime_to_ms(ktime_l); 35 | 36 | return (i32uCounter_s); 37 | } 38 | 39 | //************************************************************************************************* 40 | --------------------------------------------------------------------------------