├── LICENSE.md ├── README.md ├── include ├── iolink.h ├── iolink_dl.h ├── iolink_main.h ├── iolink_max14819.h └── iolink_types.h ├── iol_osal ├── ftdi-spi │ ├── osal_irq.c │ ├── osal_spi.c │ ├── osal_spi_internal.h │ └── unload_built_in_modules.sh ├── include │ ├── osal_irq.h │ └── osal_spi.h ├── linux-spidev │ ├── osal_irq.c │ └── osal_spi.c └── rt-kernel │ ├── osal_irq.c │ └── osal_spi.c ├── iolink_options.h.in ├── samples ├── ifm_sample_app │ ├── README.md │ ├── app_handler.c │ ├── app_handler.h │ ├── app_ifm.c │ ├── app_ifm.h │ ├── app_smi.c │ ├── app_smi.h │ └── ports │ │ ├── linux │ │ └── main.c │ │ ├── rt-kernel │ │ └── main.c │ │ └── windows │ │ └── main.c └── irq_test │ └── irq_test.c ├── src ├── iolink_al.c ├── iolink_al.h ├── iolink_cm.c ├── iolink_cm.h ├── iolink_dl.c ├── iolink_ds.c ├── iolink_ds.h ├── iolink_main.c ├── iolink_max14819_pl.c ├── iolink_max14819_pl.h ├── iolink_ode.c ├── iolink_ode.h ├── iolink_pde.c ├── iolink_pde.h ├── iolink_pl.c ├── iolink_pl.h ├── iolink_pl_hw_drv.h ├── iolink_sm.c └── iolink_sm.h ├── test ├── iolink_test.cpp ├── mocks.cpp ├── mocks.h ├── test_al.cpp ├── test_cm.cpp ├── test_ds.cpp ├── test_ode.cpp ├── test_pde.cpp ├── test_sm.cpp ├── test_spi_usb.cpp └── test_util.h └── version.h.in /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | This software is dual-licensed. 4 | 5 | ## GPL version 3 6 | 7 | This software is distributed under GPLv3. You are allowed to use this 8 | software for an open-source project with a compatible license. 9 | 10 | [GNU GPL license v3](https://www.gnu.org/licenses/gpl-3.0.html) 11 | 12 | ## Commercial license 13 | 14 | This software is also available under a commercial license with 15 | options for support and maintenance. Please contact sales@rt-labs.com 16 | for further details. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | I-Link IO-Link master stack 2 | =========================== 3 | 4 | The RT-Labs IO-Link stack I-Link is used for IO-Link master implementations. 5 | It is easy to use and provides a small footprint. It is especially well suited 6 | for embedded systems where resources are limited and efficiency is crucial. 7 | It is written in C and can be run on bare-metal hardware, an RTOS such as 8 | rt-kernel, or on Linux. The I-Link stack is supplied with full sources 9 | including a porting layer. 10 | 11 | Also, C++ (any version) is supported. 12 | 13 | RT-Labs I-Link is developed according to specification 1.1.3. 14 | 15 | Web resources 16 | ------------- 17 | 18 | * Source repository: [https://github.com/rtlabs-com/i-link](https://github.com/rtlabs-com/i-link) 19 | * Documentation: [https://rt-labs.com/docs/i-link](https://rt-labs.com/docs/i-link) 20 | * RT-Labs (stack integration, certification services and training): [https://rt-labs.com](https://rt-labs.com) 21 | 22 | Features 23 | -------- 24 | 25 | * Porting layer provided 26 | * MAX14819 master transceiver supported 27 | * The sample application currently supports two different devices from IFM: 28 | * An RFID reader (IFM part number: DTI515) 29 | * A display device (IFM part number: E30430) 30 | 31 | Limitations 32 | ----------- 33 | 34 | * For support of other IO-Link devices, code have to be added to the application 35 | 36 | License 37 | ------- 38 | 39 | This software is dual-licensed, with GPL version 3 and a commercial license. See 40 | LICENSE.md for more details. 41 | 42 | Requirements 43 | ------------ 44 | 45 | cmake 46 | 47 | * cmake 3.14 or later 48 | 49 | For Linux: 50 | 51 | * gcc 4.6 or later 52 | 53 | For rt-kernel: 54 | 55 | * Workbench 2018.1 or later 56 | 57 | As an example of a microcontroller we have been using the Infineon XMC4800, 58 | which has an ARM Cortex-M4 running at 144 MHz, with 2 MB Flash and 352 kB RAM. 59 | It runs rt-kernel, and we have tested it with 2 MAX14819 chips, each with 2 60 | IO-Link ports. 61 | 62 | Contributions 63 | ------------- 64 | 65 | Contributions are welcome. If you want to contribute you will need to sign a 66 | Contributor License Agreement and send it to us either by e-mail or by physical 67 | mail. More information is available 68 | on [https://rt-labs.com/contribution](https://rt-labs.com/contribution). 69 | -------------------------------------------------------------------------------- /include/iolink_dl.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | /** 17 | * @file 18 | * @brief Data link layer 19 | * 20 | */ 21 | 22 | #ifndef IOLINK_DL_H 23 | #define IOLINK_DL_H 24 | 25 | #include 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | typedef struct iolink_dl iolink_dl_t; 32 | #include "iolink_main.h" 33 | #include "iolink_options.h" /* IOLINK_MAX_EVENTS */ 34 | #include "osal.h" 35 | 36 | /** 37 | * @file 38 | * 39 | */ 40 | 41 | #define IOLINK_PL_EVENT BIT (0) 42 | #define IOLINK_PL_EVENT_RXRDY BIT (1) 43 | #define IOLINK_PL_EVENT_RXERR BIT (2) 44 | #define IOLINK_PL_EVENT_TXERR BIT (3) 45 | #define IOLINK_PL_EVENT_WURQ BIT (4) 46 | #define IOLINK_PL_EVENT_STATUS BIT (5) 47 | #define IOLINK_DL_EVENT_RESET BIT (9) 48 | #define IOLINK_DL_EVENT_TIMEOUT BIT (10) 49 | #define IOLINK_DL_EVENT_TIMEOUT_TCYC BIT (11) 50 | // #define IOLINK_DL_EVENT_ODH BIT(12) 51 | // #define IOLINK_DL_EVENT_PDH BIT(13) 52 | #define IOLINK_DL_EVENT_MDH BIT (14) 53 | #define IOLINK_DL_EVENT_MH BIT (15) 54 | 55 | typedef enum 56 | { 57 | IOL_DL_TIMER_NONE, 58 | IOL_DL_TIMER_TINITCYC_MH, 59 | IOL_DL_TIMER_ISDUTIME_IH, 60 | 61 | IOL_DL_TIMER_TDMT, 62 | IOL_DL_TIMER_TDSIO, 63 | IOL_DL_TIMER_TDWU, 64 | IOL_DL_TIMER_TSD, 65 | } dl_timer_t; 66 | 67 | /* 68 | * State machine states 69 | */ 70 | typedef enum 71 | { 72 | IOL_DL_MDH_ST_IDLE_0, 73 | IOL_DL_MDH_ST_ESTCOM_1, 74 | IOL_DL_MDH_ST_STARTUP_2, 75 | IOL_DL_MDH_ST_PREOPERATE_3, 76 | IOL_DL_MDH_ST_OPERATE_4, 77 | } dl_mdh_st_t; 78 | 79 | typedef enum 80 | { 81 | IOL_DL_MH_ST_INACTIVE_0, 82 | IOL_DL_MH_ST_AW_REPLY_1, 83 | IOL_DL_MH_ST_STARTUP_2, 84 | IOL_DL_MH_ST_RESPONSE_3, 85 | IOL_DL_MH_ST_AW_REPLY_4, 86 | IOL_DL_MH_ST_ERRORHANDLING_5, 87 | IOL_DL_MH_ST_PREOPERATE_6, 88 | IOL_DL_MH_ST_GETOD_7, 89 | IOL_DL_MH_ST_RESPONSE_8, 90 | IOL_DL_MH_ST_AW_REPLY_9, 91 | IOL_DL_MH_ST_ERRORHANDLING_10, 92 | IOL_DL_MH_ST_CHECKHANDLER_11, 93 | IOL_DL_MH_ST_OPERATE_12, 94 | IOL_DL_MH_ST_GETPD_13, 95 | IOL_DL_MH_ST_GETOD_14, 96 | IOL_DL_MH_ST_RESPONSE_15, 97 | IOL_DL_MH_ST_AW_REPLY_16, 98 | IOL_DL_MH_ST_ERRORHANDLING_17, 99 | } dl_mh_st_t; 100 | 101 | typedef enum 102 | { 103 | IOL_DL_INTERLEAVE_NONE, 104 | IOL_DL_INTERLEAVE_PD, 105 | IOL_DL_INTERLEAVE_OD, 106 | } dl_interleave_t; 107 | 108 | typedef enum 109 | { 110 | IOL_DL_PDH_ST_INACTIVE_0, 111 | IOL_DL_PDH_ST_PDSINGLE_1, 112 | IOL_DL_PDH_ST_PDININTERLEAVE_2, 113 | IOL_DL_PDH_ST_PDOUTINTERLEAVE_3, 114 | } dl_pdh_st_t; 115 | 116 | typedef enum 117 | { 118 | IOL_DL_ODH_ST_INACTIVE_0, 119 | IOL_DL_ODH_ST_ISDU_1, 120 | IOL_DL_ODH_ST_COMMAND_2, 121 | IOL_DL_ODH_ST_EVENT_3, 122 | } dl_odh_st_t; 123 | 124 | typedef enum 125 | { 126 | IOL_DL_ISDUH_ST_INACTIVE_0, 127 | IOL_DL_ISDUH_ST_IDLE_1, 128 | IOL_DL_ISDUH_ST_ISDUREQUEST_2, 129 | IOL_DL_ISDUH_ST_ISDUWAIT_3, 130 | IOL_DL_ISDUH_ST_ISDUERROR_4, 131 | IOL_DL_ISDUH_ST_ISDURESPONSE_5, 132 | } dl_isduh_st_t; 133 | 134 | typedef enum 135 | { 136 | IOL_DL_CMDH_ST_INACTIVE_0, 137 | IOL_DL_CMDH_ST_IDLE_1, 138 | IOL_DL_CMDH_ST_MASTERCOMMAND_2, 139 | } dl_cmdh_st_t; 140 | 141 | typedef enum 142 | { 143 | IOL_DL_EVH_ST_INACTIVE_0, 144 | IOL_DL_EVH_ST_IDLE_1, 145 | IOL_DL_EVH_ST_READEVENT_2, 146 | IOL_DL_EVH_ST_SIGNALEVENT_3, 147 | IOL_DL_EVH_ST_EVENTCONFIRMATION_4, 148 | } dl_evh_st_t; 149 | 150 | /* 151 | * Internals 152 | */ 153 | typedef enum 154 | { 155 | IOL_CHCMD_INACTIVE, 156 | IOL_CHCMD_ACTIVE, 157 | } CHCmd_t; 158 | 159 | typedef enum 160 | { 161 | IOL_OHCMD_INACTIVE, 162 | IOL_OHCMD_ACTIVE, 163 | } OHCmd_t; 164 | 165 | typedef enum 166 | { 167 | IOL_PHCMD_INACTIVE, 168 | IOL_PHCMD_ACTIVE, 169 | IOL_PHCMD_SINGLE, 170 | IOL_PHCMD_INTERLEAVE, 171 | } PHCmd_t; 172 | 173 | typedef enum 174 | { 175 | IOL_IHCMD_INACTIVE, 176 | IOL_IHCMD_ACTIVE, 177 | } IHCmd_t; 178 | 179 | typedef enum 180 | { 181 | IOL_EHCMD_INACTIVE, 182 | IOL_EHCMD_ACTIVE, 183 | } EHCmd_t; 184 | 185 | typedef enum 186 | { 187 | IOL_MHCMD_INACTIVE, 188 | IOL_MHCMD_COM1, 189 | IOL_MHCMD_COM2, 190 | IOL_MHCMD_COM3, 191 | IOL_MHCMD_ACTIVE, 192 | IOL_MHCMD_STARTUP, 193 | IOL_MHCMD_PREOPERATE, 194 | IOL_MHCMD_OPERATE, 195 | } MHCmd_t; 196 | 197 | typedef enum 198 | { 199 | IOL_MHRW_NONE, 200 | IOL_MHRW_WRITE, 201 | IOL_MHRW_READ, 202 | IOL_MHRW_WRITEPARAM, 203 | IOL_MHRW_READPARAM, 204 | IOL_MHRW_ISDUTRANSPORT, 205 | IOL_MHRW_ISDUABORT, 206 | IOL_MHRW_WRITEDEVICEMODE, 207 | } MHRWCmd; 208 | 209 | typedef enum 210 | { 211 | IOL_TRIGGERED_NONE, 212 | IOL_TRIGGERED_DEVICE_MESSAGE, 213 | IOL_TRIGGERED_MASTER_MESSAGE, 214 | } iol_trigger_t; 215 | 216 | typedef struct 217 | { 218 | dl_pdh_st_t state; 219 | PHCmd_t phcmd; 220 | iol_trigger_t trigger; 221 | uint8_t pd_address; 222 | uint8_t pd_rxlen; 223 | uint8_t pd_txlen; 224 | uint8_t pdoutdata[IOLINK_PD_MAX_SIZE]; 225 | uint8_t pdindata[IOLINK_PD_MAX_SIZE]; 226 | uint8_t * pdout_buffer; 227 | bool pd_valid; 228 | } pd_handler_t; 229 | 230 | typedef struct 231 | { 232 | dl_odh_st_t state; 233 | OHCmd_t ohcmd; 234 | iol_trigger_t trigger; 235 | uint8_t od_rxlen; 236 | uint8_t od_txlen; 237 | uint8_t data_addr; 238 | uint8_t data_value; 239 | uint8_t * odout_buffer; 240 | } od_handler_t; 241 | 242 | typedef struct 243 | { 244 | dl_mh_st_t state; 245 | MHCmd_t mhcmd; 246 | MHRWCmd rwcmd; 247 | uint8_t pd_rxlen; 248 | uint8_t pd_txlen; 249 | uint8_t od_len; 250 | uint8_t cks; 251 | uint8_t retry; 252 | dl_interleave_t interleave; 253 | } message_h_t; 254 | 255 | typedef struct 256 | { 257 | dl_mdh_st_t state; 258 | iolink_dl_mode_t dl_mode; 259 | iolink_mhinfo_t mhinfo; 260 | } mode_h_t; 261 | 262 | typedef struct 263 | { 264 | dl_cmdh_st_t state; 265 | CHCmd_t chcmd; 266 | iolink_controlcode_t control_code; 267 | uint8_t master_command; 268 | } command_h_t; 269 | 270 | typedef struct 271 | { 272 | dl_isduh_st_t state; 273 | IHCmd_t ihcmd; 274 | uint8_t total_isdu_len; 275 | uint8_t current_isdu_seg; 276 | uint8_t total_isdu_seg; 277 | uint8_t isdu_data[IOLINK_ISDU_MAX_SIZE]; 278 | uint32_t isdu_timer; 279 | bool data_dir_read; 280 | } isdu_h_t; 281 | 282 | typedef struct 283 | { 284 | uint8_t event_qualifier; 285 | uint8_t event_code_msb; 286 | uint8_t event_code_lsb; 287 | } event_t; 288 | 289 | typedef struct 290 | { 291 | dl_evh_st_t state; 292 | EHCmd_t ehcmd; 293 | bool event_flag; 294 | bool event_confirmation; 295 | uint8_t ev_addr; 296 | uint8_t status_code; 297 | uint8_t num_entries; 298 | uint8_t ev_cnt; 299 | uint8_t ev_current; 300 | event_t events[IOLINK_MAX_EVENTS]; 301 | } event_h_t; 302 | 303 | typedef struct iolink_dl 304 | { 305 | mode_h_t mode_handler; 306 | message_h_t message_handler; 307 | pd_handler_t pd_handler; 308 | od_handler_t od_handler; 309 | command_h_t cmd_handler; 310 | isdu_h_t isdu_handler; 311 | event_h_t event_handler; 312 | 313 | iolink_baudrate_t baudrate; 314 | uint8_t cycbyte; 315 | iolink_msequencetype_t mseq; 316 | 317 | os_mutex_t * mtx; 318 | os_thread_t * thread; 319 | os_event_t * event; 320 | uint32_t triggered_events; 321 | os_timer_t * timer; 322 | dl_timer_t timer_type; 323 | bool timer_elapsed; 324 | os_timer_t * timer_tcyc; 325 | bool timer_tcyc_elapsed; 326 | 327 | bool dataready; 328 | bool rxerror; 329 | bool txerror; 330 | bool rxtimeout; 331 | 332 | #if IOLINK_HW == IOLINK_HW_MAX14819 333 | bool first_read_min_cycl; 334 | uint8_t devdly; 335 | uint8_t cqerr; 336 | uint8_t txbuffer[IOLINK_RXTX_BUFFER_SIZE]; 337 | uint8_t rxbuffer[IOLINK_RXTX_BUFFER_SIZE]; 338 | uint8_t tinitcyc; 339 | #endif 340 | } iolink_dl_t; 341 | 342 | /* 343 | * DL-B services 344 | */ 345 | iolink_error_t DL_Read_req (iolink_port_t * port, uint8_t address); 346 | iolink_error_t DL_Write_req (iolink_port_t * port, uint8_t address, uint8_t value); 347 | iolink_error_t DL_SetMode_req ( 348 | iolink_port_t * port, 349 | iolink_dl_mode_t mode, 350 | iolink_mode_vl_t * valuelist); 351 | iolink_error_t DL_Write_Devicemode_req ( 352 | iolink_port_t * port, 353 | iolink_dl_mode_t devicemode); 354 | iolink_error_t DL_ReadParam_req (iolink_port_t * port, uint16_t address); 355 | iolink_error_t DL_WriteParam_req ( 356 | iolink_port_t * port, 357 | uint16_t address, 358 | uint8_t value); 359 | iolink_error_t DL_ISDUTransport_req ( 360 | iolink_port_t * port, 361 | iolink_isdu_vl_t * valuelist); 362 | iolink_error_t DL_Control_req ( 363 | iolink_port_t * port, 364 | iolink_controlcode_t controlcode); 365 | iolink_error_t DL_EventConf_req (iolink_port_t * port); 366 | iolink_error_t DL_PDOutputUpdate_req (iolink_port_t * port, uint8_t * outputdata); 367 | iolink_error_t DL_PDOutputGet_req ( 368 | iolink_port_t * port, 369 | uint8_t * len, 370 | uint8_t * data); 371 | 372 | /* 373 | * Function interface 374 | */ 375 | 376 | /** 377 | * This function instantiates the data link layer for one port, 378 | * and starts a thread handling that port. 379 | * 380 | * @param port Resulting port information struct 381 | * @param thread_prio Priority of the thread 382 | * @param thread_stack_size Stack size of the thread 383 | */ 384 | void iolink_dl_instantiate ( 385 | iolink_port_t * port, 386 | unsigned int thread_prio, 387 | size_t thread_stack_size); 388 | 389 | /** 390 | * This function resets the data link layer for one port to an initial state. 391 | * 392 | * @param port Port information struct 393 | */ 394 | void iolink_dl_reset (iolink_port_t * port); 395 | 396 | bool iolink_dl_get_pd_valid_status (iolink_port_t * port); 397 | 398 | #ifdef __cplusplus 399 | } 400 | #endif 401 | 402 | #endif /* IOLINK_DL_H */ 403 | -------------------------------------------------------------------------------- /include/iolink_main.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | /** 17 | * @file 18 | * @brief Handler 19 | * 20 | */ 21 | 22 | #ifndef IOLINK_MAIN_H 23 | #define IOLINK_MAIN_H 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include "iolink_types.h" 30 | #include "iolink.h" 31 | 32 | #include 33 | 34 | /** 35 | * Structure containing all information for one IO-Link port. 36 | */ 37 | typedef struct iolink_port iolink_port_t; 38 | 39 | typedef enum iservice 40 | { 41 | IOL_ISERVICE_MASTER_NO_SERVICE = 0b0000, 42 | IOL_ISERVICE_DEVICE_NO_SERVICE = 0b0000, 43 | IOL_ISERVICE_MASTER_WRITE_8I = 0b0001, 44 | IOL_ISERVICE_MASTER_WRITE_8I_8SI = 0b0010, 45 | IOL_ISERVICE_MASTER_WRITE_16I_16SI = 0b0011, 46 | IOL_ISERVICE_DEVICE_WRITE_RESPONSE_NEG = 0b0100, 47 | IOL_ISERVICE_DEVICE_WRITE_RESPONSE_POS = 0b0101, 48 | IOL_ISERVICE_MASTER_READ_8I = 0b1001, 49 | IOL_ISERVICE_MASTER_READ_8I_8SI = 0b1010, 50 | IOL_ISERVICE_MASTER_READ_16I_16SI = 0b1011, 51 | IOL_ISERVICE_DEVICE_READ_RESPONSE_NEG = 0b1100, 52 | IOL_ISERVICE_DEVICE_READ_RESPONSE_POS = 0b1101, 53 | IOL_ISERVICE_MAX = 0b1111, 54 | } iservice_t; 55 | 56 | typedef struct iolink_smi_service_req 57 | { 58 | iolink_arg_block_id_t exp_arg_block_id; 59 | uint16_t arg_block_len; 60 | arg_block_t * arg_block; 61 | iolink_smi_errortypes_t result; 62 | } iolink_smi_service_req_t; 63 | 64 | typedef enum iolink_job_type 65 | { 66 | IOLINK_JOB_NONE, 67 | IOLINK_JOB_PD_EVENT, 68 | IOLINK_JOB_SM_OPERATE_REQ, 69 | IOLINK_JOB_SM_SET_PORT_CFG_REQ, 70 | IOLINK_JOB_DL_MODE_IND, 71 | IOLINK_JOB_DL_READ_CNF, 72 | IOLINK_JOB_DL_WRITE_CNF, 73 | IOLINK_JOB_DL_WRITE_DEVMODE_CNF, 74 | IOLINK_JOB_DL_READPARAM_CNF, 75 | IOLINK_JOB_DL_WRITEPARAM_CNF, 76 | IOLINK_JOB_DL_ISDU_TRANS_CNF, 77 | IOLINK_JOB_DL_PDINPUT_TRANS_IND, 78 | IOLINK_JOB_SM_PORT_MODE_IND, 79 | IOLINK_JOB_DL_EVENT_IND, 80 | IOLINK_JOB_DL_CONTROL_IND, 81 | IOLINK_JOB_DS_STARTUP, 82 | IOLINK_JOB_DS_DELETE, 83 | IOLINK_JOB_DS_INIT, 84 | IOLINK_JOB_DS_UPLOAD, 85 | IOLINK_JOB_DS_READY, 86 | IOLINK_JOB_DS_CHANGE, 87 | IOLINK_JOB_DS_FAULT, 88 | IOLINK_JOB_OD_START, 89 | IOLINK_JOB_OD_STOP, 90 | IOLINK_JOB_AL_CONTROL_CNF, 91 | IOLINK_JOB_AL_READ_REQ, 92 | IOLINK_JOB_AL_READ_CNF, 93 | IOLINK_JOB_AL_WRITE_CNF, 94 | IOLINK_JOB_AL_WRITE_REQ, 95 | 96 | IOLINK_JOB_AL_ABORT, 97 | IOLINK_JOB_AL_EVENT_RSP, 98 | IOLINK_JOB_AL_EVENT_REQ, 99 | IOLINK_JOB_SMI_MASTERIDENT, 100 | IOLINK_JOB_SMI_PORTCONFIGURATION, 101 | IOLINK_JOB_SMI_READBACKPORTCONFIGURATION, 102 | IOLINK_JOB_SMI_PORTSTATUS, 103 | IOLINK_JOB_SMI_DEVICE_WRITE, 104 | IOLINK_JOB_SMI_DEVICE_READ, 105 | IOLINK_JOB_SMI_PARAM_READ, 106 | IOLINK_JOB_SMI_PARAM_WRITE, 107 | 108 | IOLINK_JOB_PERIODIC, 109 | 110 | IOLINK_JOB_EXIT, 111 | } iolink_job_type_t; 112 | 113 | /** Generic job */ 114 | typedef struct iolink_job 115 | { 116 | iolink_job_type_t type; 117 | iolink_port_t * port; 118 | void (*callback) (struct iolink_job * job); 119 | union 120 | { 121 | iolink_mhmode_t dl_mode; 122 | struct 123 | { 124 | uint8_t data_len; 125 | const uint8_t * data; 126 | } pd_event; 127 | struct 128 | { 129 | iolink_smp_parameterlist_t paramlist; 130 | } sm_setportcfg_req; 131 | struct 132 | { 133 | uint8_t addr; 134 | uint8_t val; /* Only used for Read */ 135 | iolink_status_t stat; 136 | iservice_t qualifier; 137 | } dl_rw_cnf; 138 | struct 139 | { 140 | const uint8_t * data; 141 | uint8_t data_len; 142 | iolink_smi_errortypes_t errortype; 143 | } al_read_cnf; 144 | struct 145 | { 146 | iolink_smi_errortypes_t errortype; 147 | } al_write_cnf; 148 | struct 149 | { 150 | uint16_t index; 151 | uint8_t subindex; 152 | uint8_t val; 153 | void (*al_read_cb) ( 154 | iolink_port_t * port, 155 | uint8_t len, 156 | const uint8_t * data, 157 | iolink_smi_errortypes_t errortype); 158 | } al_read_req; 159 | struct 160 | { 161 | uint16_t index; 162 | uint8_t subindex; 163 | uint8_t length; 164 | const uint8_t * data; 165 | void (*al_write_cb) ( 166 | iolink_port_t * port, 167 | iolink_smi_errortypes_t errortype); 168 | } al_write_req; 169 | struct 170 | { 171 | iolink_controlcode_t controlcode; 172 | } dl_control_ind; 173 | struct 174 | { 175 | iolink_status_t errorinfo; 176 | iolink_dl_mode_t mode; 177 | } dl_write_devicemode_cnf; 178 | struct 179 | { 180 | diag_entry_t event; 181 | uint8_t eventsleft; 182 | } dl_event_ind; 183 | struct 184 | { 185 | iolink_ds_fault_t fault; 186 | } ds_fault; 187 | struct 188 | { 189 | iolink_sm_portmode_t mode; 190 | } sm_port_mode_ind; 191 | struct 192 | { 193 | portconfiglist_t portcfg; 194 | } ds_init; 195 | iolink_smi_service_req_t smi_req; 196 | }; 197 | } iolink_job_t; 198 | 199 | typedef struct 200 | { 201 | iolink_mhmode_t mh_mode; 202 | iolink_mhmode_t comrate; 203 | } iolink_sm_temp_t; 204 | 205 | typedef struct iolink_al_port iolink_al_port_t; 206 | typedef struct iolink_cm_port iolink_cm_port_t; 207 | typedef struct iolink_ds_port iolink_ds_port_t; 208 | typedef struct iolink_ode_port iolink_ode_port_t; 209 | typedef struct iolink_pde_port iolink_pde_port_t; 210 | typedef struct iolink_pl_port iolink_pl_port_t; 211 | typedef struct iolink_sm_port iolink_sm_port_t; 212 | typedef struct iolink_dl iolink_dl_t; 213 | 214 | typedef struct iolink_port_info 215 | { 216 | uint32_t deviceid; 217 | uint16_t vendorid; 218 | uint16_t functionid; 219 | uint8_t revisionid; 220 | uint8_t cycletime; 221 | iolink_port_quality_info_t port_quality_info; 222 | iolink_port_status_info_t port_status_info; 223 | iolink_transmission_rate_t transmission_rate; 224 | uint8_t serialnumber[16]; 225 | } iolink_port_info_t; 226 | 227 | iolink_job_t * iolink_fetch_avail_job (iolink_port_t * port); 228 | iolink_job_t * iolink_fetch_avail_api_job (iolink_port_t * port); 229 | #ifdef UNIT_TEST 230 | bool iolink_post_job (iolink_port_t * port, iolink_job_t * job); 231 | #else 232 | void iolink_post_job_with_type_and_callback ( 233 | iolink_port_t * port, 234 | iolink_job_t * job, 235 | iolink_job_type_t type, 236 | void (*callback) (struct iolink_job * job)); 237 | #endif 238 | bool iolink_post_job_pd_event ( 239 | iolink_port_t * port, 240 | uint32_t timeout, 241 | uint8_t data_len, 242 | const uint8_t * data); 243 | 244 | void iolink_smi_cnf ( 245 | iolink_port_t * port, 246 | iolink_arg_block_id_t ref_arg_block_id, 247 | uint16_t arg_block_len, 248 | arg_block_t * arg_block); 249 | void iolink_smi_voidblock_cnf ( 250 | iolink_port_t * port, 251 | iolink_arg_block_id_t ref_arg_block_id); 252 | void iolink_smi_joberror_ind ( 253 | iolink_port_t * port, 254 | iolink_arg_block_id_t exp_arg_block_id, 255 | iolink_arg_block_id_t ref_arg_block_id, 256 | iolink_smi_errortypes_t error); 257 | 258 | /** 259 | * Get port information 260 | * 261 | * This function returns the port information struct for the given port. 262 | * 263 | * @param master Master information struct 264 | * @param portnumber Number of the port 265 | * @return Pointer to the port information struct 266 | */ 267 | iolink_port_t * iolink_get_port (iolink_m_t * master, uint8_t portnumber); 268 | 269 | uint8_t iolink_get_portnumber (iolink_port_t * port); 270 | 271 | uint8_t iolink_get_port_cnt (iolink_port_t * port); 272 | 273 | iolink_port_info_t * iolink_get_port_info (iolink_port_t * port); 274 | 275 | const iolink_smp_parameterlist_t * iolink_get_paramlist (iolink_port_t * port); 276 | 277 | iolink_transmission_rate_t iolink_get_transmission_rate (iolink_port_t * port); 278 | 279 | iolink_al_port_t * iolink_get_al_ctx (iolink_port_t * port); 280 | iolink_cm_port_t * iolink_get_cm_ctx (iolink_port_t * port); 281 | 282 | /** 283 | * Get the data link layer context 284 | * 285 | * @param port Port information 286 | * @return Pointer to data link layer struct 287 | */ 288 | iolink_dl_t * iolink_get_dl_ctx (iolink_port_t * port); 289 | 290 | iolink_ds_port_t * iolink_get_ds_ctx (iolink_port_t * port); 291 | iolink_ode_port_t * iolink_get_ode_ctx (iolink_port_t * port); 292 | iolink_pde_port_t * iolink_get_pde_ctx (iolink_port_t * port); 293 | iolink_pl_port_t * iolink_get_pl_ctx (iolink_port_t * port); 294 | iolink_sm_port_t * iolink_get_sm_ctx (iolink_port_t * port); 295 | 296 | #ifdef __cplusplus 297 | } 298 | #endif 299 | 300 | #endif /* IOLINK_MAIN_H */ 301 | -------------------------------------------------------------------------------- /include/iolink_max14819.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifndef IOLINK_MAX14819_H 17 | #define IOLINK_MAX14819_H 18 | 19 | #include 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | /** 26 | * @file 27 | * @brief API for initialization of the MAX14819 chip 28 | * 29 | */ 30 | 31 | #define MAX14819_CQCFG_CQFILTEREN BIT(0) 32 | #define MAX14819_CQCFG_DRVDIS BIT(1) 33 | #define MAX14819_CQCFG_PUSHPUL BIT(2) 34 | #define MAX14819_CQCFG_NPN BIT(3) 35 | #define MAX14819_CQCFG_SINKSEL(x) (((x) & 0x03) << 4) 36 | #define MAX14819_CQCFG_SINKSEL_MASK MAX14819_CQCFG_SINKSEL (0x3) 37 | #define MAX14819_CQCFG_SOURCESINK BIT(6) 38 | #define MAX14819_CQCFG_IEC3TH BIT(7) 39 | 40 | #define MAX14819_CLOCK_XTALEN BIT (0) 41 | #define MAX14819_CLOCK_EXTCLKEN BIT (1) 42 | #define MAX14819_CLOCK_CLKDIV(x) (((x) & 0x03) << 2) 43 | #define MAX14819_CLOCK_CLKDIV_MASK MAX14819_CLOCK_CLKDIV (0x03) 44 | #define MAX14819_CLOCK_CLKOEN BIT (4) 45 | #define MAX14819_CLOCK_EXTCLKMIS BIT (5) 46 | #define MAX14819_CLOCK_TXTXENDIS BIT (6) 47 | #define MAX14819_CLOCK_VCCWARNEN BIT (7) 48 | 49 | #define MAX14819_IOSTCFG_DICSINK BIT (0) 50 | #define MAX14819_IOSTCFG_DICSOURCE BIT (1) 51 | #define MAX14819_IOSTCFG_DIEC3TH BIT (2) 52 | #define MAX14819_IOSTCFG_DIFILTEREN BIT (3) 53 | #define MAX14819_IOSTCFG_TX BIT (4) 54 | #define MAX14819_IOSTCFG_TXEN BIT (5) 55 | #define MAX14819_IOSTCFG_CQLEVEL BIT (6) 56 | #define MAX14819_IOSTCFG_DILEVEL BIT (7) 57 | 58 | #define MAX14819_LPCNFG_LPEN BIT (0) 59 | #define MAX14819_LPCNFG_LPCLIMDIS BIT (1) 60 | #define MAX14819_LPCNFG_LPCL2X BIT (2) 61 | #define MAX14819_LPCNFG_LPBL(x) (((x) & 0x03) << 3) 62 | #define MAX14819_LPCNFG_LPBL_MASK MAX14819_LPCNFG_BLA (0x03) 63 | #define MAX14819_LPCNFG_LPDYNBL BIT (5) 64 | #define MAX14819_LPCNFG_LPRT(x) (((x) & 0x03) << 6) 65 | #define MAX14819_LPCNFG_LPRT_MASK MAX14819_LPCNFG_BLA (0x03) 66 | 67 | typedef struct iolink_14819_drv iolink_14819_drv_t; 68 | 69 | /** 70 | * IO-Link MAX14819 driver configuration 71 | */ 72 | typedef struct iolink_14819_cfg 73 | { 74 | /** SPI address of the transceiver */ 75 | uint8_t chip_address; 76 | 77 | /** IRQ line used to react to events */ 78 | uint32_t chip_irq; 79 | 80 | /** Identification of the SPI slave */ 81 | const char * spi_slave_name; 82 | 83 | /** Initial value of the InterruptEn register */ 84 | uint8_t IntE; 85 | 86 | /** Initial value of the CQCtrlA register */ 87 | uint8_t CQCtrlA; 88 | 89 | /** Initial value of the CQCtrlB register */ 90 | uint8_t CQCtrlB; 91 | 92 | /** Initial value of the LEDCtrl register */ 93 | uint8_t LEDCtrl; 94 | 95 | /** Initial value of the CQCfgA register */ 96 | uint8_t CQCfgA; 97 | 98 | /** Initial value of the CQCfgB register */ 99 | uint8_t CQCfgB; 100 | 101 | /** Initial value of the LPCnfgA register */ 102 | uint8_t LPCnfgA; 103 | 104 | /** Initial value of the LPCnfgB register */ 105 | uint8_t LPCnfgB; 106 | 107 | /** Initial value of the IOStCfgA register */ 108 | uint8_t IOStCfgA; 109 | 110 | /** Initial value of the IOStCfgB register */ 111 | uint8_t IOStCfgB; 112 | 113 | /** Initial value of the DrvCurrLim register */ 114 | uint8_t DrvCurrLim; 115 | 116 | /** Initial value of the Clock register */ 117 | uint8_t Clock; 118 | 119 | /** Optional function to read chip registers from the application */ 120 | void (*register_read_reg_fn) (void * read_reg_function); 121 | 122 | } iolink_14819_cfg_t; 123 | 124 | /** 125 | * Initialises a iolink_max14819 driver instance and registers 126 | * the instance. 127 | * 128 | * May only be called once per hardware block. 129 | * 130 | * @param cfg Driver configuration. 131 | * @return Driver handle or NULL on failure. 132 | */ 133 | iolink_hw_drv_t * iolink_14819_init (const iolink_14819_cfg_t * cfg); 134 | 135 | /** 136 | * Dump contents of all hardware registers to the log. 137 | * 138 | * Calls os_log() with current value of each hardware register. 139 | * 140 | * May be used for debugging purposes. 141 | * 142 | * @param drv Driver handle. 143 | */ 144 | void iolink_14819_dump_registers (iolink_hw_drv_t * drv); 145 | 146 | #ifdef __cplusplus 147 | } 148 | #endif 149 | 150 | #endif /* IOLINK_MAX14819_H */ 151 | -------------------------------------------------------------------------------- /include/iolink_types.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2021 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifndef IOLINK_TYPES_H 17 | #define IOLINK_TYPES_H 18 | 19 | #include 20 | #include 21 | 22 | /** 23 | * @file 24 | * 25 | */ 26 | 27 | #define IOLINK_RXTX_BUFFER_SIZE 64 28 | 29 | #define IOLINK_OD_MAX_SIZE 32 30 | #define IOLINK_ISDU_MAX_DATA_SIZE 238 31 | /* I-Service and len + ExtLen + CHKPDU = 3 */ 32 | #define IOLINK_ISDU_MAX_SIZE (IOLINK_ISDU_MAX_DATA_SIZE + 3) 33 | 34 | #define IOL_DIR_PARAM_REV_V10 0x10 35 | #define IOL_DIR_PARAM_REV_V11 0x11 36 | 37 | typedef enum 38 | { 39 | IOLINK_TARGET_MODE_INACTIVE, 40 | IOLINK_TARGET_MODE_DI, 41 | IOLINK_TARGET_MODE_DO, 42 | IOLINK_TARGET_MODE_COM1, 43 | IOLINK_TARGET_MODE_COM2, 44 | IOLINK_TARGET_MODE_COM3, 45 | } iolink_target_mode_t; 46 | 47 | typedef enum 48 | { 49 | IOLINK_SMTARGET_MODE_CFGCOM = 0, 50 | IOLINK_SMTARGET_MODE_AUTOCOM, 51 | IOLINK_SMTARGET_MODE_INACTIVE, 52 | IOLINK_SMTARGET_MODE_DI, 53 | IOLINK_SMTARGET_MODE_DO, 54 | } iolink_sm_target_mode_t; 55 | 56 | typedef enum 57 | { 58 | IOLINK_DLMODE_INACTIVE, 59 | IOLINK_DLMODE_STARTUP, 60 | IOLINK_DLMODE_PREOPERATE, 61 | IOLINK_DLMODE_OPERATE, 62 | } iolink_dl_mode_t; 63 | 64 | typedef enum 65 | { 66 | IOLINK_BAUDRATE_NONE, 67 | IOLINK_BAUDRATE_AUTO, 68 | IOLINK_BAUDRATE_COM1, 69 | IOLINK_BAUDRATE_COM2, 70 | IOLINK_BAUDRATE_COM3, 71 | } iolink_baudrate_t; 72 | 73 | typedef enum 74 | { 75 | IOLINK_INSPECTIONLEVEL_NO_CHECK, 76 | IOLINK_INSPECTIONLEVEL_TYPE_COMP, 77 | IOLINK_INSPECTIONLEVEL_IDENTICAL, /* NOTE: IO-Link Interface Spec v1.1.3 78 | * Chapter 9.2.2.2 Optional, not 79 | * recommended for new developments 80 | */ 81 | } iolink_inspectionlevel_t; 82 | 83 | typedef enum 84 | { 85 | IOLINK_MHMODE_INACTIVE, 86 | IOLINK_MHMODE_COM1, 87 | IOLINK_MHMODE_COM2, 88 | IOLINK_MHMODE_COM3, 89 | IOLINK_MHMODE_COMLOST, 90 | IOLINK_MHMODE_ESTABCOM, 91 | IOLINK_MHMODE_STARTUP, 92 | IOLINK_MHMODE_PREOPERATE, 93 | IOLINK_MHMODE_OPERATE, 94 | } iolink_mhmode_t; 95 | 96 | typedef enum 97 | { 98 | IOLINK_SM_PORTMODE_INACTIVE, 99 | IOLINK_SM_PORTMODE_DI, 100 | IOLINK_SM_PORTMODE_DO, 101 | IOLINK_SM_PORTMODE_COMREADY, 102 | IOLINK_SM_PORTMODE_OPERATE, 103 | IOLINK_SM_PORTMODE_COMLOST, 104 | IOLINK_SM_PORTMODE_REVISION_FAULT, 105 | IOLINK_SM_PORTMODE_COMP_FAULT, 106 | IOLINK_SM_PORTMODE_SERNUM_FAULT, 107 | IOLINK_SM_PORTMODE_CYCTIME_FAULT, 108 | } iolink_sm_portmode_t; 109 | 110 | typedef enum 111 | { 112 | IOLINK_STATUS_NO_ERROR = 0, 113 | IOLINK_STATUS_PARITY_ERROR, 114 | IOLINK_STATUS_FRAMING_ERROR, 115 | IOLINK_STATUS_OVERRUN, 116 | IOLINK_STATUS_NO_COMM, 117 | IOLINK_STATUS_STATE_CONFLICT, 118 | IOLINK_STATUS_VALID, 119 | IOLINK_STATUS_INVALID, 120 | IOLINK_STATUS_PARAMETER_CONFLICT, 121 | IOLINK_STATUS_ISDU_TIMEOUT, 122 | IOLINK_STATUS_ISDU_NOT_SUPPORTED, 123 | IOLINK_STATUS_VALUE_OUT_OF_RANGE, 124 | IOLINK_STATUS_ISDU_CHECKSUM_ERROR, 125 | IOLINK_STATUS_ISDU_ILLEGAL_SERVICE_PRIMITIVE, 126 | IOLINK_STATUS_ISDU_ABORT, 127 | } iolink_status_t; 128 | 129 | typedef enum 130 | { 131 | IOLINK_RWDIRECTION_READ = 0x80, 132 | IOLINK_RWDIRECTION_WRITE = 0x00, 133 | } iolink_rwdirection_t; 134 | 135 | typedef enum 136 | { 137 | IOLINK_COMCHANNEL_PROCESS = 0x00, 138 | IOLINK_COMCHANNEL_PAGE = 0x20, 139 | IOLINK_COMCHANNEL_DIAGNOSIS = 0x40, 140 | IOLINK_COMCHANNEL_ISDU = 0x60, 141 | } iolink_comchannel_t; 142 | 143 | typedef enum 144 | { 145 | IOLINK_MHINFO_NONE, 146 | IOLINK_MHINFO_COMLOST, 147 | IOLINK_MHINFO_ILLEGAL_MESSAGETYPE, 148 | IOLINK_MHINFO_CHECKSUM_MISMATCH, 149 | } iolink_mhinfo_t; 150 | 151 | typedef enum 152 | { 153 | IOLINK_MSEQTYPE_TYPE_NONE = 0x00, 154 | IOLINK_MSEQTYPE_TYPE_0 = 0x01, 155 | IOLINK_MSEQTYPE_TYPE_1_1 = 0x11, 156 | IOLINK_MSEQTYPE_TYPE_1_2 = 0x12, 157 | IOLINK_MSEQTYPE_TYPE_1_V = 0x1E, 158 | IOLINK_MSEQTYPE_TYPE_1_X = 0x1F, 159 | IOLINK_MSEQTYPE_TYPE_2_1 = 0x21, 160 | IOLINK_MSEQTYPE_TYPE_2_2 = 0x22, 161 | IOLINK_MSEQTYPE_TYPE_2_3 = 0x23, 162 | IOLINK_MSEQTYPE_TYPE_2_4 = 0x24, 163 | IOLINK_MSEQTYPE_TYPE_2_5 = 0x25, 164 | IOLINK_MSEQTYPE_TYPE_2_6 = 0x26, /**< removed from spec in 1.1.3, but some legacy devices use it */ 165 | IOLINK_MSEQTYPE_TYPE_2_V = 0x2E, 166 | IOLINK_MSEQTYPE_TYPE_2_X = 0x2F, 167 | } iolink_msequencetype_t; 168 | 169 | typedef enum 170 | { 171 | IOLINK_CONTROLCODE_NONE, 172 | IOLINK_CONTROLCODE_VALID, 173 | IOLINK_CONTROLCODE_INVALID, 174 | IOLINK_CONTROLCODE_PDOUTVALID, 175 | IOLINK_CONTROLCODE_PDOUTINVALID, 176 | IOLINK_CONTROLCODE_DEVICEMODE, 177 | } iolink_controlcode_t; 178 | 179 | typedef enum 180 | { 181 | IOLINK_TRANSPORTSTATUS_YES, 182 | IOLINK_TRANSPORTSTATUS_NO, 183 | } iolink_transportstatus_t; 184 | 185 | typedef enum 186 | { 187 | IOLINK_EVENT_MODE_RESERVED = 0, 188 | IOLINK_EVENT_MODE_SINGLE_SHOT = 1, 189 | IOLINK_EVENT_MODE_DISAPPEARS = 2, 190 | IOLINK_EVENT_MODE_APPEARS = 3, 191 | } iolink_event_mode_t; 192 | 193 | typedef enum 194 | { 195 | IOLINK_EVENT_TYPE_RESERVED = 0, 196 | IOLINK_EVENT_TYPE_NOTIFICATION = 1, 197 | IOLINK_EVENT_TYPE_WARNING = 2, 198 | IOLINK_EVENT_TYPE_ERROR = 3, 199 | } iolink_event_type_t; 200 | 201 | typedef enum 202 | { 203 | IOLINK_EVENT_INSTANCE_UNKNOWN = 0, 204 | IOLINK_EVENT_INSTANCE_RESERVED_1 = 1, 205 | IOLINK_EVENT_INSTANCE_RESERVED_2 = 2, 206 | IOLINK_EVENT_INSTANCE_RESERVED_3 = 3, 207 | IOLINK_EVENT_INSTANCE_APPLICATION = 4, 208 | IOLINK_EVENT_INSTANCE_RESERVED_5 = 5, 209 | IOLINK_EVENT_INSTANCE_RESERVED_6 = 6, 210 | IOLINK_EVENT_INSTANCE_RESERVED_7 = 7, 211 | } iolink_event_instance_t; 212 | 213 | typedef enum 214 | { 215 | IOLINK_EVENT_SOURCE_DEVICE = 0, 216 | IOLINK_EVENT_SOURCE_MASTER = 1, 217 | } iolink_event_source_t; 218 | 219 | typedef enum 220 | { 221 | IOLINK_FLOWCTRL_START = 0x10, 222 | IOLINK_FLOWCTRL_IDLE_1 = 0x11, 223 | IOLINK_FLOWCTRL_IDLE_2 = 0x12, 224 | IOLINK_FLOWCTRL_ABORT = 0x1F, 225 | } iolink_isdu_flowctrl_t; 226 | 227 | typedef struct 228 | { 229 | uint8_t time; 230 | iolink_msequencetype_t type; 231 | uint8_t pdinputlength; 232 | uint8_t pdoutputlength; 233 | uint8_t onreqdatalengthpermessage; 234 | } iolink_mode_vl_t; 235 | 236 | typedef struct 237 | { 238 | uint16_t index; 239 | uint8_t subindex; 240 | union 241 | { 242 | uint8_t * data_read; 243 | const uint8_t * data_write; 244 | }; 245 | uint8_t length; 246 | iolink_rwdirection_t readwrite; 247 | } iolink_isdu_vl_t; 248 | 249 | typedef struct 250 | { 251 | uint8_t portnumber; 252 | uint8_t cycletime; 253 | iolink_sm_target_mode_t mode; 254 | uint8_t revisionid; 255 | iolink_inspectionlevel_t inspectionlevel; 256 | uint16_t vendorid; 257 | uint32_t deviceid; 258 | uint16_t functionid; 259 | uint8_t serialnumber[16]; 260 | } iolink_smp_parameterlist_t; 261 | 262 | typedef enum 263 | { 264 | IOLINK_DS_FAULT_NONE = 0, 265 | IOLINK_DS_FAULT_ID, 266 | IOLINK_DS_FAULT_SIZE, 267 | IOLINK_DS_FAULT_UP, 268 | IOLINK_DS_FAULT_DOWN, 269 | IOLINK_DS_FAULT_COM_ERR, 270 | IOLINK_DS_FAULT_LOCK, 271 | } iolink_ds_fault_t; 272 | 273 | typedef struct iolink_dev_com 274 | { 275 | uint8_t mincycle; 276 | uint8_t mseq_cap; 277 | uint8_t pdi; 278 | uint8_t pdo; 279 | } iolink_dev_com_t; 280 | 281 | typedef enum 282 | { 283 | IOLINK_MASTER_MB_TRIG_AL_READ_REQ, 284 | IOLINK_MASTER_MB_TRIG_AL_WRITE_REQ, 285 | IOLINK_MASTER_MB_TRIG_AL_ABORT_REQ, 286 | IOLINK_MASTER_MB_TRIG_AL_CONTROL_REQ, 287 | IOLINK_MASTER_MB_TRIG_AL_EVENT_REQ, 288 | IOLINK_MASTER_MB_TRIG_AL_GETINPUT_REQ, 289 | IOLINK_MASTER_MB_TRIG_AL_SETOUTPUT_REQ, 290 | IOLINK_MASTER_MB_TRIG_DL_CONTROL_IND, 291 | IOLINK_MASTER_MB_TRIG_DL_EVENT_IND, 292 | IOLINK_MASTER_MB_TRIG_DL_PDINPUTTRANSPORT_IND, 293 | IOLINK_MASTER_MB_TRIG_DL_PDCYCLE, 294 | IOLINK_MASTER_MB_TRIG_DL_READPARAM_CNF, 295 | IOLINK_MASTER_MB_TRIG_DL_WRITEPARAM_CNF, 296 | IOLINK_MASTER_MB_TRIG_DL_READ_CNF, 297 | IOLINK_MASTER_MB_TRIG_DL_WRITE_CNF, 298 | IOLINK_MASTER_MB_TRIG_DL_ISDUTRAANSPORT_CNF, 299 | IOLINK_MASTER_MB_TRIG_DL_PDOUTPUTUPDATE_CNF, 300 | IOLINK_MASTER_MB_TRIG_DL_SETMODE_CNF, 301 | IOLINK_MASTER_MB_TRIG_DL_MODE_IND, 302 | IOLINK_MASTER_MB_TRIG_AL_READ_CNF, 303 | IOLINK_MASTER_MB_TRIG_SM_SETPORTCONFIG_REQ, 304 | IOLINK_MASTER_MB_TRIG_SM_GETPORTCONFIG_REQ, 305 | IOLINK_MASTER_MB_TRIG_SM_OPERATE_REQ, 306 | 307 | } iolink_master_mbox_trigger_t; 308 | 309 | typedef union 310 | { 311 | uint32_t data; 312 | struct 313 | { 314 | uint16_t extdata; 315 | uint8_t port; 316 | iolink_master_mbox_trigger_t trigger; 317 | }; 318 | } iolink_master_mbox_payload_t; 319 | #endif /* IOLINK_TYPES_H */ 320 | -------------------------------------------------------------------------------- /iol_osal/ftdi-spi/osal_irq.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "osal.h" 4 | #include "osal_log.h" 5 | /* Needed for hardware driver (iolink_14819_drv_t) */ 6 | #include "iolink_max14819_pl.h" 7 | #include "ftd2xx.h" 8 | #include "osal_spi_internal.h" 9 | 10 | #include "osal_irq.h" 11 | 12 | #define IRQ_BIT (1 << 4) 13 | #define IRQ_THREAD_SLEEP_TIME 500 14 | #define RX_BUFFER_SIZE 1 15 | #define TX_BUFFER_SIZE 2 16 | #define THREAD_STACK_SIZE 128 17 | #define THREAD_PRIORITY 6 18 | 19 | /* These defines represent hex values for specific commands and operations 20 | from the D2XX library */ 21 | #define MPSSE_CMD_GET_DATA_BITS_LOWBYTE 0x81 22 | #define MPSSE_CMD_SEND_IMMEDIATE 0x87 23 | 24 | typedef struct 25 | { 26 | void * fthandle; 27 | isr_func_t isr_func; 28 | void * irq_arg; 29 | } usb_irq_thread_t; 30 | 31 | /** 32 | * This function returns the state of the IRQ pin by sending a read command 33 | * to the FTDI chip which returns the state of the lowbyte pins (ADBUS0 - 34 | * ADBUS7). The IRQ Pin is connected to ADBUS4. 35 | * 36 | * If the writing to the device fails, the function will issue a LOG_ERROR and 37 | * return false. 38 | * 39 | * @param ftdi_handle In: A handle to the FTDI device. 40 | * @return true if the pin is low, else high 41 | */ 42 | static bool read_irq (void * ftdi_handle) 43 | { 44 | FT_STATUS status = 0; 45 | DWORD n_bytes_transferred = 0; 46 | uint8_t rx_buf[RX_BUFFER_SIZE] = {}; 47 | uint8_t tx_buf[TX_BUFFER_SIZE] = {}; 48 | 49 | /* Send MPSSE command to read status of bits in the lowbyte */ 50 | tx_buf[0] = MPSSE_CMD_GET_DATA_BITS_LOWBYTE; 51 | tx_buf[1] = MPSSE_CMD_SEND_IMMEDIATE; 52 | 53 | status = FT_Write (ftdi_handle, tx_buf, TX_BUFFER_SIZE, &n_bytes_transferred); 54 | if (status != FT_OK) 55 | { 56 | LOG_ERROR (LOG_STATE_ON, "%s: FT_Write failed: %d\n", __func__, status); 57 | return false; 58 | } 59 | 60 | n_bytes_transferred = 0; 61 | /* Read one byte from the FTDI buffer to get the status of the lowbyte */ 62 | status = FT_Read (ftdi_handle, rx_buf, RX_BUFFER_SIZE, &n_bytes_transferred); 63 | if (status != FT_OK) 64 | { 65 | LOG_ERROR (LOG_STATE_ON, "%s: FT_Read failed: %d\n", __func__, status); 66 | return false; 67 | } 68 | 69 | /* IRQ pin is active low*/ 70 | if (rx_buf[0] & IRQ_BIT) 71 | { 72 | return false; 73 | } 74 | else 75 | { 76 | return true; 77 | } 78 | } 79 | 80 | /** 81 | * This function runs in a separate thread and continuously monitors the ADBUS4 82 | * pin on an FT2232H, which is connected to the IRQ pin on an MAX14819. It uses 83 | * a mutex to ensure exclusive access to the FT2232H chip during the IRQ check. 84 | * If an IRQ is detected, it triggers the associated ISR function provided 85 | * during thread initialization. 86 | * 87 | * @param arg In: Pointer to the usb_irq_thread_t structure containing thread 88 | * parameter 89 | * 90 | * @note The function uses a loop to periodically check for IRQ with the time 91 | * interval (IRQ_THREAD_SLEEP_TIME) between checks. 92 | * @note ensure the ftdi_io_mutex is properly initiated before starting this 93 | * thread. 94 | */ 95 | static void irq_thread (void * arg) 96 | { 97 | usb_irq_thread_t * irq = (usb_irq_thread_t *)arg; 98 | bool irq_flag = false; 99 | while (1) 100 | { 101 | os_mutex_lock (ftdi_io_mutex); 102 | irq_flag = read_irq (irq->fthandle); 103 | os_mutex_unlock (ftdi_io_mutex); 104 | 105 | if (irq_flag) 106 | { 107 | irq->isr_func (irq->irq_arg); 108 | } 109 | 110 | os_usleep (IRQ_THREAD_SLEEP_TIME); 111 | } 112 | } 113 | 114 | int _iolink_setup_int (int gpio_pin, isr_func_t isr_func, void * irq_arg) 115 | { 116 | iolink_14819_drv_t * usb_drv = (iolink_14819_drv_t *)irq_arg; 117 | static usb_irq_thread_t irqarg; 118 | 119 | irqarg.isr_func = isr_func; 120 | irqarg.irq_arg = irq_arg; 121 | irqarg.fthandle = usb_drv->fd_spi; 122 | 123 | os_thread_t * isr_service_thread; 124 | isr_service_thread = os_thread_create ( 125 | "isr_service_thread", 126 | THREAD_PRIORITY, 127 | THREAD_STACK_SIZE, 128 | irq_thread, 129 | &irqarg); 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /iol_osal/ftdi-spi/osal_spi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "iolink_options.h" 4 | #include "osal_spi.h" 5 | #include "osal_log.h" 6 | #include "ftd2xx.h" 7 | #include "osal_spi_internal.h" 8 | #include "string.h" 9 | 10 | #define CLK_FREQUENCY 10 * 1000 * 1000 11 | #define BUFF_SIZE 250 12 | #define CLOCK_SETTINGS_BUFF_SIZE 4 13 | #define PIN_SETUP_BUFF_SIZE 5 14 | #define READ_TIMEOUT_MS 100 15 | #define WRITE_TIMEOUT_MS 100 16 | #define LATENCY 2 17 | #define DEFAULT_SLEEP_TIME 2 18 | #define INIT_SLEEP_USEC 200 * 1000 19 | 20 | /* Specific commands and operations to the D2XX library */ 21 | #define MPSSE_CMD_SET_DATA_BITS_LOWBYTE 0x80 22 | #define MPSSE_CMD_DISABLE_3PHASE_CLOCKING 0x8D 23 | #define MPSSE_CMD_DATA_BYTES_IN_POS_OUT_NEG_EDGE 0x31 24 | #define MPSSE_CMD_SEND_IMMEDIATE 0x87 25 | #define ENABLE_MPSSE 0x02 26 | #define MAX_CLOCK_RATE 30000000 27 | #define DISABLE_ADAPTIVE_CLOCKING 0x97 28 | #define SET_CLOCK_FREQUENCY_CMD 0x86 29 | #define DISABLE_CLOCK_DIVIDE 0x8A 30 | #define SET_CS_LOW 0x00 31 | #define SET_CS_HIGH 0x08 32 | #define SET_GPIO_CMD 0x80 33 | #define SET_VALS 0x0A 34 | #define SET_DIRECTION 0x0B 35 | 36 | os_mutex_t * ftdi_io_mutex; 37 | uint8_t ftdi_io_buf[BUFF_SIZE]; 38 | 39 | /** 40 | * Configures the FT2232H chip for MPSSE mode, setting timeouts, latency, flow 41 | * control, and clock frequency. 42 | * 43 | * This function utilizes the ftd2xx API to handle device configuration. In case 44 | * of failure, it logs an error and returns an appropriate status message, which 45 | * should be handled accordingly. 46 | * 47 | * @param ftdi_handle In: A handle to the FTDI device. 48 | * @param clk_freq In: The clock frequency, with a maximum value of 49 | * 30,000,000. 50 | * @return 0 for success; otherwise, check the FT_STATUS enum in ftd2xx.h for 51 | * state values. 52 | */ 53 | static uint32_t cfg_ftdi_prt (void * ftdi_handle, uint32_t clk_freq) 54 | { 55 | uint8_t tx_buf[CLOCK_SETTINGS_BUFF_SIZE] = {}; 56 | uint8_t val_L = 0; 57 | uint8_t val_H = 0; 58 | FT_STATUS status = 0; 59 | DWORD n_bytes_written = 0; 60 | uint32_t val = 0; 61 | 62 | status = FT_ResetDevice (ftdi_handle); 63 | if (status != FT_OK) 64 | { 65 | LOG_ERROR (LOG_STATE_ON, "APP: %s: Failed to reset the device\n", __func__); 66 | return status; 67 | } 68 | 69 | status = FT_SetTimeouts (ftdi_handle, READ_TIMEOUT_MS, WRITE_TIMEOUT_MS); 70 | if (status != FT_OK) 71 | { 72 | LOG_ERROR (LOG_STATE_ON, "APP: %s: Failed to set timeouts\n", __func__); 73 | return status; 74 | } 75 | 76 | status = FT_SetLatencyTimer (ftdi_handle, LATENCY); 77 | if (status != FT_OK) 78 | { 79 | LOG_ERROR (LOG_STATE_ON, "APP: %s: Failed to set latency timer\n", __func__); 80 | return status; 81 | } 82 | 83 | status = FT_SetFlowControl (ftdi_handle, FT_FLOW_NONE, 0, 0); 84 | if (status != FT_OK) 85 | { 86 | LOG_ERROR (LOG_STATE_ON, "APP: %s: Failed to set flow control\n", __func__); 87 | return status; 88 | } 89 | 90 | status = FT_SetBitMode (ftdi_handle, 0x00, 0x00); 91 | if (status != FT_OK) 92 | { 93 | LOG_ERROR (LOG_STATE_ON, "APP: %s: Failed to reset bit mode\n", __func__); 94 | return status; 95 | } 96 | 97 | status = FT_SetBitMode (ftdi_handle, 0x00, ENABLE_MPSSE); 98 | if (status != FT_OK) 99 | { 100 | LOG_ERROR (LOG_STATE_ON, "APP: %s: Failed to set bit mode\n", __func__); 101 | return status; 102 | } 103 | 104 | val = (MAX_CLOCK_RATE / clk_freq) - 1; 105 | val_L = (uint8_t)val; 106 | val_H = (uint8_t)(val >> 8); 107 | tx_buf[0] = DISABLE_CLOCK_DIVIDE; 108 | tx_buf[1] = SET_CLOCK_FREQUENCY_CMD; 109 | tx_buf[2] = val_L; 110 | tx_buf[3] = val_H; 111 | /*Send command to set clock frequency*/ 112 | status = 113 | FT_Write (ftdi_handle, tx_buf, CLOCK_SETTINGS_BUFF_SIZE, &n_bytes_written); 114 | if (status != FT_OK) 115 | { 116 | LOG_ERROR (LOG_STATE_ON, "APP: %s: Failed to set clock frequency\n", __func__); 117 | return status; 118 | } 119 | 120 | return status; 121 | } 122 | 123 | /** 124 | * This function sets up the pins of the FT2232H chip 125 | * to act as a SPI Master and a pin for the IRQ. 126 | * 127 | * The pins of the FT2232H chip is setup as followed: 128 | * - ADBUS0 (SCLK) - Output, Clock Idle Low 129 | * - ADBUS1 (MOSI) - Output, Default Low 130 | * - ADBUS2 (MISO) - Input 131 | * - ADBUS3 (CS) - Output, Default High 132 | * - ADBUS4 (IRQ) - Input 133 | * 134 | * The 3 phase clocking and adaptive clocking is also disabled. 135 | * 136 | * @param ftdi_handle In: A handle to the FTDI device. 137 | * @return 0 for success; otherwise, check the FT_STATUS enum in 138 | * ftd2xx.h for state values. 139 | */ 140 | static uint32_t mpsse_setup (void * ftdi_handle) 141 | { 142 | FT_STATUS status = 0; 143 | uint8_t tx_buf[PIN_SETUP_BUFF_SIZE] = {}; 144 | DWORD n_bytes_to_transfer = 0; 145 | DWORD n_bytes_written = 0; 146 | tx_buf[0] = MPSSE_CMD_DISABLE_3PHASE_CLOCKING; 147 | tx_buf[1] = DISABLE_ADAPTIVE_CLOCKING; 148 | tx_buf[2] = SET_GPIO_CMD; 149 | tx_buf[3] = SET_VALS; 150 | tx_buf[4] = SET_DIRECTION; 151 | /*Send command and options to set up FTDI to handle SPI and ABUS4 (IRQ-pin) 152 | * to GPIO*/ 153 | status = FT_Write (ftdi_handle, tx_buf, PIN_SETUP_BUFF_SIZE, &n_bytes_written); 154 | if (status != FT_OK) 155 | { 156 | LOG_ERROR ( 157 | IOLINK_APP_LOG, 158 | "APP: %s: Failed to configure pin directions\n", 159 | __func__); 160 | return status; 161 | } 162 | 163 | os_usleep (DEFAULT_SLEEP_TIME); 164 | return status; 165 | } 166 | 167 | static void list_devices (void) 168 | { 169 | FT_STATUS status; 170 | FT_DEVICE_LIST_INFO_NODE * info = NULL; 171 | DWORD n_devs = 0; 172 | int i; 173 | 174 | /* Discover how many FTDI devices are connected */ 175 | status = FT_CreateDeviceInfoList (&n_devs); 176 | if (status != FT_OK) 177 | { 178 | LOG_ERROR ( 179 | IOLINK_APP_LOG, 180 | "APP: %s: FT_CreateDeviceInfoList failed (error code %d)\n", 181 | __func__, 182 | (int)status); 183 | goto exit; 184 | } 185 | 186 | if (n_devs == 0) 187 | { 188 | LOG_ERROR (IOLINK_APP_LOG, "APP: %s: no devices connected\n", __func__); 189 | goto exit; 190 | } 191 | 192 | /* Allocate storage */ 193 | info = calloc ((size_t)n_devs, sizeof (FT_DEVICE_LIST_INFO_NODE)); 194 | if (info == NULL) 195 | { 196 | LOG_ERROR (IOLINK_APP_LOG, "APP: %s: Allocation failure.\n", __func__); 197 | goto exit; 198 | } 199 | 200 | /* Populate the list of info nodes */ 201 | status = FT_GetDeviceInfoList (info, &n_devs); 202 | if (status != FT_OK) 203 | { 204 | LOG_ERROR ( 205 | IOLINK_APP_LOG, 206 | "APP: %s: FT_GetDeviceInfoList failed (error code %d)\n", 207 | __func__, 208 | (int)status); 209 | 210 | goto exit; 211 | } 212 | 213 | /* Display info (including EEPROM fields) for each connected FTDI device */ 214 | for (i = 0; i < (int)n_devs; i++) 215 | { 216 | LOG_INFO (IOLINK_APP_LOG, "Device %d:\n", i); 217 | LOG_INFO (IOLINK_APP_LOG, " Flags = 0x%x\n", info[i].Flags); 218 | LOG_INFO (IOLINK_APP_LOG, " Type = 0x%x\n", info[i].Type); 219 | LOG_INFO (IOLINK_APP_LOG, " ID = 0x%04x\n", info[i].ID); 220 | LOG_INFO (IOLINK_APP_LOG, " LocId = 0x%x\n", info[i].LocId); 221 | LOG_INFO (IOLINK_APP_LOG, " SerialNumber = %s\n", info[i].SerialNumber); 222 | LOG_INFO (IOLINK_APP_LOG, " Description = %s\n", info[i].Description); 223 | LOG_INFO (IOLINK_APP_LOG, " ftHandle = %p\n", info[i].ftHandle); 224 | } 225 | 226 | exit: 227 | free (info); 228 | } 229 | 230 | void * _iolink_pl_hw_spi_init (const char * spi_slave_name) 231 | { 232 | ftdi_io_mutex = os_mutex_create(); 233 | void * ftdi_handle; 234 | FT_STATUS status = 0; 235 | 236 | list_devices(); 237 | 238 | /* TODO: This function supports opening multiple devices by setting 239 | deviceNumber to 0, 1, etc. However, it does not provide the capability to 240 | open a specific device by name. For opening named devices, consider using 241 | FT_OpenEx instead. 242 | */ 243 | status = FT_Open (atoi (spi_slave_name), &ftdi_handle); 244 | if (status != FT_OK) 245 | { 246 | LOG_ERROR (IOLINK_APP_LOG, "APP: %s: Failed to open device\n", __func__); 247 | os_mutex_destroy (ftdi_io_mutex); 248 | return NULL; 249 | } 250 | 251 | status = cfg_ftdi_prt (ftdi_handle, CLK_FREQUENCY); 252 | if (status != FT_OK) 253 | { 254 | LOG_ERROR (IOLINK_APP_LOG, "APP: %s: Failed to config port\n", __func__); 255 | os_mutex_destroy (ftdi_io_mutex); 256 | return NULL; 257 | } 258 | 259 | status = mpsse_setup (ftdi_handle); 260 | if (status != FT_OK) 261 | { 262 | LOG_ERROR (IOLINK_APP_LOG, "APP: %s: Failed to to setup MPSSE\n", __func__); 263 | os_mutex_destroy (ftdi_io_mutex); 264 | return NULL; 265 | } 266 | os_usleep(INIT_SLEEP_USEC); 267 | 268 | return ftdi_handle; 269 | } 270 | 271 | void _iolink_pl_hw_spi_close (void * ftdi_handle) 272 | { 273 | if (FT_Close (ftdi_handle) != FT_OK) 274 | { 275 | LOG_ERROR (LOG_STATE_ON, "APP: %s: Failed to close handle\n", __func__); 276 | return; 277 | } 278 | 279 | os_mutex_destroy (ftdi_io_mutex); 280 | } 281 | 282 | void _iolink_pl_hw_spi_transfer ( 283 | void * ftdi_handle, 284 | void * data_read, 285 | const void * data_written, 286 | size_t n_bytes_to_transfer) 287 | { 288 | FT_STATUS status = 0; 289 | DWORD n_bytes_written = 0; 290 | DWORD n_bytes_read = 0; 291 | uint8_t * cmd_buf = ftdi_io_buf; 292 | 293 | os_mutex_lock (ftdi_io_mutex); 294 | 295 | int idx = 0; 296 | 297 | /* chip select */ 298 | cmd_buf[idx++] = MPSSE_CMD_SET_DATA_BITS_LOWBYTE; 299 | cmd_buf[idx++] = SET_CS_LOW; 300 | cmd_buf[idx++] = SET_DIRECTION; 301 | 302 | /* payload */ 303 | cmd_buf[idx++] = MPSSE_CMD_DATA_BYTES_IN_POS_OUT_NEG_EDGE; 304 | cmd_buf[idx++] = (uint8_t)((n_bytes_to_transfer - 1) & 0x000000FF); 305 | cmd_buf[idx++] = (uint8_t)(((n_bytes_to_transfer - 1) & 0x0000FF00) >> 8); 306 | 307 | memcpy (&cmd_buf[idx], data_written, n_bytes_to_transfer); 308 | idx += n_bytes_to_transfer; 309 | 310 | /* chip select */ 311 | cmd_buf[idx++] = MPSSE_CMD_SET_DATA_BITS_LOWBYTE; 312 | cmd_buf[idx++] = SET_CS_HIGH; 313 | cmd_buf[idx++] = SET_DIRECTION; 314 | 315 | cmd_buf[idx++] = MPSSE_CMD_SEND_IMMEDIATE; 316 | 317 | /*Send the command buffer to prepare for read and write operation*/ 318 | status = FT_Write (ftdi_handle, cmd_buf, idx, &n_bytes_written); 319 | if (status != FT_OK) 320 | { 321 | LOG_ERROR (LOG_STATE_ON, "%s: failed to send SPI message\n", ""); 322 | goto unlock; 323 | } 324 | 325 | if (idx != n_bytes_written) 326 | { 327 | LOG_ERROR ( 328 | LOG_STATE_ON, 329 | "%s: Not the same amount written as expected (%u, %u)\n", 330 | __func__, 331 | (DWORD)n_bytes_to_transfer, n_bytes_read); 332 | goto unlock; 333 | } 334 | 335 | /*Read data*/ 336 | status = FT_Read ( 337 | ftdi_handle, 338 | data_read, 339 | n_bytes_to_transfer, 340 | &n_bytes_read); 341 | 342 | if (status != FT_OK) 343 | { 344 | LOG_ERROR (LOG_STATE_ON, "%s: failed to send SPI message\n", __func__); 345 | goto unlock; 346 | } 347 | 348 | if (n_bytes_to_transfer != n_bytes_read) 349 | { 350 | LOG_ERROR ( 351 | LOG_STATE_ON, 352 | "%s: Not the same amount read as expected (%u, %u)\n", 353 | __func__, 354 | (DWORD)n_bytes_to_transfer, n_bytes_read); 355 | goto unlock; 356 | } 357 | 358 | unlock: 359 | os_mutex_unlock (ftdi_io_mutex); 360 | return; 361 | } 362 | -------------------------------------------------------------------------------- /iol_osal/ftdi-spi/osal_spi_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef OSAL_SPI_USB_HELPERS_H 2 | #define OSAL_SPI_USB_HELPERS_H 3 | 4 | #include "osal.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /* A Mutex lock used when accessing the ftdi_handle during communication with 11 | * the FTDI chip.*/ 12 | extern os_mutex_t * ftdi_io_mutex; 13 | 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /iol_osal/ftdi-spi/unload_built_in_modules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # script for unloading ftdi_sio and usbserial drivers 3 | sudo rmmod ftdi_sio 4 | sudo rmmod usbserial 5 | -------------------------------------------------------------------------------- /iol_osal/include/osal_irq.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2020 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifndef OSAL_PL_HW_IRQ_H 17 | #define OSAL_PL_HW_IRQ_H 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | typedef void (*isr_func_t) (void *); 24 | 25 | /** 26 | * A function that creates an interrupt handler thread, which will call the 27 | * isr_func when a falling edge is detected on the GPIO pin. 28 | * 29 | * @note In USB mode, the GPIO pin value does not matter, instead the interrupt 30 | * handler is setup to poll a pre defined pin on the FT2232H chip. 31 | * @note In USB mode, state of the GPIO pin is polled and isr_func is called 32 | * when GPIO pin is detected as low. 33 | * 34 | * @param gpio_pin In: The GPIO pin that is connected to the IRQ pin 35 | * @param isr_func In: The function that will be called when the interrupt gets 36 | * triggered 37 | * @param irq_arg In: Pointer to the iolink_hw_drv_t structure containing 38 | * thread parameter 39 | */ 40 | int _iolink_setup_int (int gpio_pin, isr_func_t isr_func, void * irq_arg); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /iol_osal/include/osal_spi.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2020 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifndef OSAL_PL_HW_SPI_H 17 | #define OSAL_PL_HW_SPI_H 18 | 19 | #include /* size_t */ 20 | #include /* uint32_t */ 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | /** 27 | * Function that opens an SPI channel, initiates it for SPI communication, 28 | * and returns a file descriptor/handle. 29 | * 30 | * If the opening of an SPI channel fails, NULL will be returned. 31 | * 32 | * @note This function's behavior may vary depending on the underlying operating 33 | * system. 34 | * @note When compiling with rt-kernel or Linux without USB, 35 | * the spi_slave_name should be a null-terminated string with the file path to 36 | * the SPI device wanted to be opened. Example: spi_slave_name = 37 | * "/dev/spidev0.0" 38 | * @note In USB mode, a mutex (ftdi_io_mutex) is created and used when 39 | * reading/writing to the FTDI chip. If the opening of a device fails, this 40 | * mutex will be destroyed before returning NULL. 41 | * @note In USB mode, only one channel is supported, and it is opened with 42 | * spi_slave_name = "0" as a null-terminated string. 43 | * 44 | * @param spi_slave_name In: A null-terminated string with the SPI slave 45 | * identifier. 46 | * @return File descriptor, or NULL on failure. 47 | */ 48 | void * _iolink_pl_hw_spi_init (const char * spi_slave_name); 49 | 50 | /** 51 | * Function used for closing the SPI channel. 52 | * 53 | * This function releases the resources associated with the specified file 54 | * descriptor/handle. 55 | * 56 | * @note The behavior of this function may vary depending on the underlying 57 | * operating system. 58 | * @note In USB mode, the function destroys the mutex (ftdi_io_mutex) used for 59 | * exclusive locking during SPI communication. 60 | * 61 | * @param fd In: File descriptor/handle for the SPI channel 62 | */ 63 | void _iolink_pl_hw_spi_close (void * fd); 64 | 65 | /** 66 | * Reads and writes data from/to an SPI slave device. 67 | * 68 | * This function transfer data in both directions between an SPI 69 | * master and a slave. During each clock cycle, one bit is clocked out from the 70 | * master, and one is clocked in to the master. If the SPI transmission fails, a 71 | * LOG_ERROR will be issued. 72 | * 73 | * @note The behavior of this function may vary depending on the underlying 74 | * operating system. 75 | * @note In USB mode, the ftd2xx API will be used to communicate with the device 76 | * @note In USB mode, a mutex (ftdi_io_mutex) is locked during communication 77 | * with the FTDI chip to ensure exclusive access. 78 | * 79 | * @param fd In: File descriptor/handle for the SPI channel 80 | * @param data_read Out: Pointer to the buffer that receives the data 81 | * @param data_written In: Pointer to the buffer that contains the data 82 | * to be written 83 | * @param n_bytes_to_transfer In: The number of bytes to be transferred 84 | */ 85 | void _iolink_pl_hw_spi_transfer ( 86 | void * fd, 87 | void * data_read, 88 | const void * data_written, 89 | size_t n_bytes_to_transfer); 90 | 91 | #ifdef __cplusplus 92 | } 93 | #endif 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /iol_osal/linux-spidev/osal_irq.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "osal_irq.h" 9 | 10 | #define SYSFS_GPIO_DIR "/sys/class/gpio" 11 | #define MAX_BUF 64 12 | 13 | typedef struct 14 | { 15 | void* irq_arg; 16 | int irq_fd; 17 | isr_func_t isr_func; 18 | } irq_thread_t; 19 | 20 | static void * irq_thread (void * arg) 21 | { 22 | irq_thread_t * irq = (irq_thread_t *)arg; 23 | struct pollfd p_fd; 24 | char buf[MAX_BUF]; 25 | 26 | while (1) 27 | { 28 | memset (&p_fd, 0, sizeof (p_fd)); 29 | 30 | p_fd.fd = irq->irq_fd; 31 | p_fd.events = POLLPRI; 32 | 33 | int rc = poll (&p_fd, 1, 5000); 34 | 35 | if (rc < 0) 36 | { 37 | printf ("\npoll() failed!\n"); 38 | } 39 | else if (rc == 0) 40 | { 41 | printf ("."); 42 | } 43 | else if (p_fd.revents & POLLPRI) 44 | { 45 | lseek (p_fd.fd, 0, SEEK_SET); 46 | if (read (p_fd.fd, buf, MAX_BUF) > 0) 47 | { 48 | irq->isr_func (irq->irq_arg); 49 | } 50 | } 51 | 52 | fflush (stdout); 53 | } 54 | close (irq->irq_fd); 55 | } 56 | 57 | static pthread_t thread; 58 | 59 | int _iolink_setup_int (int gpio_pin, isr_func_t isr_func, void* irq_arg) 60 | { 61 | char buf[MAX_BUF]; 62 | pthread_attr_t attr; 63 | static irq_thread_t irqarg; 64 | 65 | // Add gpio_pin to exported pins 66 | int fd = open (SYSFS_GPIO_DIR "/export", O_WRONLY); 67 | 68 | if (fd < 0) 69 | { 70 | perror ("gpio/export"); 71 | return -1; 72 | } 73 | 74 | int n = write (fd, buf, snprintf (buf, sizeof (buf), "%d", gpio_pin)); 75 | close (fd); 76 | 77 | if (n < 0) 78 | { 79 | perror ("setup_int(): Failed to write gpio_pin to gpio/export"); 80 | } 81 | 82 | // Set direction of pin to input 83 | snprintf (buf, sizeof (buf), SYSFS_GPIO_DIR "/gpio%d/direction", gpio_pin); 84 | fd = open (buf, O_WRONLY); 85 | 86 | if (fd < 0) 87 | { 88 | perror ("gpio/direction"); 89 | return -1; 90 | } 91 | 92 | n = write (fd, "in", sizeof ("in")); 93 | close (fd); 94 | 95 | if (n < 0) 96 | { 97 | perror ("write direction"); 98 | return -1; 99 | } 100 | 101 | // Set edge detection to falling 102 | snprintf (buf, sizeof (buf), SYSFS_GPIO_DIR "/gpio%d/edge", gpio_pin); 103 | fd = open (buf, O_WRONLY); 104 | 105 | if (fd < 0) 106 | { 107 | perror ("gpio/edge"); 108 | return -1; 109 | } 110 | 111 | n = write (fd, "falling", sizeof ("falling")); 112 | close (fd); 113 | 114 | if (n < 0) 115 | { 116 | perror ("write edge"); 117 | return -1; 118 | } 119 | 120 | // Open file and return file descriptor 121 | snprintf (buf, sizeof (buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio_pin); 122 | fd = open (buf, O_RDONLY | O_NONBLOCK); 123 | 124 | if (fd < 0) 125 | { 126 | perror ("gpio/value"); 127 | return -1; 128 | } 129 | 130 | // Create isr service thread 131 | irqarg.isr_func = isr_func; 132 | irqarg.irq_arg = irq_arg; 133 | irqarg.irq_fd = fd; 134 | pthread_attr_init (&attr); 135 | pthread_create (&thread, &attr, irq_thread, &irqarg); 136 | 137 | return 0; 138 | } 139 | -------------------------------------------------------------------------------- /iol_osal/linux-spidev/osal_spi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "osal_spi.h" 4 | #include "osal_log.h" 5 | #include "iolink_options.h" 6 | #include "fcntl.h" 7 | #include "unistd.h" 8 | 9 | void * _iolink_pl_hw_spi_init (const char * spi_slave_name) 10 | { 11 | int fd = -1; 12 | fd = open (spi_slave_name, O_RDWR); 13 | if (fd == -1) 14 | { 15 | return NULL; 16 | } 17 | return (void *)(intptr_t)fd; 18 | } 19 | 20 | void _iolink_pl_hw_spi_close (void * fd) 21 | { 22 | close ((int)(intptr_t)fd); 23 | } 24 | 25 | void _iolink_pl_hw_spi_transfer ( 26 | void * fd, 27 | void * data_read, 28 | const void * data_written, 29 | size_t n_bytes_to_transfer) 30 | { 31 | int spi_fd = (int)(intptr_t)fd; 32 | // TODO 33 | int delay = 100; 34 | int speed = 4 * 1000 * 1000; 35 | int bits = 8; 36 | 37 | struct spi_ioc_transfer tr = { 38 | .tx_buf = (unsigned long)data_written, 39 | .rx_buf = (unsigned long)data_read, 40 | .len = n_bytes_to_transfer, 41 | .delay_usecs = delay, 42 | .speed_hz = speed, 43 | .bits_per_word = bits, 44 | }; 45 | 46 | if (ioctl (spi_fd, SPI_IOC_MESSAGE (1), &tr) < 1) 47 | { 48 | LOG_ERROR (IOLINK_PL_LOG, "%s: failed to send SPI message\n", __func__); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /iol_osal/rt-kernel/osal_irq.c: -------------------------------------------------------------------------------- 1 | #include "osal.h" 2 | #include "osal_irq.h" 3 | #include "kern/int.h" 4 | 5 | int _iolink_setup_int (int irq_pin, isr_func_t isr_func, void* irq_arg) 6 | { 7 | int_connect(irq_pin, isr_func, irq_arg); 8 | int_enable(irq_pin); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /iol_osal/rt-kernel/osal_spi.c: -------------------------------------------------------------------------------- 1 | #include "osal_spi.h" 2 | 3 | #include 4 | 5 | void * _iolink_pl_hw_spi_init (const char * spi_slave_name) 6 | { 7 | int fd = -1; 8 | fd = open(spi_slave_name, O_RDWR); 9 | if (fd == -1) 10 | { 11 | return NULL; 12 | } 13 | return (void*)fd; 14 | } 15 | 16 | void _iolink_pl_hw_spi_close (void * fd) 17 | { 18 | close ((int)fd); 19 | } 20 | 21 | void _iolink_pl_hw_spi_transfer ( 22 | void * fd, 23 | void * data_read, 24 | const void * data_written, 25 | size_t n_bytes_to_transfer) 26 | { 27 | spi_select ((int)fd); 28 | spi_bidirectionally_transfer ((int)fd, data_read, data_written, n_bytes_to_transfer); 29 | spi_unselect ((int)fd); 30 | } 31 | -------------------------------------------------------------------------------- /iolink_options.h.in: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2021 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifndef OPTIONS_H 17 | #define OPTIONS_H 18 | 19 | /* #undef WITH_MALLOC */ 20 | 21 | #cmakedefine LOG_ENABLE 22 | #cmakedefine WITH_MALLOC 23 | 24 | /* 25 | * Supported IO-Link HW 26 | */ 27 | #define IOLINK_HW_NONE 0 28 | #define IOLINK_HW_MAX14819 1 29 | 30 | #ifndef LOG_LEVEL 31 | #define LOG_LEVEL (LOG_LEVEL_@LOG_LEVEL@) 32 | #endif 33 | 34 | #ifndef IOLINK_PL_LOG 35 | #define IOLINK_PL_LOG (LOG_STATE_ON) 36 | #endif 37 | 38 | #ifndef IOLINK_DL_LOG 39 | #define IOLINK_DL_LOG (LOG_STATE_ON) 40 | #endif 41 | 42 | #ifndef IOLINK_AL_LOG 43 | #define IOLINK_AL_LOG (LOG_STATE_ON) 44 | #endif 45 | 46 | #ifndef IOLINK_SM_LOG 47 | #define IOLINK_SM_LOG (LOG_STATE_ON) 48 | #endif 49 | 50 | #ifndef IOLINK_CM_LOG 51 | #define IOLINK_CM_LOG (LOG_STATE_ON) 52 | #endif 53 | 54 | #ifndef IOLINK_DS_LOG 55 | #define IOLINK_DS_LOG (LOG_STATE_ON) 56 | #endif 57 | 58 | #ifndef IOLINK_ODE_LOG 59 | #define IOLINK_ODE_LOG (LOG_STATE_ON) 60 | #endif 61 | 62 | #ifndef IOLINK_PDE_LOG 63 | #define IOLINK_PDE_LOG (LOG_STATE_ON) 64 | #endif 65 | 66 | #ifndef IOLINK_APP_LOG 67 | #define IOLINK_APP_LOG (LOG_STATE_ON) 68 | #endif 69 | 70 | #ifndef IOLINK_NUM_PORTS 71 | #define IOLINK_NUM_PORTS (@IOLINK_NUM_PORTS@) 72 | #endif 73 | 74 | #ifndef IOLINK_NUM_DIAG_ENTRIES 75 | #define IOLINK_NUM_DIAG_ENTRIES (@IOLINK_NUM_DIAG_ENTRIES@) 76 | #endif 77 | 78 | #ifndef IOLINK_MAX_EVENTS 79 | #define IOLINK_MAX_EVENTS (@IOLINK_MAX_EVENTS@) 80 | #endif 81 | 82 | /* 83 | * IO-Link HW 84 | */ 85 | #ifndef IOLINK_HW 86 | #define IOLINK_HW (IOLINK_HW_@IOLINK_HW@) 87 | #endif 88 | 89 | #if IOLINK_HW == IOLINK_HW_MAX14819 90 | #define IOLINK_MAX14819_IRQ_GPIO 91 | //#define IOLINK_MAX14819_IRQ_SPI 92 | 93 | //#define IOLINK_MAX14819_RXRDY_GPIO 94 | #define IOLINK_MAX14819_RXRDY_SPI 95 | 96 | //#define IOLINK_MAX14819_RXERR_GPIO 97 | #define IOLINK_MAX14819_RXERR_SPI 98 | #endif 99 | 100 | #endif /* OPTIONS_H */ 101 | -------------------------------------------------------------------------------- /samples/ifm_sample_app/README.md: -------------------------------------------------------------------------------- 1 | i-link Sample Application 2 | ------------------------- 3 | 4 | A sample application running a single port in SDCI / IO-Link mode with 5 | SMI (Standardised Master Interface) and HMI (Human Machine Interface) support 6 | for controlling a RFID tag reader as well as a display unit. 7 | 8 | 9 | Supported devices 10 | ----------------- 11 | 12 | The sample application currently supports two different devices from IFM 13 | (with vendor ID = 0x0136): 14 | 15 | * Display IFM E30391 (device ID = 0x02A9): https://www.ifm.com/se/sv/product/E30391 16 | * RFID Reader IFM DTI515 (device ID = 0x03C7): https://www.ifm.com/se/sv/product/DTI515 17 | -------------------------------------------------------------------------------- /samples/ifm_sample_app/app_handler.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2021 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifndef APP_HANDLER_H 17 | #define APP_HANDLER_H 18 | 19 | #include "sys/osal_sys.h" 20 | #include "iolink.h" 21 | 22 | #define MASTER_VENDOR_ID 1171 23 | #define MASTER_ID 123 24 | 25 | typedef enum 26 | { 27 | NONE, 28 | IFM_LAMP, 29 | IFM_RFID, 30 | IFM_SENS, 31 | IFM_HMI, 32 | GOLDEN, 33 | UNKNOWN, 34 | } app_device_type_t; 35 | 36 | /* Port events for app_port.event */ 37 | typedef enum 38 | { 39 | SMI_READ_CNF = 0x01, 40 | SMI_WRITE_CNF = 0x02, 41 | SMI_PORTCFG_CNF = 0x04, 42 | SMI_PORTSTATUS_CNF = 0x08, 43 | SMI_MASTERIDENT_CNF = 0x10, 44 | SMI_PARSERV_TO_DS_CNF = 0x20, 45 | } app_port_event_t; 46 | 47 | typedef enum 48 | { 49 | IOL_STATE_INACTIVE = 0, 50 | IOL_STATE_STARTING, 51 | IOL_STATE_STOPPING, 52 | IOL_STATE_RUNNING, 53 | IOL_STATE_WU_RETRY_WAIT_TSD, 54 | } app_port_state_t; 55 | 56 | typedef struct app_port_status 57 | { 58 | iolink_port_status_info_t port_status_info; 59 | iolink_port_quality_info_t port_quality_info; 60 | uint8_t revision_id; 61 | iolink_transmission_rate_t transmission_rate; 62 | uint8_t master_cycle_time; 63 | uint16_t vendorid; 64 | uint32_t deviceid; 65 | } app_port_status_t; 66 | 67 | #define PORT_EVENT_COUNT 10 68 | 69 | typedef struct app_master_ctx app_master_ctx_t; 70 | typedef struct app_port_ctx app_port_ctx_t; 71 | 72 | typedef struct app_port_ctx 73 | { 74 | app_master_ctx_t * app_master; 75 | uint8_t portnumber; 76 | app_device_type_t type; 77 | os_event_t * event; 78 | uint8_t allocated; 79 | app_port_state_t app_port_state; 80 | app_port_status_t status; 81 | os_mutex_t * status_mtx; 82 | iolink_smi_errortypes_t errortype; 83 | 84 | struct 85 | { 86 | uint8_t count; 87 | diag_entry_t diag_entry[PORT_EVENT_COUNT]; 88 | iolink_eventcode_t eventcode[PORT_EVENT_COUNT]; 89 | } events; 90 | os_mutex_t * event_mtx; 91 | struct 92 | { 93 | iolink_port_qualifier_info_t pqi; 94 | uint8_t data[IOLINK_PD_MAX_SIZE]; 95 | uint8_t data_len; 96 | } pdin; 97 | struct 98 | { 99 | uint16_t data_len; 100 | } param_read; 101 | os_mutex_t * pdout_mtx; 102 | void (*run_function) (app_port_ctx_t * app_port); 103 | #ifdef __rtk__ 104 | bool alarm_active; 105 | #endif 106 | } app_port_ctx_t; 107 | 108 | typedef struct app_master_ctx 109 | { 110 | iolink_m_t * master; 111 | app_port_ctx_t app_port[IOLINK_NUM_PORTS]; 112 | os_event_t * app_event; 113 | uint16_t vendorid; 114 | uint32_t masterid; 115 | } app_master_ctx_t; 116 | 117 | extern app_master_ctx_t app_master; 118 | 119 | void app_handler (iolink_m_cfg_t m_cfg); 120 | 121 | iolink_smi_errortypes_t app_wait_for_cnf ( 122 | app_port_ctx_t * app_port, 123 | uint32_t mask, 124 | uint32_t ms); 125 | 126 | uint8_t app_get_port_status (app_port_ctx_t * app_port); 127 | 128 | #endif // APP_HANDLER_H 129 | -------------------------------------------------------------------------------- /samples/ifm_sample_app/app_ifm.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2021 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #include 17 | #include 18 | #include "osal.h" 19 | #include "osal_log.h" 20 | #include "iolink.h" 21 | #include "app_handler.h" 22 | #include "app_smi.h" 23 | 24 | #define RFID_DATA_SIZE 8 25 | 26 | static bool app_ifm_rfid_get_tag_present (uint8_t * data) 27 | { 28 | return (data[1] & 0x04); 29 | } 30 | 31 | static uint8_t app_ifm_rfid_get_error_code (uint8_t * data) 32 | { 33 | return data[31]; 34 | } 35 | 36 | static bool is_all_zeroes (uint8_t * data, int length) 37 | { 38 | int i; 39 | 40 | for (i = 0; i < length; i++) 41 | { 42 | if (data[i] != 0) 43 | { 44 | return false; 45 | } 46 | } 47 | 48 | return true; 49 | } 50 | 51 | static void app_ifm_rfid_run (app_port_ctx_t * app_port) 52 | { 53 | uint8_t port_index = app_port->portnumber - 1; 54 | uint8_t pdata[IOLINK_PD_MAX_SIZE]; 55 | static bool tag_is_present[IOLINK_NUM_PORTS] = {false}; 56 | 57 | if (app_smi_pdin (app_port, NULL, pdata) != IOLINK_PD_MAX_SIZE) 58 | { 59 | LOG_WARNING ( 60 | LOG_STATE_ON, 61 | "%s: Failed to get PDIn (RFID port #%u)\n", 62 | __func__, 63 | app_port->portnumber); 64 | return; 65 | } 66 | 67 | uint8_t error_code = app_ifm_rfid_get_error_code (pdata); 68 | if (error_code != 0) 69 | { 70 | LOG_ERROR ( 71 | LOG_STATE_ON, 72 | "%s: Got error code %d (RFID port #%u)\n", 73 | __func__, 74 | error_code, 75 | app_port->portnumber); 76 | return; 77 | } 78 | 79 | if (app_ifm_rfid_get_tag_present (pdata)) 80 | { 81 | if (!tag_is_present[port_index]) // tagPresent transition from 0 to 1 82 | { 83 | // check if UID is invalid, i.e. all zeroes (data starts at byte 2) 84 | if (is_all_zeroes (&pdata[2], RFID_DATA_SIZE)) 85 | { 86 | LOG_ERROR ( 87 | LOG_STATE_ON, 88 | "%s: Got tag_present with UID = 0 (RFID port #%u)\n", 89 | __func__, 90 | app_port->portnumber); 91 | return; 92 | } 93 | 94 | tag_is_present[port_index] = true; 95 | char UUID[30]; 96 | char * p = UUID; 97 | int i; 98 | for (i = 0; i < RFID_DATA_SIZE; i++) 99 | { 100 | p += sprintf (p, "%02X", pdata[2 + i]); 101 | } 102 | LOG_INFO ( 103 | LOG_STATE_ON, 104 | "Port %u: RFID Tag data: %s\n", 105 | app_port->portnumber, 106 | UUID); 107 | } 108 | } 109 | else if (tag_is_present[port_index]) // tagPresent transition from 1 to 0 110 | { 111 | tag_is_present[port_index] = false; 112 | LOG_INFO ( 113 | LOG_STATE_ON, 114 | "Port %u: RFID Tag not present\n", 115 | app_port->portnumber); 116 | } 117 | } 118 | 119 | void app_ifm_rfid_setup (app_port_ctx_t * app_port) 120 | { 121 | uint8_t data[2] = {0, 0}; 122 | 123 | LOG_INFO (LOG_STATE_ON, "Set up RFID on port %u\n", app_port->portnumber); 124 | 125 | // Blocksize 4 126 | data[0] = 4; 127 | if (app_smi_device_write (app_port, 1900, 0, 1, data) != IOLINK_ERROR_NONE) 128 | { 129 | LOG_ERROR ( 130 | LOG_STATE_ON, 131 | "Set up RFID blocksize failed on port %u\n", 132 | app_port->portnumber); 133 | return; 134 | } 135 | 136 | // Data order normal 137 | data[0] = 0; 138 | if (app_smi_device_write (app_port, 1901, 0, 1, data) != IOLINK_ERROR_NONE) 139 | { 140 | LOG_ERROR ( 141 | LOG_STATE_ON, 142 | "Set up RFID data order failed on port %u\n", 143 | app_port->portnumber); 144 | return; 145 | } 146 | 147 | // Data hold time 148 | data[0] = 0; 149 | data[1] = 50; 150 | if (app_smi_device_write (app_port, 1902, 0, 2, data) != IOLINK_ERROR_NONE) 151 | { 152 | LOG_ERROR ( 153 | LOG_STATE_ON, 154 | "Set up RFID data hold failed on port %u\n", 155 | app_port->portnumber); 156 | return; 157 | } 158 | 159 | // Auto read/write length 160 | data[0] = 4; 161 | if (app_smi_device_write (app_port, 1904, 0, 1, data) != IOLINK_ERROR_NONE) 162 | { 163 | LOG_ERROR ( 164 | LOG_STATE_ON, 165 | "Set up RFID auto read/write failed on port %u\n", 166 | app_port->portnumber); 167 | return; 168 | } 169 | 170 | app_port->type = IFM_RFID; 171 | 172 | app_port->run_function = app_ifm_rfid_run; 173 | 174 | app_port->app_port_state = IOL_STATE_RUNNING; 175 | } 176 | 177 | // ifmMHI Process data offsets 178 | #define VALUE_LINE_1_MSB 0 179 | #define VALUE_LINE_1_LSB 1 180 | #define VALUE_LINE_2_MSB 2 181 | #define VALUE_LINE_2_LSB 3 182 | #define COLOR_LINE_1 8 183 | #define COLOR_LINE_2 9 184 | #define LEDS 12 185 | #define LAYOUT 13 186 | 187 | // ifmMHI Process data values 188 | #define DOWN_KEY_PRESSED BIT (0) 189 | #define UP_KEY_PRESSED BIT (1) 190 | #define ENTER_KEY_PRESSED BIT (2) 191 | #define ESC_KEY_PRESSED BIT (3) 192 | #define BLACK_WHITE 0 193 | #define RED 1 194 | #define GREEN 2 195 | #define YELLOW 3 196 | #define BLINK 40 197 | #define DISPLAY_TEXT 80 198 | #define LED_1_ON BIT (0) 199 | #define LED_1_OFF 0 200 | #define LED_2_ON BIT (2) 201 | #define LED_2_OFF 0 202 | #define ONE_LINE 1 203 | #define TWO_LINES 2 204 | #define TEXT_ID(n) (n) 205 | 206 | #define IFM_MHI_PD_SIZE 16 207 | 208 | static void app_ifm_hmi_run (app_port_ctx_t * app_port) 209 | { 210 | static uint32_t itr = 0; 211 | static uint8_t prev_hmi_pdin = 0; 212 | static uint8_t hmi_pdin = 0; 213 | bool pdin_valid = false; 214 | 215 | itr++; 216 | 217 | if ((itr % 100) == 0) 218 | { 219 | static uint8_t addr = 0; 220 | static uint8_t data[12] = { 221 | 'A', 222 | ' ', 223 | ' ', 224 | ' ', 225 | ' ', 226 | ' ', 227 | ' ', 228 | ' ', 229 | ' ', 230 | ' ', 231 | ' ', 232 | ' ', 233 | }; 234 | static uint8_t i = 0; 235 | 236 | /* 90 or 91, description of line 1 and 2 */ 237 | uint16_t index = 90 + (addr % 2); 238 | 239 | if (app_smi_device_write (app_port, index, 0, sizeof (data), data) != IOLINK_ERROR_NONE) 240 | { 241 | LOG_WARNING ( 242 | LOG_STATE_ON, 243 | "%s: Failed to write on port %u\n", 244 | __func__, 245 | app_port->portnumber); 246 | } 247 | 248 | if (data[i] == 'Z') 249 | { 250 | i++; 251 | i = i % 12; 252 | data[i] = 'A'; 253 | } 254 | else 255 | { 256 | data[i]++; 257 | } 258 | addr++; 259 | } 260 | 261 | if (app_smi_pdin (app_port, &pdin_valid, &hmi_pdin) != sizeof (hmi_pdin) != IOLINK_ERROR_NONE) 262 | { 263 | LOG_WARNING ( 264 | LOG_STATE_ON, 265 | "%s: Failed to get PDIn from port %u\n", 266 | __func__, 267 | app_port->portnumber); 268 | } 269 | else 270 | { 271 | if (!pdin_valid) 272 | { 273 | LOG_WARNING ( 274 | LOG_STATE_ON, 275 | "%s: PDIn data is invalid for port %u\n", 276 | __func__, 277 | app_port->portnumber); 278 | } 279 | else 280 | { 281 | if (prev_hmi_pdin != hmi_pdin) 282 | { 283 | LOG_INFO ( 284 | LOG_STATE_ON, 285 | "\nUp: %u\nDown: %u\nEnter: %u\nEsc: %u\n\n", 286 | hmi_pdin & UP_KEY_PRESSED ? 1 : 0, 287 | hmi_pdin & DOWN_KEY_PRESSED ? 1 : 0, 288 | hmi_pdin & ENTER_KEY_PRESSED ? 1 : 0, 289 | hmi_pdin & ESC_KEY_PRESSED ? 1 : 0); 290 | prev_hmi_pdin = hmi_pdin; 291 | } 292 | } 293 | } 294 | 295 | if ((itr % 512) == 0) 296 | { 297 | static bool show_text = false; 298 | 299 | uint8_t data[IFM_MHI_PD_SIZE]; 300 | memset (data, 0, sizeof (data)); 301 | 302 | if (show_text) 303 | { 304 | data[VALUE_LINE_1_LSB] = TEXT_ID (4); 305 | data[COLOR_LINE_1] = YELLOW | DISPLAY_TEXT; 306 | data[LEDS] = LED_1_OFF | LED_2_ON; 307 | data[LAYOUT] = ONE_LINE; 308 | } 309 | else 310 | { 311 | uint32_t val = itr - 512; 312 | data[VALUE_LINE_1_MSB] = val >> 8; 313 | data[VALUE_LINE_1_LSB] = val; 314 | data[VALUE_LINE_2_MSB] = val >> 18; 315 | data[VALUE_LINE_2_LSB] = val >> 10; 316 | data[COLOR_LINE_1] = GREEN; 317 | data[COLOR_LINE_2] = RED | BLINK; 318 | data[LEDS] = LED_1_ON | LED_2_OFF; 319 | data[LAYOUT] = TWO_LINES; 320 | } 321 | 322 | app_smi_pdout (app_port, true, sizeof (data), data); 323 | show_text = !show_text; 324 | } 325 | 326 | if ((itr % 2048) == 0) 327 | { 328 | if (app_smi_device_read (app_port, 90, 0, 17, NULL, NULL) != IOLINK_ERROR_NONE) 329 | { 330 | LOG_WARNING ( 331 | LOG_STATE_ON, 332 | "%s: Failed to read from port %u\n", 333 | __func__, 334 | app_port->portnumber); 335 | } 336 | } 337 | 338 | if ((itr % 8192) == 0) 339 | { 340 | if (app_smi_pdinout (app_port) != IOLINK_ERROR_NONE) 341 | { 342 | LOG_WARNING ( 343 | LOG_STATE_ON, 344 | "%s: PDInOut failed on port %u\n", 345 | __func__, 346 | app_port->portnumber); 347 | } 348 | } 349 | } 350 | 351 | void app_ifm_hmi_setup (app_port_ctx_t * app_port) 352 | { 353 | uint8_t data[IFM_MHI_PD_SIZE]; 354 | memset (data, 0, sizeof (data)); 355 | 356 | LOG_INFO (LOG_STATE_ON, "Set up HMI on port %u\n", app_port->portnumber); 357 | 358 | data[VALUE_LINE_1_LSB] = TEXT_ID (3); 359 | data[COLOR_LINE_1] = BLACK_WHITE | DISPLAY_TEXT; 360 | data[LEDS] = LED_1_ON | LED_2_OFF; 361 | data[LAYOUT] = TWO_LINES; 362 | 363 | app_smi_pdout (app_port, true, sizeof (data), data); 364 | 365 | app_port->type = IFM_HMI; 366 | 367 | app_port->run_function = app_ifm_hmi_run; 368 | 369 | app_port->app_port_state = IOL_STATE_RUNNING; 370 | } 371 | -------------------------------------------------------------------------------- /samples/ifm_sample_app/app_ifm.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2021 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifndef APP_IFM_H 17 | #define APP_IFM_H 18 | 19 | #include "app_handler.h" 20 | 21 | #define IFM_VENDOR_ID 0x0136 22 | 23 | #define IFM_RFID_DEVICE_ID 0x03C7 24 | #define IFM_HMI_DEVICE_ID 0x02A9 25 | 26 | void app_ifm_rfid_setup (app_port_ctx_t * app_port); 27 | 28 | void app_ifm_hmi_setup (app_port_ctx_t * app_port); 29 | 30 | #endif // APP_IFM_H 31 | -------------------------------------------------------------------------------- /samples/ifm_sample_app/app_smi.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2021 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #include 17 | #include "osal.h" 18 | #include "osal_log.h" 19 | #include "iolink.h" 20 | #include "iolink_main.h" /* TODO Use headers from include/ directory */ 21 | #include "iolink_dl.h" /* TODO Use headers from include/ directory */ 22 | #include "app_handler.h" 23 | 24 | iolink_smi_errortypes_t app_smi_device_write ( 25 | app_port_ctx_t * app_port, 26 | uint16_t index, 27 | uint8_t subindex, 28 | uint8_t len, 29 | const uint8_t * data) 30 | { 31 | uint8_t arg_block_len = sizeof (arg_block_od_t) + len; 32 | uint8_t buffer[arg_block_len]; 33 | arg_block_od_t * arg_block_od = (arg_block_od_t *)buffer; 34 | 35 | memset (buffer, 0, arg_block_len); 36 | arg_block_od->arg_block.id = IOLINK_ARG_BLOCK_ID_OD_WR; 37 | arg_block_od->index = index; 38 | arg_block_od->subindex = subindex; 39 | 40 | memcpy (arg_block_od->data, data, len); 41 | 42 | iolink_error_t err = SMI_DeviceWrite_req ( 43 | app_port->portnumber, 44 | IOLINK_ARG_BLOCK_ID_VOID_BLOCK, 45 | arg_block_len, 46 | (arg_block_t *)arg_block_od); 47 | if (err != IOLINK_ERROR_NONE) 48 | { 49 | LOG_WARNING ( 50 | LOG_STATE_ON, 51 | "%s failed (SMI_DeviceWrite_req returned %d) for port %u\n", 52 | __func__, 53 | err, 54 | app_port->portnumber); 55 | 56 | return (err == IOLINK_ERROR_ODLENGTH) 57 | ? IOLINK_SMI_ERRORTYPE_VAL_LENOVRRUN 58 | : IOLINK_SMI_ERRORTYPE_APP_DEV; // TODO 59 | } 60 | 61 | return app_wait_for_cnf (app_port, SMI_WRITE_CNF, 10000); 62 | } 63 | 64 | iolink_smi_errortypes_t app_smi_device_read ( 65 | app_port_ctx_t * app_port, 66 | uint16_t index, 67 | uint8_t subindex, 68 | uint8_t len, 69 | uint8_t * data, 70 | uint8_t * actual_len) 71 | { 72 | if (len == 0) 73 | { 74 | LOG_WARNING ( 75 | LOG_STATE_ON, 76 | "%s: len == 0 for port %u\n", 77 | __func__, 78 | app_port->portnumber); 79 | 80 | return IOLINK_SMI_ERRORTYPE_ARGBLOCK_INCONSISTENT; // TODO what to return? 81 | } 82 | 83 | uint16_t arg_block_len = sizeof (arg_block_od_t) + len; 84 | uint8_t buffer[arg_block_len]; 85 | arg_block_od_t * arg_block_od = (arg_block_od_t *)buffer; 86 | 87 | memset (buffer, 0, arg_block_len); 88 | arg_block_od->arg_block.id = IOLINK_ARG_BLOCK_ID_OD_RD; 89 | arg_block_od->index = index; 90 | arg_block_od->subindex = subindex; 91 | 92 | iolink_smi_errortypes_t errortype = IOLINK_SMI_ERRORTYPE_NONE; 93 | iolink_error_t err = SMI_DeviceRead_req ( 94 | app_port->portnumber, 95 | IOLINK_ARG_BLOCK_ID_OD_RD, 96 | arg_block_len, 97 | (arg_block_t *)arg_block_od); 98 | if (err != IOLINK_ERROR_NONE) 99 | { 100 | LOG_WARNING ( 101 | LOG_STATE_ON, 102 | "%s failed (SMI_DeviceRead_req returned %d) for port %u\n", 103 | __func__, 104 | err, 105 | app_port->portnumber); 106 | 107 | errortype = IOLINK_SMI_ERRORTYPE_APP_DEV; // TODO 108 | } 109 | else 110 | { 111 | uint32_t event_value; 112 | 113 | /* Wait for SMI_DeviceRead_cnf() */ 114 | if (os_event_wait (app_port->event, SMI_READ_CNF, &event_value, 10000)) 115 | { 116 | errortype = app_port->errortype; 117 | 118 | LOG_WARNING ( 119 | LOG_STATE_ON, 120 | "%s: timeout for port %u\n", 121 | __func__, 122 | app_port->portnumber); 123 | 124 | if (errortype != IOLINK_SMI_ERRORTYPE_NONE) 125 | { 126 | app_port->errortype = IOLINK_SMI_ERRORTYPE_NONE; 127 | } 128 | else 129 | { 130 | errortype = IOLINK_SMI_ERRORTYPE_APP_DEV; // TODO 131 | } 132 | } 133 | else 134 | { 135 | os_event_clr (app_port->event, event_value); 136 | 137 | if (data != NULL) 138 | { 139 | if (app_port->param_read.data_len > 0) 140 | { 141 | memcpy (data, arg_block_od->data, len); 142 | 143 | if (actual_len != NULL) 144 | { 145 | *actual_len = app_port->param_read.data_len; 146 | } 147 | } 148 | 149 | app_port->param_read.data_len = 0; 150 | } 151 | } 152 | } 153 | 154 | return errortype; 155 | } 156 | 157 | uint8_t app_smi_pdout ( 158 | app_port_ctx_t * app_port, 159 | bool output_enable, 160 | uint8_t len, 161 | const uint8_t * data) 162 | { 163 | os_mutex_lock (app_port->pdout_mtx); 164 | 165 | arg_block_pdout_t arg_block_pdout; 166 | 167 | arg_block_pdout.h.arg_block.id = IOLINK_ARG_BLOCK_ID_PD_OUT; 168 | memcpy (arg_block_pdout.data, data, len); 169 | arg_block_pdout.h.len = len; 170 | arg_block_pdout.h.oe = (output_enable) ? 1 : 0; 171 | 172 | iolink_error_t err = SMI_PDOut_req ( 173 | app_port->portnumber, 174 | IOLINK_ARG_BLOCK_ID_VOID_BLOCK, 175 | sizeof (arg_block_pdout_head_t) + len, 176 | (arg_block_t *)&arg_block_pdout); 177 | os_mutex_unlock (app_port->pdout_mtx); 178 | 179 | return (err == IOLINK_ERROR_NONE) ? 0 : 1; 180 | } 181 | 182 | int8_t app_smi_pdin (app_port_ctx_t * app_port, bool * valid, uint8_t * pdin) 183 | { 184 | arg_block_void_t arg_block_void; 185 | int8_t len = 0; 186 | 187 | memset (&arg_block_void, 0, sizeof (arg_block_void_t)); 188 | arg_block_void.arg_block.id = IOLINK_ARG_BLOCK_ID_VOID_BLOCK; 189 | 190 | if ( 191 | SMI_PDIn_req ( 192 | app_port->portnumber, 193 | IOLINK_ARG_BLOCK_ID_PD_IN, 194 | sizeof (arg_block_void_t), 195 | (arg_block_t *)&arg_block_void) == IOLINK_ERROR_NONE) 196 | { 197 | len = app_port->pdin.data_len; 198 | 199 | memcpy (pdin, app_port->pdin.data, len); 200 | 201 | if (valid != NULL) 202 | { 203 | *valid = !(app_port->pdin.pqi & IOLINK_PORT_QUALIFIER_INFO_PQ_INVALID); 204 | 205 | iolink_port_t * port = 206 | iolink_get_port (app_master.master, app_port->portnumber); 207 | iolink_get_dl_ctx (port)->pd_handler.pd_valid = *valid; 208 | } 209 | } 210 | 211 | return len; 212 | } 213 | 214 | uint8_t app_smi_pdinout (app_port_ctx_t * app_port) 215 | { 216 | arg_block_void_t arg_block_void; 217 | 218 | memset (&arg_block_void, 0, sizeof (arg_block_void_t)); 219 | arg_block_void.arg_block.id = IOLINK_ARG_BLOCK_ID_VOID_BLOCK; 220 | 221 | iolink_error_t err = SMI_PDInOut_req ( 222 | app_port->portnumber, 223 | IOLINK_ARG_BLOCK_ID_PD_IN_OUT, 224 | sizeof (arg_block_void_t), 225 | (arg_block_t *)&arg_block_void); 226 | 227 | return (err == IOLINK_ERROR_NONE) ? 0 : 1; 228 | } 229 | -------------------------------------------------------------------------------- /samples/ifm_sample_app/app_smi.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2021 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifndef APP_SMI_H 17 | #define APP_SMI_H 18 | 19 | #include "iolink.h" 20 | #include "app_handler.h" 21 | 22 | iolink_smi_errortypes_t app_smi_device_write ( 23 | app_port_ctx_t * app_port, 24 | uint16_t index, 25 | uint8_t subindex, 26 | uint8_t len, 27 | const uint8_t * data); 28 | 29 | iolink_smi_errortypes_t app_smi_device_read ( 30 | app_port_ctx_t * app_port, 31 | uint16_t index, 32 | uint8_t subindex, 33 | uint8_t len, 34 | uint8_t * data, 35 | uint8_t * actual_len); 36 | 37 | uint8_t app_smi_pdout ( 38 | app_port_ctx_t * app_port, 39 | bool output_enable, 40 | uint8_t len, 41 | const uint8_t * data); 42 | 43 | int8_t app_smi_pdin (app_port_ctx_t * app_port, bool * valid, uint8_t * pdin); 44 | 45 | uint8_t app_smi_pdinout (app_port_ctx_t * app_port); 46 | 47 | #endif // APP_SMI_H 48 | -------------------------------------------------------------------------------- /samples/ifm_sample_app/ports/linux/main.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2021 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #include 17 | #include 18 | 19 | #include "osal.h" 20 | #include "osal_irq.h" 21 | #include "osal_log.h" 22 | #include "iolink.h" 23 | #include "iolink_max14819.h" 24 | #include "app_handler.h" 25 | 26 | #define APP_MASTER_THREAD_STACK_SIZE (4 * 1024) 27 | #define APP_MASTER_THREAD_PRIO 6 28 | #define APP_DL_THREAD_STACK_SIZE 1500 29 | #define APP_DL_THREAD_PRIO (APP_MASTER_THREAD_PRIO + 1) 30 | 31 | static iolink_pl_mode_t mode_ch[] = { 32 | iolink_mode_SDCI, 33 | iolink_mode_INACTIVE, 34 | }; 35 | 36 | int main (int argc, char ** argv) 37 | { 38 | os_thread_t * app_handler_thread; 39 | iolink_hw_drv_t * hw; 40 | 41 | if (argc != 4) 42 | { 43 | printf ("usage: %s \n", argv[0]); 44 | return -1; 45 | } 46 | 47 | char * spi = argv[1]; 48 | int irq = atoi (argv[2]); 49 | int address = atoi (argv[3]); 50 | 51 | iolink_14819_cfg_t iol_14819_0_cfg = { 52 | .chip_address = address, 53 | .chip_irq = irq, 54 | .spi_slave_name = spi, 55 | .CQCfgA = MAX14819_CQCFG_DRVDIS | MAX14819_CQCFG_SINKSEL (0x2), 56 | .LPCnfgA = MAX14819_LPCNFG_LPEN, 57 | .IOStCfgA = MAX14819_IOSTCFG_DICSINK | MAX14819_IOSTCFG_DIEC3TH, 58 | .DrvCurrLim = 0x00, 59 | .Clock = MAX14819_CLOCK_XTALEN | MAX14819_CLOCK_TXTXENDIS, 60 | }; 61 | 62 | hw = iolink_14819_init (&iol_14819_0_cfg); 63 | if (hw == NULL) 64 | { 65 | LOG_ERROR (IOLINK_APP_LOG, "APP: Failed to open driver\n"); 66 | return -1; 67 | } 68 | 69 | iolink_port_cfg_t port_cfgs[] = { 70 | { 71 | .name = "/iolink0/0", 72 | .mode = &mode_ch[0], 73 | .drv = hw, 74 | .arg = (void *)0, 75 | }, 76 | { 77 | .name = "/iolink0/1", 78 | .mode = &mode_ch[1], 79 | .drv = hw, 80 | .arg = (void *)1, 81 | }, 82 | }; 83 | 84 | iolink_m_cfg_t app_cfg = { 85 | .cb_arg = NULL, 86 | .cb_smi = NULL, 87 | .cb_pd = NULL, 88 | .port_cnt = NELEMENTS (port_cfgs), 89 | .port_cfgs = port_cfgs, 90 | .master_thread_prio = APP_MASTER_THREAD_PRIO, 91 | .master_thread_stack_size = APP_MASTER_THREAD_STACK_SIZE, 92 | .dl_thread_prio = APP_DL_THREAD_PRIO, 93 | .dl_thread_stack_size = APP_DL_THREAD_STACK_SIZE, 94 | }; 95 | 96 | os_usleep (200 * 1000); 97 | 98 | app_handler (app_cfg); 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /samples/ifm_sample_app/ports/rt-kernel/main.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2021 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #include 17 | 18 | #include "osal.h" 19 | #include "osal_irq.h" 20 | #include "osal_log.h" 21 | #include "iolink.h" 22 | #include "iolink_max14819.h" 23 | #include "app_handler.h" 24 | #include 25 | 26 | #define APP_MASTER_THREAD_STACK_SIZE (4 * 1024) 27 | #define APP_MASTER_THREAD_PRIO 6 28 | #define APP_DL_THREAD_STACK_SIZE 1500 29 | #define APP_DL_THREAD_PRIO (APP_MASTER_THREAD_PRIO + 1) 30 | #define APP_HANDLER_THREAD_STACK_SIZE (2048) 31 | #define APP_HANDLER_THREAD_PRIO 6 32 | 33 | 34 | #ifdef SPI_IOLINK0 35 | #define APP_IOLINK_CHIP0_SPI SPI_IOLINK0 36 | #define APP_IOLINK_CHIP0_IRQ IRQ_IOLINK0 37 | #endif 38 | 39 | #ifdef ADR_IOLINK0 40 | #define APP_IOLINK_CHIP0_ADDRESS ADR_IOLINK0 41 | #endif 42 | 43 | #ifdef SPI_IOLINK1 44 | #define APP_IOLINK_CHIP1_SPI SPI_IOLINK1 45 | #define APP_IOLINK_CHIP1_IRQ IRQ_IOLINK1 46 | #endif 47 | 48 | #ifdef ADR_IOLINK1 49 | #define APP_IOLINK_CHIP1_ADDRESS ADR_IOLINK1 50 | #endif 51 | 52 | 53 | #ifndef APP_IOLINK_CHIP0_ADDRESS 54 | #define APP_IOLINK_CHIP0_ADDRESS 0x0 55 | #endif 56 | 57 | #ifndef APP_IOLINK_CHIP1_ADDRESS 58 | #define APP_IOLINK_CHIP1_ADDRESS 0x0 59 | #endif 60 | 61 | static iolink_pl_mode_t mode_ch[] = { 62 | #ifdef APP_IOLINK_CHIP0_SPI 63 | iolink_mode_SDCI, 64 | iolink_mode_INACTIVE, 65 | #endif 66 | #ifdef APP_IOLINK_CHIP1_SPI 67 | iolink_mode_SDCI, 68 | iolink_mode_INACTIVE, 69 | #endif 70 | }; 71 | 72 | void app_handler_thread_start (void * ctx) 73 | { 74 | const iolink_m_cfg_t * cfg = (const iolink_m_cfg_t *)ctx; 75 | app_handler (*cfg); 76 | } 77 | 78 | static iolink_hw_drv_t * app_14819_init ( 79 | const char * name, 80 | const iolink_14819_cfg_t * cfg, 81 | int irq) 82 | { 83 | iolink_hw_drv_t * drv; 84 | drv = iolink_14819_init (cfg); 85 | if (drv == NULL) 86 | { 87 | LOG_ERROR (IOLINK_APP_LOG, "APP: Failed to open SPI %s\n", name); 88 | return NULL; 89 | } 90 | return drv; 91 | } 92 | 93 | int main (int argc, char ** argv) 94 | { 95 | os_thread_t * app_handler_thread; 96 | iolink_hw_drv_t * hw[2]; 97 | 98 | #ifdef APP_IOLINK_CHIP0_SPI 99 | static const iolink_14819_cfg_t iol_14819_0_cfg = { 100 | .chip_address = APP_IOLINK_CHIP0_ADDRESS, 101 | .chip_irq = APP_IOLINK_CHIP0_IRQ, 102 | .spi_slave_name = APP_IOLINK_CHIP0_SPI, 103 | .CQCfgA = MAX14819_CQCFG_DRVDIS | MAX14819_CQCFG_SINKSEL (0x2), 104 | .LPCnfgA = MAX14819_LPCNFG_LPEN, 105 | .IOStCfgA = MAX14819_IOSTCFG_DICSINK | MAX14819_IOSTCFG_DIEC3TH, 106 | .DrvCurrLim = 0x00, 107 | .Clock = MAX14819_CLOCK_XTALEN | MAX14819_CLOCK_TXTXENDIS, 108 | }; 109 | #endif 110 | 111 | #ifdef APP_IOLINK_CHIP1_SPI 112 | static const iolink_14819_cfg_t iol_14819_1_cfg = { 113 | .chip_address = APP_IOLINK_CHIP1_ADDRESS, 114 | .chip_irq = APP_IOLINK_CHIP1_IRQ, 115 | .spi_slave_name = APP_IOLINK_CHIP1_SPI, 116 | .CQCfgA = MAX14819_CQCFG_DRVDIS | MAX14819_CQCFG_SINKSEL (0x2), 117 | .LPCnfgA = MAX14819_LPCNFG_LPEN, 118 | .IOStCfgA = MAX14819_IOSTCFG_DICSINK | MAX14819_IOSTCFG_DIEC3TH, 119 | .DrvCurrLim = 0x00, 120 | .Clock = MAX14819_CLOCK_XTALEN | MAX14819_CLOCK_TXTXENDIS, 121 | }; 122 | #endif 123 | 124 | #ifdef APP_IOLINK_CHIP0_SPI 125 | hw[0] = iolink_14819_init (&iol_14819_0_cfg); 126 | if (hw[0] == NULL) 127 | { 128 | LOG_ERROR (IOLINK_APP_LOG, "APP: Failed to open driver: 0\n"); 129 | exit(-1); 130 | } 131 | #endif 132 | 133 | #ifdef APP_IOLINK_CHIP1_SPI 134 | hw[1] = iolink_14819_init (&iol_14819_1_cfg); 135 | if (hw[1] == NULL) 136 | { 137 | LOG_ERROR (IOLINK_APP_LOG, "APP: Failed to open driver: 1\n"); 138 | exit(-1); 139 | } 140 | #endif 141 | 142 | iolink_port_cfg_t port_cfgs[] = { 143 | #ifdef APP_IOLINK_CHIP0_SPI 144 | { 145 | .name = "/iolink0/0", 146 | .mode = &mode_ch[0], 147 | .drv = hw[0], 148 | .arg = (void *)0, 149 | }, 150 | { 151 | .name = "/iolink0/1", 152 | .mode = &mode_ch[1], 153 | .drv = hw[0], 154 | .arg = (void *)1, 155 | }, 156 | #endif 157 | #ifdef APP_IOLINK_CHIP1_SPI 158 | { 159 | .name = "/iolink1/0", 160 | .mode = &mode_ch[2], 161 | .drv = hw[1], 162 | .arg = (void *)0, 163 | }, 164 | { 165 | .name = "/iolink1/1", 166 | .mode = &mode_ch[3], 167 | .drv = hw[1], 168 | .arg = (void *)1, 169 | }, 170 | #endif 171 | }; 172 | 173 | iolink_m_cfg_t app_cfg = { 174 | .cb_arg = NULL, 175 | .cb_smi = NULL, 176 | .cb_pd = NULL, 177 | .port_cnt = NELEMENTS (port_cfgs), 178 | .port_cfgs = port_cfgs, 179 | .master_thread_prio = APP_MASTER_THREAD_PRIO, 180 | .master_thread_stack_size = APP_MASTER_THREAD_STACK_SIZE, 181 | .dl_thread_prio = APP_DL_THREAD_PRIO, 182 | .dl_thread_stack_size = APP_DL_THREAD_STACK_SIZE, 183 | }; 184 | 185 | os_usleep (200 * 1000); 186 | 187 | app_handler_thread = os_thread_create ( 188 | "app_handler_thread", 189 | APP_HANDLER_THREAD_PRIO, 190 | APP_HANDLER_THREAD_STACK_SIZE, 191 | app_handler_thread_start, 192 | (void *)&app_cfg); 193 | CC_ASSERT (app_handler_thread != NULL); 194 | 195 | for (;;) 196 | { 197 | os_usleep (1000 * 1000); 198 | } 199 | 200 | return 0; 201 | } 202 | -------------------------------------------------------------------------------- /samples/ifm_sample_app/ports/windows/main.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2025 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #include 17 | #include 18 | 19 | #include "osal.h" 20 | #include "osal_irq.h" 21 | #include "osal_log.h" 22 | #include "iolink.h" 23 | #include "iolink_max14819.h" 24 | #include "app_handler.h" 25 | 26 | #define APP_MASTER_THREAD_STACK_SIZE (4 * 1024) 27 | #define APP_MASTER_THREAD_PRIO 6 28 | #define APP_DL_THREAD_STACK_SIZE 1500 29 | #define APP_DL_THREAD_PRIO (APP_MASTER_THREAD_PRIO + 1) 30 | 31 | static iolink_pl_mode_t mode_ch[] = { 32 | iolink_mode_SDCI, 33 | iolink_mode_INACTIVE, 34 | }; 35 | 36 | int main (int argc, char ** argv) 37 | { 38 | os_thread_t * app_handler_thread; 39 | iolink_hw_drv_t * hw; 40 | 41 | if (argc != 4) 42 | { 43 | printf ("usage: %s \n", argv[0]); 44 | return -1; 45 | } 46 | 47 | char * spi = argv[1]; 48 | int irq = atoi (argv[2]); 49 | int address = atoi (argv[3]); 50 | 51 | iolink_14819_cfg_t iol_14819_0_cfg = { 52 | .chip_address = address, 53 | .chip_irq = irq, 54 | .spi_slave_name = spi, 55 | .CQCfgA = MAX14819_CQCFG_DRVDIS | MAX14819_CQCFG_SINKSEL (0x2), 56 | .LPCnfgA = MAX14819_LPCNFG_LPEN, 57 | .IOStCfgA = MAX14819_IOSTCFG_DICSINK | MAX14819_IOSTCFG_DIEC3TH, 58 | .DrvCurrLim = 0x00, 59 | .Clock = MAX14819_CLOCK_XTALEN | MAX14819_CLOCK_TXTXENDIS, 60 | }; 61 | 62 | hw = iolink_14819_init (&iol_14819_0_cfg); 63 | if (hw == NULL) 64 | { 65 | LOG_ERROR (IOLINK_APP_LOG, "APP: Failed to open driver\n"); 66 | return -1; 67 | } 68 | 69 | iolink_port_cfg_t port_cfgs[] = { 70 | { 71 | .name = "/iolink0/0", 72 | .mode = &mode_ch[0], 73 | .drv = hw, 74 | .arg = (void *)0, 75 | }, 76 | { 77 | .name = "/iolink0/1", 78 | .mode = &mode_ch[1], 79 | .drv = hw, 80 | .arg = (void *)1, 81 | }, 82 | }; 83 | 84 | iolink_m_cfg_t app_cfg = { 85 | .cb_arg = NULL, 86 | .cb_smi = NULL, 87 | .cb_pd = NULL, 88 | .port_cnt = NELEMENTS (port_cfgs), 89 | .port_cfgs = port_cfgs, 90 | .master_thread_prio = APP_MASTER_THREAD_PRIO, 91 | .master_thread_stack_size = APP_MASTER_THREAD_STACK_SIZE, 92 | .dl_thread_prio = APP_DL_THREAD_PRIO, 93 | .dl_thread_stack_size = APP_DL_THREAD_STACK_SIZE, 94 | }; 95 | 96 | os_usleep (200 * 1000); 97 | 98 | app_handler (app_cfg); 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /samples/irq_test/irq_test.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2021 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #include 17 | #include 18 | #include "osal.h" 19 | #include "osal_irq.h" 20 | 21 | void isr_func (void* irq_arg) 22 | { 23 | printf ("\n%s: GPIO pin %p interrupt occurred\n", __func__, irq_arg); 24 | } 25 | 26 | /**************************************************************** 27 | * Main 28 | ****************************************************************/ 29 | int main (int argc, char ** argv) 30 | { 31 | if (argc < 2) 32 | { 33 | printf ("Usage: %s \n\n", argv[0]); 34 | printf ("Waits for a change in the GPIO pin voltage level or input on " 35 | "stdin\n"); 36 | exit (-1); 37 | } 38 | 39 | _iolink_setup_int (atoi (argv[1]), isr_func, (void*)(intptr_t)atoi (argv[1])); 40 | 41 | while (true) 42 | { 43 | } 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /src/iolink_al.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | /** 17 | * @file 18 | * @brief Application layer 19 | * 20 | */ 21 | 22 | #ifndef IOLINK_AL_H 23 | #define IOLINK_AL_H 24 | 25 | #include "iolink_main.h" /* iolink_job_t */ 26 | #include "iolink.h" 27 | #include "iolink_types.h" 28 | #include "iolink_options.h" /* IOLINK_MAX_EVENTS */ 29 | #include "osal.h" 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | typedef enum iolink_al_od_state 36 | { 37 | AL_OD_STATE_OnReq_Idle = 0, 38 | AL_OD_STATE_Build_DL_Service, 39 | AL_OD_STATE_Await_DL_param_cnf, 40 | AL_OD_STATE_Await_DL_ISDU_cnf, 41 | AL_OD_STATE_LAST 42 | } iolink_al_od_state_t; 43 | 44 | typedef enum iolink_al_event_state 45 | { 46 | AL_EVENT_STATE_Event_inactive = 0, 47 | AL_EVENT_STATE_Event_idle, 48 | AL_EVENT_STATE_Read_Event_Set, 49 | AL_EVENT_STATE_DU_Event_handling, 50 | AL_EVENT_STATE_LAST 51 | } iolink_al_event_state_t; 52 | 53 | typedef struct iolink_al_port 54 | { 55 | iolink_al_od_state_t od_state; 56 | iolink_al_event_state_t event_state; 57 | 58 | uint8_t pdin_data[IOLINK_PD_MAX_SIZE]; 59 | uint8_t pdin_data_len; 60 | os_mutex_t * mtx_pdin; 61 | struct 62 | { 63 | uint16_t index; 64 | uint8_t subindex; 65 | iolink_rwdirection_t direction; 66 | uint8_t data_read[IOLINK_ISDU_MAX_SIZE]; 67 | const uint8_t * data_write; 68 | uint8_t data_len; 69 | iolink_status_t errinfo; 70 | iservice_t qualifier; 71 | iolink_job_t * job_req; 72 | union 73 | { 74 | void (*al_read_cnf_cb) ( 75 | iolink_port_t * port, 76 | uint8_t len, 77 | const uint8_t * data, 78 | iolink_smi_errortypes_t errortype); 79 | void (*al_write_cnf_cb) ( 80 | iolink_port_t * port, 81 | iolink_smi_errortypes_t errortype); 82 | }; 83 | } service; 84 | 85 | struct 86 | { 87 | uint8_t event_cnt; 88 | diag_entry_t events[IOLINK_MAX_EVENTS]; 89 | } event; 90 | } iolink_al_port_t; 91 | 92 | /** 93 | * AL OD state-machine events. 94 | * 95 | * AL events trigger state transitions in the OD FSM. 96 | */ 97 | typedef enum iolink_fsm_al_od_event 98 | { 99 | AL_OD_EVENT_NONE = 0, 100 | AL_OD_EVENT_service, /* T1, T16, T8 and T12 */ 101 | AL_OD_EVENT_arg_err, /* T2 */ 102 | AL_OD_EVENT_param_read, /* T3 */ 103 | AL_OD_EVENT_param_write_1, /* T4 */ 104 | AL_OD_EVENT_param_write_2, /* T5 */ 105 | AL_OD_EVENT_isdu_read, /* T6 */ 106 | AL_OD_EVENT_isdu_write, /* T7 */ 107 | AL_OD_EVENT_abort, /* T9 and T11 */ 108 | AL_OD_EVENT_rwparam, /* T10 */ 109 | AL_OD_EVENT_readparam_cnf, /* T13 */ 110 | AL_OD_EVENT_writeparam_cnf, /* T14 */ 111 | AL_OD_EVENT_isdutransport_cnf, /* T15 */ 112 | AL_OD_EVENT_abort_port, /* T17 */ 113 | AL_OD_EVENT_LAST 114 | } iolink_fsm_al_od_event_t; 115 | 116 | /** 117 | * AL Event state-machine events. 118 | * 119 | * AL events trigger state transitions in the Event FSM. 120 | */ 121 | typedef enum iolink_fsm_al_event_event 122 | { 123 | AL_EVENT_EVENT_NONE = 0, 124 | AL_EVENT_EVENT_dl_event_ind_more, /* T3 or T4 */ 125 | AL_EVENT_EVENT_dl_event_ind_done, /* T5 or T3 + T5 */ 126 | AL_EVENT_EVENT_al_event_rsp, /* T6 */ 127 | AL_EVENT_EVENT_al_event_req, /* T7 */ 128 | AL_EVENT_EVENT_LAST 129 | } iolink_fsm_al_event_event_t; 130 | 131 | iolink_error_t AL_Read_req ( 132 | iolink_port_t * port, 133 | uint16_t index, 134 | uint8_t subindex, 135 | void (*al_read_cnf_cb) ( 136 | iolink_port_t * port, 137 | uint8_t len, 138 | const uint8_t * data, 139 | iolink_smi_errortypes_t errortype)); 140 | iolink_error_t AL_Write_req ( 141 | iolink_port_t * port, 142 | uint16_t index, 143 | uint8_t subindex, 144 | uint8_t len, 145 | const uint8_t * data, 146 | void (*al_write_cnf_cb) ( 147 | iolink_port_t * port, 148 | iolink_smi_errortypes_t errortype)); 149 | iolink_error_t AL_SetOutput_req (iolink_port_t * port, uint8_t * data); 150 | iolink_error_t AL_Control_req ( 151 | iolink_port_t * port, 152 | iolink_controlcode_t controlcode); 153 | iolink_error_t AL_GetInput_req (iolink_port_t * port, uint8_t * len, uint8_t * data); 154 | iolink_error_t AL_GetInputOutput_req ( 155 | iolink_port_t * port, 156 | uint8_t * len, 157 | uint8_t * data); 158 | void DL_Event_ind ( 159 | iolink_port_t * port, 160 | uint16_t eventcode, 161 | uint8_t event_qualifier, 162 | uint8_t eventsleft); 163 | void DL_Control_ind (iolink_port_t * port, iolink_controlcode_t controlcode); 164 | void DL_ReadParam_cnf (iolink_port_t * port, uint8_t value, iolink_status_t errinfo); 165 | void DL_WriteParam_cnf (iolink_port_t * port, iolink_status_t errinfo); 166 | void DL_ISDUTransport_cnf ( 167 | iolink_port_t * port, 168 | uint8_t * data, 169 | uint8_t length, 170 | iservice_t qualifier, 171 | iolink_status_t errinfo); 172 | void DL_PDInputTransport_ind ( 173 | iolink_port_t * port, 174 | uint8_t * inputdata, 175 | uint8_t length); 176 | void AL_Event_ind ( 177 | iolink_port_t * port, 178 | uint8_t event_cnt, 179 | diag_entry_t events[6]); // TODO where to put this? 180 | iolink_error_t AL_Abort (iolink_port_t * port); 181 | iolink_error_t AL_Event_rsp (iolink_port_t * port); 182 | 183 | void iolink_al_init (iolink_port_t * port); 184 | 185 | #ifdef UNIT_TEST 186 | // TODO: A more logical location for this function is in iolink_main, but for 187 | // some 188 | // unknown reason will some unit tests fail when placing it there?? 189 | void iolink_post_job_with_type_and_callback ( 190 | iolink_port_t * port, 191 | iolink_job_t * job, 192 | iolink_job_type_t type, 193 | void (*callback) (struct iolink_job * job)); 194 | #endif 195 | 196 | #ifdef __cplusplus 197 | } 198 | #endif 199 | 200 | #endif /* IOLINK_AL_H */ 201 | -------------------------------------------------------------------------------- /src/iolink_cm.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | /** 17 | * @file 18 | * @brief Configuration Manager 19 | * 20 | */ 21 | 22 | #ifndef IOLINK_CM_H 23 | #define IOLINK_CM_H 24 | 25 | #include "iolink_types.h" 26 | #include "iolink_main.h" 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /** 33 | * CM FSM states 34 | */ 35 | typedef enum iolink_cm_state 36 | { 37 | CM_STATE_CheckPortMode = 0, 38 | CM_STATE_SM_Startup, 39 | CM_STATE_DS_ParamManager, 40 | CM_STATE_PortFault, 41 | CM_STATE_WaitingOnOperate, 42 | CM_STATE_Port_Active, 43 | CM_STATE_Port_DIDO, 44 | CM_STATE_Port_Deactivated, 45 | CM_STATE_LAST, 46 | } iolink_cm_state_t; 47 | 48 | typedef struct iolink_cm_port 49 | { 50 | iolink_cm_state_t state; 51 | iolink_smi_service_req_t smi_req; 52 | portconfiglist_t cfg_list; 53 | iolink_ds_fault_t ds_fault; 54 | } iolink_cm_port_t; 55 | 56 | /** 57 | * CM state-machine events. 58 | * 59 | * CM events trigger state transitions in the CM FSM. 60 | */ 61 | typedef enum iolink_fsm_cm_event 62 | { 63 | CM_EVENT_NONE, 64 | CM_EVENT_IOL_MANUAL, /* T1 */ 65 | CM_EVENT_IOL_AUTOSTART, /* T2 */ 66 | CM_EVENT_SM_COMREADY, /* T3 */ 67 | CM_EVENT_SM_FAULT, /* T4 */ 68 | CM_EVENT_DS_Ready, /* T5 */ 69 | CM_EVENT_DS_Fault, /* T6 */ 70 | CM_EVENT_SM_OPERATE, /* T7 */ 71 | CM_EVENT_SM_COMLOST, /* T8 */ 72 | CM_EVENT_DI_CQ, 73 | /* T9 */ // TODO merge 74 | CM_EVENT_DO_CQ, 75 | /* T10 */ // TODO merge 76 | CM_EVENT_DEACTIVATED, /* T11 */ 77 | CM_EVENT_CFG_CHANGE, /* T12, T13 and T15 */ 78 | CM_EVENT_DS_CHANGE, /* T14 */ 79 | CM_EVENT_UNKNOWN, /* T16 */ 80 | CM_EVENT_SM_COMLOST_DS, /* T8 */ 81 | CM_EVENT_LAST 82 | } iolink_fsm_cm_event_t; 83 | 84 | void iolink_cm_init (iolink_port_t * port); 85 | 86 | void SM_PortMode_ind (iolink_port_t * port, iolink_sm_portmode_t mode); 87 | 88 | void DS_Ready (iolink_port_t * port); 89 | void DS_Change (iolink_port_t * port); 90 | void DS_Fault (iolink_port_t * port, iolink_ds_fault_t fault); 91 | 92 | iolink_error_t cm_SMI_MasterIdentification_req ( 93 | iolink_port_t * port, 94 | iolink_arg_block_id_t exp_arg_block_id, 95 | uint16_t arg_block_len, 96 | arg_block_t * arg_block); 97 | 98 | iolink_error_t cm_SMI_PortConfiguration_req ( 99 | iolink_port_t * port, 100 | iolink_arg_block_id_t exp_arg_block_id, 101 | uint16_t arg_block_len, 102 | arg_block_t * arg_block); 103 | 104 | iolink_error_t cm_SMI_ReadbackPortConfiguration_req ( 105 | iolink_port_t * port, 106 | iolink_arg_block_id_t exp_arg_block_id, 107 | uint16_t arg_block_len, 108 | arg_block_t * arg_block); 109 | 110 | iolink_error_t cm_SMI_PortStatus_req ( 111 | iolink_port_t * port, 112 | iolink_arg_block_id_t exp_arg_block_id, 113 | uint16_t arg_block_len, 114 | arg_block_t * arg_block); 115 | 116 | #ifdef __cplusplus 117 | } 118 | #endif 119 | 120 | #endif /* IOLINK_CM_H */ 121 | -------------------------------------------------------------------------------- /src/iolink_ds.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | /** 17 | * @file 18 | * @brief Data storage layer 19 | * 20 | */ 21 | 22 | #ifndef IOLINK_DS_H 23 | #define IOLINK_DS_H 24 | 25 | #include "iolink_types.h" 26 | #include "iolink_main.h" 27 | 28 | #include "sys/osal_cc.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | #define IOLINK_DS_MAX_SIZE 2048 35 | 36 | #define DS_STATE_PROPERTY_STATE_MASK (3 << 1) 37 | #define DS_STATE_PROPERTY_STATE_UPLOAD (1 << 1) 38 | #define DS_STATE_PROPERTY_STATE_DOWNLOAD (2 << 1) 39 | #define DS_STATE_PROPERTY_STATE_LOCKED (3 << 1) 40 | #define DS_STATE_PROPERTY_UPLOAD_REQ BIT (7) 41 | 42 | typedef enum iolink_ds_command 43 | { 44 | DS_CMD_RES = 0x00, 45 | DS_CMD_UL_START = 0x01, 46 | DS_CMD_UL_END = 0x02, 47 | DS_CMD_DL_START = 0x03, 48 | DS_CMD_DL_END = 0x04, 49 | DS_CMD_BREAK = 0x05, 50 | } iolink_ds_command_t; 51 | 52 | typedef enum iolink_ds_state 53 | { 54 | DS_STATE_CheckActivationState = 0, 55 | DS_STATE_WaitingOnDSActivity, 56 | DS_STATE_Off, 57 | /* UploadeDownload_2 */ 58 | DS_STATE_CheckIdentity, 59 | DS_STATE_CheckMemSize, 60 | DS_STATE_CheckUpload, 61 | DS_STATE_CheckDSValidity, 62 | DS_STATE_CheckChecksum, 63 | DS_STATE_DS_Ready, 64 | DS_STATE_DS_Fault, 65 | /* Upload_7 */ 66 | DS_STATE_Decompose_IL, 67 | DS_STATE_ReadParameter, 68 | DS_STATE_StoreDataSet, 69 | DS_STATE_UploadFault, 70 | /* Download_10 */ 71 | DS_STATE_Decompose_Set, 72 | DS_STATE_Write_Parameter, 73 | DS_STATE_Download_Done, 74 | DS_STATE_Download_Fault, 75 | DS_STATE_LAST, 76 | } iolink_ds_state_t; 77 | 78 | CC_PACKED_BEGIN 79 | typedef struct CC_PACKED ds_index_list_entry 80 | { 81 | uint16_t index; 82 | uint8_t subindex; 83 | } ds_index_list_entry_t; 84 | CC_PACKED_END 85 | 86 | typedef struct iolink_ds_port 87 | { 88 | iolink_ds_state_t state; 89 | iolink_ds_fault_t fault; 90 | bool ds_enable; 91 | bool ds_upload; 92 | bool ds_download; 93 | 94 | struct 95 | { 96 | uint32_t cs; 97 | uint32_t size; 98 | uint32_t size_max; 99 | uint8_t state_property; 100 | } device_ds; 101 | struct 102 | { 103 | uint32_t cs; 104 | uint16_t vid; 105 | uint32_t did; 106 | uint16_t fid; 107 | bool valid; 108 | uint32_t size; 109 | uint32_t size_max; 110 | 111 | uint16_t pos; /* Position in data */ 112 | uint8_t data[IOLINK_DS_MAX_SIZE]; 113 | } master_ds; 114 | struct 115 | { 116 | uint8_t count; 117 | uint8_t pos; /* Position in entries */ 118 | ds_index_list_entry_t entries[70]; 119 | } index_list; 120 | 121 | iolink_ds_command_t command; 122 | uint16_t current_index; 123 | uint8_t current_subindex; 124 | 125 | struct 126 | { 127 | uint16_t vendorid; 128 | uint32_t deviceid; 129 | } pending_id; 130 | } iolink_ds_port_t; 131 | 132 | /** 133 | * DS state-machine events. 134 | * 135 | * DS events trigger state transitions in the DS FSM. 136 | */ 137 | typedef enum iolink_fsm_ds_event 138 | { 139 | DS_EVENT_NONE, 140 | DS_EVENT_ENABLE, /* T1 */ 141 | DS_EVENT_STARTUP, /* T2 or T14 */ 142 | DS_EVENT_READY, /* T25, T26 or T27; + T3 */ 143 | DS_EVENT_UPLOAD, /* T4 or T13 */ 144 | DS_EVENT_FAULT_DONE, /* T5 */ 145 | DS_EVENT_FAULT_ID, /* T15 */ 146 | DS_EVENT_FAULT_SIZE, /* T17 */ 147 | DS_EVENT_FAULT_UL, /* T23 */ 148 | DS_EVENT_FAULT_DL, /* T28 */ 149 | DS_EVENT_FAULT_LOCK, /* T29 */ 150 | DS_EVENT_ENABLE_COM, /* T6 */ 151 | DS_EVENT_CLR_DISA, /* T7 */ 152 | DS_EVENT_ENABLE_NO_C, /* T8 */ 153 | DS_EVENT_DELETE, /* T9 + T10 */ 154 | DS_EVENT_CLEAR, /* T11 */ 155 | DS_EVENT_DISABLE, /* T12 */ 156 | DS_EVENT_PASSED, /* T16, T18 or T22 */ 157 | DS_EVENT_DO_UPLOAD, /* T19 or T21 */ 158 | DS_EVENT_NO_UPLOAD, /* T20 or T43 */ 159 | DS_EVENT_DOWNLOAD, /* T24 */ 160 | 161 | DS_EVENT_MORE_DATA, /* T30 or T37 */ 162 | DS_EVENT_READ_DONE, /* T31 */ 163 | DS_EVENT_STORE_DATA, /* T35 */ 164 | DS_EVENT_UL_DONE, /* T26 */ 165 | DS_EVENT_DEV_ERR, /* T32 or T39 */ 166 | DS_EVENT_COM_ERR, /* T33, T34, T36, T40 or T42 */ 167 | 168 | DS_EVENT_WR_DONE, /* T38 */ 169 | DS_EVENT_DL_DONE, /* T41 */ 170 | DS_EVENT_INIT, /* Not in spec. */ 171 | DS_EVENT_LAST, 172 | } iolink_fsm_ds_event_t; 173 | 174 | void iolink_ds_init (iolink_port_t * port); 175 | 176 | iolink_error_t DS_Delete (iolink_port_t * port); 177 | 178 | iolink_error_t DS_Startup (iolink_port_t * port); 179 | 180 | iolink_error_t DS_Upload (iolink_port_t * port); 181 | 182 | iolink_error_t DS_Init ( 183 | iolink_port_t * port, // Not in spec 184 | const portconfiglist_t * cfg_list); 185 | 186 | bool DS_Chk_Cfg (iolink_port_t * port, const portconfiglist_t * cfg_list); 187 | 188 | iolink_error_t ds_SMI_ParServToDS_req ( 189 | iolink_port_t * port, 190 | iolink_arg_block_id_t exp_arg_block_id, 191 | uint16_t arg_block_len, 192 | arg_block_t * arg_block); 193 | 194 | iolink_error_t ds_SMI_DSToParServ_req ( 195 | iolink_port_t * port, 196 | iolink_arg_block_id_t exp_arg_block_id, 197 | uint16_t arg_block_len, 198 | arg_block_t * arg_block); 199 | #ifdef __cplusplus 200 | } 201 | #endif 202 | 203 | #endif /* IOLINK_DS_H */ 204 | -------------------------------------------------------------------------------- /src/iolink_max14819_pl.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | /** 17 | * @file 18 | * @brief Driver for the MAX14819 IO-Link chip 19 | * 20 | * The iolink_max14819 driver is used for communication with a MAX14819 21 | * IO-Link chip. The chip contains two IO-Link master transceivers with logic. 22 | * 23 | */ 24 | 25 | #ifndef IOLINK_MAX14819_PL_H 26 | #define IOLINK_MAX14819_PL_H 27 | 28 | #include 29 | // TODO 30 | #ifdef __rtk__ 31 | #include 32 | #include 33 | #endif /* __rtk__ */ 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | #define MAX14819_NUM_CHANNELS 2 40 | #define IOLINK14819_TX_MAX (66) 41 | 42 | #include 43 | #include 44 | 45 | /* Driver structure */ 46 | typedef struct iolink_14819_drv 47 | { 48 | /* Generic driver interface */ 49 | iolink_hw_drv_t drv; 50 | 51 | /* Private data */ 52 | void * fd_spi; 53 | uint8_t chip_address; 54 | 55 | uint32_t pl_flag; 56 | 57 | bool wurq_request[MAX14819_NUM_CHANNELS]; 58 | bool is_iolink[MAX14819_NUM_CHANNELS]; 59 | os_mutex_t * exclusive; 60 | 61 | os_event_t * dl_event[MAX14819_NUM_CHANNELS]; 62 | } iolink_14819_drv_t; 63 | 64 | #ifdef __cplusplus 65 | } 66 | #endif 67 | 68 | #endif /* IOLINK_MAX14819_PL_H */ 69 | -------------------------------------------------------------------------------- /src/iolink_ode.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifdef UNIT_TEST 17 | #include "mocks.h" 18 | #define AL_Read_req mock_AL_Read_req 19 | #define AL_Write_req mock_AL_Write_req 20 | #define iolink_post_job mock_iolink_post_job 21 | #define iolink_fetch_avail_job mock_iolink_fetch_avail_job 22 | #define iolink_fetch_avail_api_job mock_iolink_fetch_avail_api_job 23 | #endif /* UNIT_TEST */ 24 | 25 | #include "iolink_ode.h" 26 | #include "iolink_al.h" /* AL_Read_req AL_Write_req */ 27 | #include "iolink_main.h" /* iolink_fetch_avail_job, iolink_fetch_avail_api_job, iolink_post_job, iolink_get_portnumber */ 28 | 29 | #include "osal_log.h" 30 | 31 | #include 32 | 33 | /** 34 | * @file 35 | * @brief On-request Data Exchange layer 36 | * 37 | */ 38 | 39 | /** 40 | * ODE state-machine events. 41 | * 42 | * ODE events trigger state transitions in the FSM. 43 | */ 44 | typedef enum iolink_fsm_ode_event 45 | { 46 | ODE_EVENT_NONE = 0, 47 | ODE_EVENT_SMI_DEV_RW_1, /* T1 */ 48 | ODE_EVENT_OD_START, /* T2 */ 49 | ODE_EVENT_OD_STOP, /* T3 */ 50 | ODE_EVENT_SMI_DEV_RW_4, /* T4 */ 51 | ODE_EVENT_OD_BLOCK, /* T5 */ 52 | ODE_EVENT_SMI_DEV_RW_6, /* T6 */ 53 | ODE_EVENT_OD_UNBLOCK, /* T7 */ 54 | ODE_EVENT_LAST 55 | } iolink_fsm_ode_event_t; 56 | 57 | static const char * const iolink_ode_state_literals[] = { 58 | "Inactive", 59 | "ODactive", 60 | "ODblocked", 61 | }; 62 | 63 | static const char * const iolink_ode_event_literals[] = { 64 | "NONE", 65 | "SMI_DEV_RW_1", /* T1 */ 66 | "OD_START", /* T2 */ 67 | "OD_STOP", /* T3 */ 68 | "SMI_DEV_RW_4", /* T4 */ 69 | "OD_BLOCK", /* T5 */ 70 | "SMI_DEV_RW_6", /* T6 */ 71 | "OD_UNBLOCK", /* T7 */ 72 | }; 73 | 74 | typedef struct iolink_fsm_ode_transition 75 | { 76 | iolink_fsm_ode_event_t event; 77 | iolink_ode_state_t next_state; 78 | iolink_fsm_ode_event_t ( 79 | *action) (iolink_port_t * port, iolink_fsm_ode_event_t event); 80 | } iolink_fsm_ode_transition_t; 81 | 82 | typedef struct iolink_fsm_ode_state_transitions 83 | { 84 | const uint8_t number_of_trans; 85 | const iolink_fsm_ode_transition_t * transitions; 86 | } iolink_fsm_ode_state_transitions_t; 87 | 88 | /** 89 | * Trigger OD FSM state transition 90 | * 91 | * This function triggers an OD state transition according to the event. 92 | */ 93 | static void iolink_ode_event (iolink_port_t * port, iolink_fsm_ode_event_t event); 94 | 95 | /* Actions taken when transitioning to a new state. See iolink_ode_event(). */ 96 | static iolink_fsm_ode_event_t ode_smi_od_err ( 97 | iolink_port_t * port, 98 | iolink_fsm_ode_event_t event); 99 | static iolink_fsm_ode_event_t ode_od_start ( 100 | iolink_port_t * port, 101 | iolink_fsm_ode_event_t event); 102 | static iolink_fsm_ode_event_t ode_od_stop ( 103 | iolink_port_t * port, 104 | iolink_fsm_ode_event_t event); 105 | static iolink_fsm_ode_event_t ode_smi_rw ( 106 | iolink_port_t * port, 107 | iolink_fsm_ode_event_t event); 108 | static iolink_fsm_ode_event_t ode_smi_done ( 109 | iolink_port_t * port, 110 | iolink_fsm_ode_event_t event); 111 | static iolink_fsm_ode_event_t ode_wait ( 112 | iolink_port_t * port, 113 | iolink_fsm_ode_event_t event); 114 | 115 | /* Callback functions to run on main thread */ 116 | static void ode_read_cnf_cb (iolink_job_t * job); 117 | static void ode_write_cnf_cb (iolink_job_t * job); 118 | static void od_start_cb (iolink_job_t * job); 119 | static void od_stop_cb (iolink_job_t * job); 120 | static void SMI_rw_req_cb (iolink_job_t * job); 121 | 122 | /* Other functions */ 123 | static iolink_fsm_ode_event_t ode_AL_Read_req (iolink_port_t * port); 124 | static iolink_fsm_ode_event_t ode_AL_Write_req (iolink_port_t * port); 125 | static iolink_error_t ode_SMI_rw_req ( 126 | iolink_job_type_t job_type, 127 | iolink_port_t * port, 128 | iolink_arg_block_id_t exp_arg_block_id, 129 | uint16_t arg_block_len, 130 | arg_block_t * arg_block); 131 | static void ode_AL_Read_cnf ( 132 | iolink_port_t * port, 133 | uint8_t len, 134 | const uint8_t * data, 135 | iolink_smi_errortypes_t errortype); 136 | static void ode_AL_Write_cnf ( 137 | iolink_port_t * port, 138 | iolink_smi_errortypes_t errortype); 139 | static iolink_error_t ode_SMI_DeviceReadWrite_req ( 140 | iolink_port_t * port, 141 | iolink_arg_block_id_t exp_arg_block_id, 142 | uint16_t arg_block_len, 143 | arg_block_t * arg_block, 144 | iolink_arg_block_id_t od_or_void, 145 | iolink_job_type_t type); 146 | 147 | static iolink_fsm_ode_event_t ode_smi_od_err ( 148 | iolink_port_t * port, 149 | iolink_fsm_ode_event_t event) 150 | { 151 | iolink_smi_errortypes_t errortype = IOLINK_SMI_ERRORTYPE_NONE; 152 | iolink_ode_port_t * ode = iolink_get_ode_ctx (port); 153 | iolink_smi_service_req_t * smi_req = &ode->job_smi_req_busy->smi_req; 154 | iolink_arg_block_id_t ref_arg_block_id = 155 | smi_req->arg_block->id; 156 | 157 | /* SMI_ParamReadBatch and SMI_ParamWriteBatch are not supported */ 158 | if ( 159 | ref_arg_block_id == IOLINK_ARG_BLOCK_ID_PORT_INXDEX_LIST || 160 | ref_arg_block_id == IOLINK_ARG_BLOCK_ID_DEV_PAR_BAT) 161 | { 162 | errortype = IOLINK_SMI_ERRORTYPE_SERVICE_NOT_SUPPORTED; 163 | } 164 | else 165 | { 166 | switch (event) 167 | { 168 | case ODE_EVENT_SMI_DEV_RW_1: 169 | /* Access blocked (inactive) indicate IDX_NOTAVAIL */ 170 | errortype = IOLINK_SMI_ERRORTYPE_IDX_NOTAVAIL; 171 | break; 172 | case ODE_EVENT_SMI_DEV_RW_6: 173 | /* Access blocked (temporarily) inidcate SERVICE_TEMP_UNAVAILABLE */ 174 | errortype = IOLINK_SMI_ERRORTYPE_SERVICE_TEMP_UNAVAILABLE; 175 | break; 176 | default: 177 | CC_ASSERT (0); 178 | break; 179 | } 180 | } 181 | 182 | switch (ref_arg_block_id) 183 | { 184 | case IOLINK_ARG_BLOCK_ID_OD_RD: 185 | case IOLINK_ARG_BLOCK_ID_OD_WR: 186 | case IOLINK_ARG_BLOCK_ID_PORT_INXDEX_LIST: 187 | case IOLINK_ARG_BLOCK_ID_DEV_PAR_BAT: 188 | break; 189 | default: 190 | errortype = IOLINK_SMI_ERRORTYPE_ARGBLOCK_NOT_SUPPORTED; 191 | break; 192 | } 193 | 194 | iolink_smi_joberror_ind ( 195 | port, 196 | smi_req->exp_arg_block_id, 197 | ref_arg_block_id, 198 | errortype); 199 | 200 | return ODE_EVENT_NONE; 201 | } 202 | 203 | static iolink_fsm_ode_event_t ode_od_start ( 204 | iolink_port_t * port, 205 | iolink_fsm_ode_event_t event) 206 | { 207 | /* Wait for SMI calls */ 208 | return ODE_EVENT_NONE; 209 | } 210 | 211 | static iolink_fsm_ode_event_t ode_od_stop ( 212 | iolink_port_t * port, 213 | iolink_fsm_ode_event_t event) 214 | { 215 | return ODE_EVENT_NONE; 216 | } 217 | 218 | static iolink_fsm_ode_event_t ode_AL_Read_req (iolink_port_t * port) 219 | { 220 | iolink_ode_port_t * ode = iolink_get_ode_ctx (port); 221 | iolink_smi_service_req_t * smi_req = &ode->smi_req; 222 | arg_block_od_t * arg_block_od = (arg_block_od_t *)smi_req->arg_block; 223 | 224 | AL_Read_req (port, arg_block_od->index, arg_block_od->subindex, ode_AL_Read_cnf); 225 | 226 | return ODE_EVENT_OD_BLOCK; 227 | } 228 | 229 | static iolink_fsm_ode_event_t ode_AL_Write_req (iolink_port_t * port) 230 | { 231 | iolink_ode_port_t * ode = iolink_get_ode_ctx (port); 232 | iolink_smi_service_req_t * smi_req = &ode->smi_req; 233 | uint8_t arg_block_len = smi_req->arg_block_len; 234 | uint8_t arg_block_data_len = 0; 235 | 236 | arg_block_od_t * arg_block_od = (arg_block_od_t *)smi_req->arg_block; 237 | 238 | if (arg_block_len >= sizeof (arg_block_od_t)) 239 | { 240 | arg_block_data_len = arg_block_len - sizeof (arg_block_od_t); 241 | } 242 | 243 | AL_Write_req ( 244 | port, 245 | arg_block_od->index, 246 | arg_block_od->subindex, 247 | arg_block_data_len, 248 | arg_block_od->data, 249 | ode_AL_Write_cnf); 250 | 251 | return ODE_EVENT_OD_BLOCK; 252 | } 253 | 254 | static iolink_fsm_ode_event_t ode_smi_rw ( 255 | iolink_port_t * port, 256 | iolink_fsm_ode_event_t event) 257 | { 258 | iolink_fsm_ode_event_t res_event = ODE_EVENT_NONE; 259 | iolink_ode_port_t * ode = iolink_get_ode_ctx (port); 260 | iolink_smi_service_req_t * smi_req = &ode->smi_req; 261 | iolink_arg_block_id_t exp_arg_block_id = smi_req->exp_arg_block_id; 262 | iolink_arg_block_id_t ref_arg_block_id = 263 | smi_req->arg_block->id; 264 | 265 | if ( 266 | (exp_arg_block_id == IOLINK_ARG_BLOCK_ID_OD_RD) && 267 | (ref_arg_block_id == IOLINK_ARG_BLOCK_ID_OD_RD)) 268 | { 269 | res_event = ode_AL_Read_req (port); 270 | } 271 | else if ( 272 | (exp_arg_block_id == IOLINK_ARG_BLOCK_ID_VOID_BLOCK) && 273 | (ref_arg_block_id == IOLINK_ARG_BLOCK_ID_OD_WR)) 274 | { 275 | res_event = ode_AL_Write_req (port); 276 | } 277 | else 278 | { 279 | iolink_smi_errortypes_t errortype = 280 | IOLINK_SMI_ERRORTYPE_ARGBLOCK_NOT_SUPPORTED; 281 | 282 | /* SMI_ParamReadBatch and SMI_ParamWriteBatch are not supported */ 283 | if ( 284 | (ref_arg_block_id == IOLINK_ARG_BLOCK_ID_PORT_INXDEX_LIST) || 285 | (ref_arg_block_id == IOLINK_ARG_BLOCK_ID_DEV_PAR_BAT)) 286 | { 287 | errortype = IOLINK_SMI_ERRORTYPE_SERVICE_NOT_SUPPORTED; 288 | } 289 | 290 | iolink_smi_joberror_ind (port, exp_arg_block_id, ref_arg_block_id, errortype); 291 | } 292 | 293 | return res_event; 294 | } 295 | 296 | static iolink_fsm_ode_event_t ode_smi_done ( 297 | iolink_port_t * port, 298 | iolink_fsm_ode_event_t event) 299 | { 300 | iolink_ode_port_t * ode = iolink_get_ode_ctx (port); 301 | iolink_smi_service_req_t * smi_req = &ode->smi_req; 302 | iolink_arg_block_id_t exp_arg_block_id = smi_req->exp_arg_block_id; 303 | iolink_arg_block_id_t ref_arg_block_id = 304 | smi_req->arg_block->id; 305 | 306 | if (smi_req->result != IOLINK_SMI_ERRORTYPE_NONE) 307 | { 308 | iolink_smi_joberror_ind ( 309 | port, 310 | exp_arg_block_id, 311 | ref_arg_block_id, 312 | smi_req->result); 313 | } 314 | else 315 | { 316 | if ( 317 | (exp_arg_block_id == IOLINK_ARG_BLOCK_ID_OD_RD) && 318 | (ref_arg_block_id == IOLINK_ARG_BLOCK_ID_OD_RD)) 319 | { 320 | iolink_smi_cnf ( 321 | port, 322 | ref_arg_block_id, 323 | smi_req->arg_block_len, 324 | smi_req->arg_block); 325 | } 326 | else if ( 327 | (exp_arg_block_id == IOLINK_ARG_BLOCK_ID_VOID_BLOCK) && 328 | (ref_arg_block_id == IOLINK_ARG_BLOCK_ID_OD_WR)) 329 | { 330 | iolink_smi_voidblock_cnf (port, ref_arg_block_id); 331 | } 332 | else 333 | { 334 | CC_ASSERT (0); 335 | } 336 | } 337 | 338 | return ODE_EVENT_NONE; 339 | } 340 | 341 | static iolink_fsm_ode_event_t ode_wait ( 342 | iolink_port_t * port, 343 | iolink_fsm_ode_event_t event) 344 | { 345 | /* Wait for AL_{Read,Write}_cnf */ 346 | return ODE_EVENT_NONE; 347 | } 348 | 349 | /* ODE FSM state transitions, IO-Link Interface Spec v1.1.3 Chapter 11.5 */ 350 | /* since we iterate through the list on events put the most likely in the top of 351 | * the list. */ 352 | static const iolink_fsm_ode_transition_t ode_trans_s0[] = { 353 | /* Inactive_0 */ 354 | {ODE_EVENT_OD_START, ODE_STATE_ODactive, ode_od_start}, /* T2 */ 355 | {ODE_EVENT_SMI_DEV_RW_1, ODE_STATE_Inactive, ode_smi_od_err}, /* T1 */ 356 | 357 | {ODE_EVENT_OD_STOP, ODE_STATE_Inactive, ode_od_stop}, /* NOTE: Not in spec */ 358 | }; 359 | 360 | static const iolink_fsm_ode_transition_t ode_trans_s1[] = { 361 | /* ODactive_1 */ 362 | {ODE_EVENT_SMI_DEV_RW_4, ODE_STATE_ODactive, ode_smi_rw}, /* T4 */ 363 | {ODE_EVENT_OD_BLOCK, ODE_STATE_ODblocked, ode_wait}, /* T5 */ 364 | {ODE_EVENT_OD_STOP, ODE_STATE_Inactive, ode_od_stop}, /* T3 */ 365 | }; 366 | 367 | static const iolink_fsm_ode_transition_t ode_trans_s2[] = { 368 | /* ODblocked_2 */ 369 | {ODE_EVENT_OD_UNBLOCK, ODE_STATE_ODactive, ode_smi_done}, /* T7 */ 370 | {ODE_EVENT_SMI_DEV_RW_6, ODE_STATE_ODblocked, ode_smi_od_err}, /* T6 */ 371 | 372 | {ODE_EVENT_OD_STOP, ODE_STATE_Inactive, ode_od_stop}, /* Not part of FSM */ 373 | }; 374 | 375 | /* The index is the state in this array */ 376 | static const iolink_fsm_ode_state_transitions_t iolink_ode_fsm[] = { 377 | {/* Inactive_0 */ 378 | .number_of_trans = NELEMENTS (ode_trans_s0), 379 | .transitions = ode_trans_s0}, 380 | {/* ODactive_1 */ 381 | .number_of_trans = NELEMENTS (ode_trans_s1), 382 | .transitions = ode_trans_s1}, 383 | {/* ODblocked_2 */ 384 | .number_of_trans = NELEMENTS (ode_trans_s2), 385 | .transitions = ode_trans_s2}, 386 | }; 387 | 388 | static void iolink_ode_event (iolink_port_t * port, iolink_fsm_ode_event_t event) 389 | { 390 | do 391 | { 392 | int i; 393 | iolink_ode_port_t * ode = iolink_get_ode_ctx (port); 394 | iolink_ode_state_t previous = ode->state; 395 | const iolink_fsm_ode_transition_t * next_trans = NULL; 396 | 397 | for (i = 0; i < iolink_ode_fsm[previous].number_of_trans; i++) 398 | { 399 | if (iolink_ode_fsm[previous].transitions[i].event == event) 400 | { 401 | if (!next_trans) 402 | { 403 | next_trans = &iolink_ode_fsm[previous].transitions[i]; 404 | break; 405 | } 406 | } 407 | } 408 | if (!next_trans) 409 | { 410 | LOG_ERROR ( 411 | IOLINK_ODE_LOG, 412 | "ODE (%u): next_trans == NULL: state %s - event %s\n", 413 | iolink_get_portnumber (port), 414 | iolink_ode_state_literals[previous], 415 | iolink_ode_event_literals[event]); 416 | return; 417 | } 418 | 419 | /* Transition to next state */ 420 | ode->state = next_trans->next_state; 421 | 422 | LOG_DEBUG ( 423 | IOLINK_ODE_LOG, 424 | "ODE (%u): event: %s, state transition: %s -> %s\n", 425 | iolink_get_portnumber (port), 426 | iolink_ode_event_literals[event], 427 | iolink_ode_state_literals[previous], 428 | iolink_ode_state_literals[ode->state]); 429 | 430 | event = next_trans->action (port, event); 431 | } while (event != ODE_EVENT_NONE); 432 | } 433 | 434 | /* AL job callback functions */ 435 | static iolink_error_t ode_SMI_rw_req ( 436 | iolink_job_type_t job_type, 437 | iolink_port_t * port, 438 | iolink_arg_block_id_t exp_arg_block_id, 439 | uint16_t arg_block_len, 440 | arg_block_t * arg_block) 441 | { 442 | iolink_job_t * job = iolink_fetch_avail_api_job (port); 443 | job->smi_req.exp_arg_block_id = exp_arg_block_id; 444 | job->smi_req.arg_block_len = arg_block_len; 445 | job->smi_req.arg_block = arg_block; 446 | 447 | iolink_post_job_with_type_and_callback (port, job, job_type, SMI_rw_req_cb); 448 | 449 | return IOLINK_ERROR_NONE; 450 | } 451 | 452 | static void ode_read_cnf_cb (iolink_job_t * job) 453 | { 454 | iolink_port_t * port = job->port; 455 | iolink_ode_port_t * ode = iolink_get_ode_ctx (port); 456 | iolink_smi_service_req_t * smi_req = &ode->smi_req; 457 | 458 | smi_req->result = job->al_read_cnf.errortype; 459 | if (smi_req->result == IOLINK_SMI_ERRORTYPE_NONE) 460 | { 461 | uint8_t arg_block_len = smi_req->arg_block_len; 462 | uint8_t arg_block_data_len = arg_block_len - sizeof (arg_block_od_t); 463 | uint8_t len = job->al_read_cnf.data_len; 464 | 465 | if (arg_block_data_len >= len) 466 | { 467 | arg_block_od_t * arg_block_od = (arg_block_od_t *)smi_req->arg_block; 468 | 469 | memset (arg_block_od->data, 0, arg_block_data_len); 470 | memcpy (arg_block_od->data, job->al_read_cnf.data, len); 471 | 472 | arg_block_len = len + sizeof (arg_block_od_t); 473 | smi_req->arg_block_len = arg_block_len; 474 | } 475 | else 476 | { 477 | smi_req->result = IOLINK_SMI_ERRORTYPE_ARGBLOCK_LENGTH_INVALID; 478 | } 479 | } 480 | 481 | iolink_ode_event (port, ODE_EVENT_OD_UNBLOCK); 482 | } 483 | 484 | static void ode_AL_Read_cnf ( 485 | iolink_port_t * port, 486 | uint8_t len, 487 | const uint8_t * data, 488 | iolink_smi_errortypes_t errortype) 489 | { 490 | iolink_job_t * job = iolink_fetch_avail_job (port); 491 | job->al_read_cnf.data = data; 492 | job->al_read_cnf.data_len = len; 493 | job->al_read_cnf.errortype = errortype; 494 | 495 | iolink_post_job_with_type_and_callback ( 496 | port, 497 | job, 498 | IOLINK_JOB_AL_READ_CNF, 499 | ode_read_cnf_cb); 500 | } 501 | 502 | static void ode_write_cnf_cb (iolink_job_t * job) 503 | { 504 | iolink_port_t * port = job->port; 505 | iolink_ode_port_t * ode = iolink_get_ode_ctx (port); 506 | iolink_smi_service_req_t * smi_req = &ode->smi_req; 507 | 508 | if (job->al_write_cnf.errortype != IOLINK_SMI_ERRORTYPE_NONE) 509 | { 510 | smi_req->result = job->al_write_cnf.errortype; 511 | } 512 | 513 | iolink_ode_event (port, ODE_EVENT_OD_UNBLOCK); 514 | } 515 | 516 | static void ode_AL_Write_cnf (iolink_port_t * port, iolink_smi_errortypes_t errortype) 517 | { 518 | iolink_job_t * job = iolink_fetch_avail_job (port); 519 | job->al_write_cnf.errortype = errortype; 520 | 521 | iolink_post_job_with_type_and_callback ( 522 | port, 523 | job, 524 | IOLINK_JOB_AL_WRITE_CNF, 525 | ode_write_cnf_cb); 526 | } 527 | 528 | static void od_start_cb (iolink_job_t * job) 529 | { 530 | iolink_ode_event (job->port, ODE_EVENT_OD_START); 531 | } 532 | 533 | static void od_stop_cb (iolink_job_t * job) 534 | { 535 | iolink_ode_event (job->port, ODE_EVENT_OD_STOP); 536 | } 537 | 538 | static void SMI_rw_req_cb (iolink_job_t * job) 539 | { 540 | iolink_fsm_ode_event_t event = ODE_EVENT_NONE; 541 | iolink_port_t * port = job->port; 542 | iolink_ode_port_t * ode = iolink_get_ode_ctx (port); 543 | iolink_smi_service_req_t * smi_req = &ode->smi_req; 544 | 545 | switch (ode->state) 546 | { 547 | case ODE_STATE_ODactive: 548 | smi_req->result = IOLINK_SMI_ERRORTYPE_NONE; 549 | smi_req->exp_arg_block_id = job->smi_req.exp_arg_block_id; 550 | smi_req->arg_block_len = job->smi_req.arg_block_len; 551 | smi_req->arg_block = job->smi_req.arg_block; 552 | event = ODE_EVENT_SMI_DEV_RW_4; 553 | break; 554 | case ODE_STATE_Inactive: 555 | case ODE_STATE_ODblocked: 556 | /* Store the job, for a negative response */ 557 | ode->job_smi_req_busy = job; 558 | event = (ode->state == ODE_STATE_Inactive) ? ODE_EVENT_SMI_DEV_RW_1 559 | : ODE_EVENT_SMI_DEV_RW_6; 560 | break; 561 | default: 562 | CC_ASSERT (0); 563 | break; 564 | } 565 | 566 | iolink_ode_event (port, event); 567 | } 568 | 569 | /* Stack internal API */ 570 | void iolink_ode_init (iolink_port_t * port) 571 | { 572 | iolink_ode_port_t * ode = iolink_get_ode_ctx (port); 573 | 574 | ode->state = ODE_STATE_Inactive; 575 | } 576 | 577 | iolink_error_t OD_Start (iolink_port_t * port) 578 | { 579 | iolink_job_t * job = iolink_fetch_avail_job (port); 580 | 581 | iolink_post_job_with_type_and_callback ( 582 | port, 583 | job, 584 | IOLINK_JOB_OD_START, 585 | od_start_cb); 586 | 587 | return IOLINK_ERROR_NONE; 588 | } 589 | 590 | iolink_error_t OD_Stop (iolink_port_t * port) 591 | { 592 | iolink_job_t * job = iolink_fetch_avail_job (port); 593 | 594 | iolink_post_job_with_type_and_callback ( 595 | port, 596 | job, 597 | IOLINK_JOB_OD_STOP, 598 | od_stop_cb); 599 | 600 | return IOLINK_ERROR_NONE; 601 | } 602 | 603 | static iolink_error_t ode_SMI_DeviceReadWrite_req ( 604 | iolink_port_t * port, 605 | iolink_arg_block_id_t exp_arg_block_id, 606 | uint16_t arg_block_len, 607 | arg_block_t * arg_block, 608 | iolink_arg_block_id_t od_or_void, 609 | iolink_job_type_t type) 610 | { 611 | if (exp_arg_block_id != od_or_void) 612 | { 613 | return IOLINK_ERROR_PARAMETER_CONFLICT; 614 | } 615 | 616 | return ode_SMI_rw_req (type, port, exp_arg_block_id, arg_block_len, arg_block); 617 | } 618 | 619 | iolink_error_t ode_SMI_DeviceRead_req ( 620 | iolink_port_t * port, 621 | iolink_arg_block_id_t exp_arg_block_id, 622 | uint16_t arg_block_len, 623 | arg_block_t * arg_block) 624 | { 625 | return ode_SMI_DeviceReadWrite_req ( 626 | port, 627 | exp_arg_block_id, 628 | arg_block_len, 629 | arg_block, 630 | IOLINK_ARG_BLOCK_ID_OD_RD, 631 | IOLINK_JOB_SMI_DEVICE_READ); 632 | } 633 | 634 | iolink_error_t ode_SMI_DeviceWrite_req ( 635 | iolink_port_t * port, 636 | iolink_arg_block_id_t exp_arg_block_id, 637 | uint16_t arg_block_len, 638 | arg_block_t * arg_block) 639 | { 640 | return ode_SMI_DeviceReadWrite_req ( 641 | port, 642 | exp_arg_block_id, 643 | arg_block_len, 644 | arg_block, 645 | IOLINK_ARG_BLOCK_ID_VOID_BLOCK, 646 | IOLINK_JOB_SMI_DEVICE_WRITE); 647 | } 648 | 649 | iolink_error_t ode_SMI_ParamReadBatch_req ( 650 | iolink_port_t * port, 651 | iolink_arg_block_id_t exp_arg_block_id, 652 | uint16_t arg_block_len, 653 | arg_block_t * arg_block) 654 | { 655 | return ode_SMI_rw_req ( 656 | IOLINK_JOB_SMI_PARAM_READ, 657 | port, 658 | exp_arg_block_id, 659 | arg_block_len, 660 | arg_block); 661 | } 662 | 663 | iolink_error_t ode_SMI_ParamWriteBatch_req ( 664 | iolink_port_t * port, 665 | iolink_arg_block_id_t exp_arg_block_id, 666 | uint16_t arg_block_len, 667 | arg_block_t * arg_block) 668 | { 669 | return ode_SMI_rw_req ( 670 | IOLINK_JOB_SMI_PARAM_WRITE, 671 | port, 672 | exp_arg_block_id, 673 | arg_block_len, 674 | arg_block); 675 | } 676 | -------------------------------------------------------------------------------- /src/iolink_ode.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | /** 17 | * @file 18 | * @brief On-request Data Exchange layer 19 | * 20 | */ 21 | 22 | #ifndef IOLINK_ODE_H 23 | #define IOLINK_ODE_H 24 | 25 | #include "iolink_main.h" /* iolink_job_t */ 26 | #include "iolink_types.h" 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | typedef enum iolink_ode_state 33 | { 34 | ODE_STATE_Inactive = 0, 35 | ODE_STATE_ODactive, 36 | ODE_STATE_ODblocked, 37 | ODE_STATE_LAST 38 | } iolink_ode_state_t; 39 | 40 | typedef struct iolink_ode_port 41 | { 42 | iolink_ode_state_t state; 43 | iolink_smi_service_req_t smi_req; 44 | iolink_job_t * job_smi_req_busy; 45 | } iolink_ode_port_t; 46 | 47 | void iolink_ode_init (iolink_port_t * port); 48 | 49 | iolink_error_t OD_Start (iolink_port_t * port); 50 | iolink_error_t OD_Stop (iolink_port_t * port); 51 | 52 | iolink_error_t ode_SMI_DeviceRead_req ( 53 | iolink_port_t * port, 54 | iolink_arg_block_id_t exp_arg_block_id, 55 | uint16_t arg_block_len, 56 | arg_block_t * arg_block); 57 | 58 | iolink_error_t ode_SMI_DeviceWrite_req ( 59 | iolink_port_t * port, 60 | iolink_arg_block_id_t exp_arg_block_id, 61 | uint16_t arg_block_len, 62 | arg_block_t * arg_block); 63 | 64 | iolink_error_t ode_SMI_ParamReadBatch_req ( 65 | iolink_port_t * port, 66 | iolink_arg_block_id_t exp_arg_block_id, 67 | uint16_t arg_block_len, 68 | arg_block_t * arg_block); 69 | 70 | iolink_error_t ode_SMI_ParamWriteBatch_req ( 71 | iolink_port_t * port, 72 | iolink_arg_block_id_t exp_arg_block_id, 73 | uint16_t arg_block_len, 74 | arg_block_t * arg_block); 75 | #ifdef __cplusplus 76 | } 77 | #endif 78 | 79 | #endif /* IOLINK_ODE_H */ 80 | -------------------------------------------------------------------------------- /src/iolink_pde.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifdef UNIT_TEST 17 | #include "mocks.h" 18 | #define AL_Control_req mock_AL_Control_req 19 | #define AL_GetInput_req mock_AL_GetInput_req 20 | #define AL_GetInputOutput_req mock_AL_GetInputOutput_req 21 | #define AL_SetOutput_req mock_AL_SetOutput_req 22 | #define iolink_post_job mock_iolink_post_job 23 | #define iolink_fetch_avail_job mock_iolink_fetch_avail_job 24 | #define iolink_fetch_avail_api_job mock_iolink_fetch_avail_api_job 25 | #endif /* UNIT_TEST */ 26 | 27 | #include "iolink_pde.h" 28 | #include "iolink_al.h" /* AL_Control_req, AL_GetInput_req, AL_GetInputOutput_req, AL_SetOutput_req */ 29 | #include "iolink_main.h" /* iolink_fetch_avail_job, iolink_fetch_avail_api_job, iolink_post_job, iolink_get_portnumber, iolink_get_port_info */ 30 | 31 | #include "osal_log.h" 32 | 33 | #include 34 | 35 | /** 36 | * @file 37 | * @brief Process Data Exchange layer 38 | * 39 | */ 40 | 41 | static iolink_port_qualifier_info_t pde_port_quality_to_qualifier ( 42 | iolink_port_t * port); 43 | static iolink_port_status_info_t pde_get_port_status_info ( 44 | iolink_port_t * port); 45 | static iolink_error_t pde_check_smi_req ( 46 | iolink_port_t * port, 47 | iolink_arg_block_id_t exp_arg_block_id, 48 | iolink_arg_block_id_t ref_arg_block_id, 49 | uint8_t arg_block_len, 50 | iolink_arg_block_id_t exp_exp_arg_block_id, 51 | iolink_arg_block_id_t exp_ref_arg_block_id, 52 | uint8_t exp_arg_block_len_min, 53 | uint8_t exp_arg_block_len_max); 54 | 55 | static iolink_port_qualifier_info_t pde_port_quality_to_qualifier ( 56 | iolink_port_t * port) 57 | { 58 | iolink_port_info_t * port_info = iolink_get_port_info (port); 59 | 60 | return (port_info->port_quality_info & IOLINK_PORT_QUALITY_INFO_INVALID) 61 | ? IOLINK_PORT_QUALIFIER_INFO_PQ_INVALID 62 | : IOLINK_PORT_QUALIFIER_INFO_PQ_VALID; 63 | } 64 | 65 | static iolink_port_status_info_t pde_get_port_status_info ( 66 | iolink_port_t * port) 67 | { 68 | iolink_port_info_t * port_info = iolink_get_port_info (port); 69 | 70 | return port_info->port_status_info; 71 | } 72 | 73 | static iolink_error_t pde_check_smi_req ( 74 | iolink_port_t * port, 75 | iolink_arg_block_id_t exp_arg_block_id, 76 | iolink_arg_block_id_t ref_arg_block_id, 77 | uint8_t arg_block_len, 78 | iolink_arg_block_id_t exp_exp_arg_block_id, 79 | iolink_arg_block_id_t exp_ref_arg_block_id, 80 | uint8_t exp_arg_block_len_min, 81 | uint8_t exp_arg_block_len_max) 82 | { 83 | iolink_smi_errortypes_t smi_error = IOLINK_SMI_ERRORTYPE_NONE; 84 | iolink_error_t error = IOLINK_ERROR_NONE; 85 | 86 | if (exp_ref_arg_block_id != ref_arg_block_id) 87 | { 88 | smi_error = IOLINK_SMI_ERRORTYPE_ARGBLOCK_NOT_SUPPORTED; 89 | } 90 | else if ((arg_block_len < exp_arg_block_len_min) || (arg_block_len > exp_arg_block_len_max)) 91 | { 92 | smi_error = IOLINK_SMI_ERRORTYPE_ARGBLOCK_LENGTH_INVALID; 93 | } 94 | else if (exp_exp_arg_block_id != exp_arg_block_id) 95 | { 96 | smi_error = IOLINK_SMI_ERRORTYPE_ARGBLOCK_INCONSISTENT; 97 | } 98 | 99 | if (smi_error != IOLINK_SMI_ERRORTYPE_NONE) 100 | { 101 | iolink_smi_joberror_ind (port, exp_arg_block_id, ref_arg_block_id, smi_error); 102 | error = IOLINK_ERROR_PARAMETER_CONFLICT; 103 | } 104 | else 105 | { 106 | iolink_pde_port_t * pde = iolink_get_pde_ctx (port); 107 | if (pde->state == PD_STATE_Inactive) 108 | { 109 | smi_error = IOLINK_SMI_ERRORTYPE_DEV_NOT_IN_OPERATE; // TODO is this 110 | // correct? 111 | iolink_smi_joberror_ind ( 112 | port, 113 | exp_arg_block_id, 114 | ref_arg_block_id, 115 | smi_error); 116 | error = IOLINK_ERROR_STATE_INVALID; // TODO what to return? 117 | } 118 | } 119 | 120 | return error; 121 | } 122 | 123 | /* Stack internal API */ 124 | void iolink_pde_init (iolink_port_t * port) 125 | { 126 | iolink_pde_port_t * pde = iolink_get_pde_ctx (port); 127 | 128 | pde->output_enable = false; 129 | pde->state = PD_STATE_Inactive; 130 | } 131 | 132 | iolink_error_t PD_Start (iolink_port_t * port) 133 | { 134 | iolink_pde_port_t * pde = iolink_get_pde_ctx (port); 135 | 136 | pde->state = PD_STATE_PDactive; 137 | 138 | return IOLINK_ERROR_NONE; 139 | } 140 | 141 | iolink_error_t PD_Stop (iolink_port_t * port) 142 | { 143 | iolink_pde_init (port); 144 | 145 | return IOLINK_ERROR_NONE; 146 | } 147 | 148 | iolink_error_t AL_NewInput_ind (iolink_port_t * port) 149 | { 150 | return IOLINK_ERROR_NONE; 151 | } 152 | 153 | iolink_error_t AL_Control_ind (iolink_port_t * port, iolink_controlcode_t controlcode) 154 | { 155 | iolink_port_info_t * port_info = iolink_get_port_info (port); 156 | 157 | switch (controlcode) 158 | { 159 | case IOLINK_CONTROLCODE_VALID: 160 | port_info->port_quality_info &= ~IOLINK_PORT_QUALITY_INFO_INVALID; 161 | break; 162 | case IOLINK_CONTROLCODE_INVALID: 163 | port_info->port_quality_info |= IOLINK_PORT_QUALITY_INFO_INVALID; 164 | break; 165 | case IOLINK_CONTROLCODE_PDOUTVALID: 166 | port_info->port_quality_info &= ~IOLINK_PORT_QUALITY_INFO_PDO_INVALID; 167 | break; 168 | case IOLINK_CONTROLCODE_PDOUTINVALID: 169 | port_info->port_quality_info |= IOLINK_PORT_QUALITY_INFO_PDO_INVALID; 170 | break; 171 | default: 172 | CC_ASSERT (0); 173 | break; 174 | } 175 | 176 | return IOLINK_ERROR_NONE; 177 | } 178 | 179 | iolink_error_t pde_SMI_PDIn_req ( 180 | iolink_port_t * port, 181 | iolink_arg_block_id_t exp_arg_block_id, 182 | uint16_t arg_block_len, 183 | arg_block_t * arg_block) 184 | { 185 | iolink_arg_block_id_t ref_arg_block_id = arg_block->id; 186 | arg_block_pdin_t arg_block_pdin; 187 | memset (&arg_block_pdin, 0, sizeof (arg_block_pdin_t)); 188 | uint8_t pdin_len = 1; 189 | iolink_error_t error = pde_check_smi_req ( 190 | port, 191 | exp_arg_block_id, 192 | ref_arg_block_id, 193 | arg_block_len, 194 | IOLINK_ARG_BLOCK_ID_PD_IN, 195 | IOLINK_ARG_BLOCK_ID_VOID_BLOCK, 196 | sizeof (arg_block_void_t), 197 | sizeof (arg_block_void_t)); 198 | 199 | if (error != IOLINK_ERROR_NONE) 200 | { 201 | iolink_port_status_info_t port_status_info = 202 | pde_get_port_status_info (port); 203 | 204 | if ( 205 | (error == IOLINK_ERROR_STATE_INVALID) && 206 | ((port_status_info == IOLINK_PORT_STATUS_INFO_DI) || 207 | (port_status_info == IOLINK_PORT_STATUS_INFO_DEACTIVATED))) 208 | { 209 | // TODO Copy result from Get signal status DI_C/Q 210 | arg_block_pdin.data[0] = 1; 211 | error = IOLINK_ERROR_NONE; 212 | } 213 | } 214 | else 215 | { 216 | arg_block_pdin.h.port_qualifier_info = pde_port_quality_to_qualifier (port); 217 | error = AL_GetInput_req (port, &pdin_len, arg_block_pdin.data); 218 | 219 | if (error != IOLINK_ERROR_NONE) 220 | { 221 | iolink_smi_errortypes_t smi_error = 222 | IOLINK_SMI_ERRORTYPE_DEV_NOT_ACCESSIBLE; 223 | iolink_smi_joberror_ind ( 224 | port, 225 | exp_arg_block_id, 226 | ref_arg_block_id, 227 | smi_error); 228 | } 229 | } 230 | 231 | if (error == IOLINK_ERROR_NONE) 232 | { 233 | arg_block_pdin.h.arg_block.id = exp_arg_block_id; 234 | arg_block_pdin.h.len = pdin_len; 235 | iolink_smi_cnf ( 236 | port, 237 | ref_arg_block_id, 238 | sizeof (arg_block_pdin_head_t) + pdin_len, 239 | (arg_block_t *)&arg_block_pdin); 240 | } 241 | 242 | return error; 243 | } 244 | 245 | iolink_error_t pde_SMI_PDOut_req ( 246 | iolink_port_t * port, 247 | iolink_arg_block_id_t exp_arg_block_id, 248 | uint16_t arg_block_len, 249 | arg_block_t * arg_block) 250 | { 251 | iolink_arg_block_id_t ref_arg_block_id = arg_block->id; 252 | iolink_error_t error = pde_check_smi_req ( 253 | port, 254 | exp_arg_block_id, 255 | ref_arg_block_id, 256 | arg_block_len, 257 | IOLINK_ARG_BLOCK_ID_VOID_BLOCK, 258 | IOLINK_ARG_BLOCK_ID_PD_OUT, 259 | sizeof (arg_block_pdout_head_t) + 1, 260 | sizeof (arg_block_pdout_t)); 261 | 262 | if (error == IOLINK_ERROR_NONE) 263 | { 264 | if (pde_get_port_status_info (port) == IOLINK_PORT_STATUS_INFO_DO) 265 | { 266 | // TODO Set signal DO_C/Q, arg_block_pdout->data[0] & BIT(0) 267 | } 268 | else 269 | { 270 | arg_block_pdout_t * arg_block_pdout = (arg_block_pdout_t *)arg_block; 271 | bool output_enable = arg_block_pdout->h.oe & 1; 272 | iolink_pde_port_t * pde = iolink_get_pde_ctx (port); 273 | iolink_smi_errortypes_t smi_error = 274 | IOLINK_SMI_ERRORTYPE_DEV_NOT_ACCESSIBLE; 275 | 276 | error = AL_SetOutput_req (port, arg_block_pdout->data); 277 | 278 | if (error != IOLINK_ERROR_NONE) 279 | { 280 | iolink_smi_joberror_ind ( 281 | port, 282 | exp_arg_block_id, 283 | ref_arg_block_id, 284 | smi_error); 285 | } 286 | else if (pde->output_enable != output_enable) 287 | { 288 | error = AL_Control_req ( 289 | port, 290 | (output_enable) ? IOLINK_CONTROLCODE_PDOUTVALID 291 | : IOLINK_CONTROLCODE_PDOUTINVALID); 292 | 293 | if (error != IOLINK_ERROR_NONE) 294 | { 295 | iolink_smi_joberror_ind ( 296 | port, 297 | exp_arg_block_id, 298 | ref_arg_block_id, 299 | smi_error); 300 | } 301 | else 302 | { 303 | pde->output_enable = output_enable; 304 | } 305 | } 306 | } 307 | } 308 | 309 | if (error == IOLINK_ERROR_NONE) 310 | { 311 | iolink_smi_voidblock_cnf (port, ref_arg_block_id); 312 | } 313 | 314 | return error; 315 | } 316 | 317 | iolink_error_t pde_SMI_PDInOut_req ( 318 | iolink_port_t * port, 319 | iolink_arg_block_id_t exp_arg_block_id, 320 | uint16_t arg_block_len, 321 | arg_block_t * arg_block) 322 | { 323 | iolink_arg_block_id_t ref_arg_block_id = arg_block->id; 324 | iolink_error_t error = pde_check_smi_req ( 325 | port, 326 | exp_arg_block_id, 327 | ref_arg_block_id, 328 | arg_block_len, 329 | IOLINK_ARG_BLOCK_ID_PD_IN_OUT, 330 | IOLINK_ARG_BLOCK_ID_VOID_BLOCK, 331 | sizeof (arg_block_void_t), 332 | sizeof (arg_block_void_t)); 333 | 334 | if (error == IOLINK_ERROR_NONE) 335 | { 336 | if (pde_get_port_status_info (port) == IOLINK_PORT_STATUS_INFO_DI) 337 | { 338 | // TODO Copy result from Get signal status DI_C/Q 339 | } 340 | else 341 | { 342 | arg_block_pdinout_t arg_block_pdinout; 343 | uint8_t pdlen; 344 | iolink_pde_port_t * pde = iolink_get_pde_ctx (port); 345 | 346 | memset (&arg_block_pdinout, 0, sizeof (arg_block_pdinout_t)); 347 | arg_block_pdinout.h.port_qualifier_info = 348 | pde_port_quality_to_qualifier (port); 349 | arg_block_pdinout.h.oe = pde->output_enable; 350 | 351 | error = AL_GetInputOutput_req (port, &pdlen, arg_block_pdinout.data); 352 | 353 | if (error != IOLINK_ERROR_NONE) 354 | { 355 | iolink_smi_errortypes_t smi_error = 356 | IOLINK_SMI_ERRORTYPE_DEV_NOT_ACCESSIBLE; 357 | iolink_smi_joberror_ind ( 358 | port, 359 | exp_arg_block_id, 360 | ref_arg_block_id, 361 | smi_error); 362 | } 363 | else 364 | { 365 | arg_block_pdinout.h.arg_block.id = exp_arg_block_id; 366 | uint8_t arg_block_pdinout_len = 367 | sizeof (arg_block_pdinout_head_t) + pdlen; 368 | iolink_smi_cnf ( 369 | port, 370 | ref_arg_block_id, 371 | arg_block_pdinout_len, 372 | (arg_block_t *)&arg_block_pdinout); 373 | } 374 | } 375 | } 376 | 377 | return error; 378 | } 379 | -------------------------------------------------------------------------------- /src/iolink_pde.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | /** 17 | * @file 18 | * @brief Process Data Exchange layer 19 | * 20 | */ 21 | 22 | #ifndef IOLINK_PDE_H 23 | #define IOLINK_PDE_H 24 | 25 | #include "iolink_main.h" /* iolink_job_t */ 26 | #include "iolink_types.h" 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /** 33 | * PDE FSM states 34 | */ 35 | typedef enum iolink_pde_state 36 | { 37 | PD_STATE_Inactive = 0, 38 | PD_STATE_PDactive, 39 | PD_STATE_LAST 40 | } iolink_pde_state_t; 41 | 42 | typedef struct iolink_pde_port 43 | { 44 | iolink_pde_state_t state; 45 | bool output_enable; 46 | } iolink_pde_port_t; 47 | 48 | void iolink_pde_init (iolink_port_t * port); 49 | 50 | iolink_error_t PD_Start (iolink_port_t * port); 51 | iolink_error_t PD_Stop (iolink_port_t * port); 52 | iolink_error_t AL_NewInput_ind (iolink_port_t * port); 53 | iolink_error_t AL_Control_ind ( 54 | iolink_port_t * port, 55 | iolink_controlcode_t controlcode); 56 | 57 | iolink_error_t pde_SMI_PDIn_req ( 58 | iolink_port_t * port, 59 | iolink_arg_block_id_t exp_arg_block_id, 60 | uint16_t arg_block_len, 61 | arg_block_t * arg_block); 62 | 63 | iolink_error_t pde_SMI_PDOut_req ( 64 | iolink_port_t * port, 65 | iolink_arg_block_id_t exp_arg_block_id, 66 | uint16_t arg_block_len, 67 | arg_block_t * arg_block); 68 | 69 | iolink_error_t pde_SMI_PDInOut_req ( 70 | iolink_port_t * port, 71 | iolink_arg_block_id_t exp_arg_block_id, 72 | uint16_t arg_block_len, 73 | arg_block_t * arg_block); 74 | #ifdef __cplusplus 75 | } 76 | #endif 77 | 78 | #endif /* IOLINK_PDE_H */ 79 | -------------------------------------------------------------------------------- /src/iolink_pl.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #include "iolink_pl.h" 17 | 18 | #include "osal.h" 19 | #include "osal_log.h" 20 | 21 | #ifdef __rtk__ 22 | #include 23 | #include 24 | #include 25 | #endif 26 | 27 | #include /* O_RDWR */ 28 | #include /* close */ 29 | 30 | #include "iolink_main.h" /* iolink_port_t */ 31 | 32 | /** 33 | * @file 34 | * @brief Physical layer 35 | * 36 | */ 37 | 38 | #define IOLINK_RXERR_CHKSM BIT (0) 39 | #define IOLINK_RXERR_SIZE BIT (1) 40 | #define IOLINK_RXERR_FRAME BIT (2) 41 | #define IOLINK_RXERR_PARITY BIT (3) 42 | #define IOLINK_RXERR_TDLY BIT (4) 43 | #define IOLINK_TXERR_TRANSM BIT (5) 44 | #define IOLINK_TXERR_CYCL BIT (6) 45 | #define IOLINK_TXERR_CHKSM BIT (7) 46 | #define IOLINK_TXERR_SIZE BIT (8) 47 | 48 | /* 49 | * Wrapper functions 50 | */ 51 | void iolink_configure_pl_event ( 52 | iolink_port_t * port, 53 | os_event_t * event, 54 | uint32_t flag) 55 | { 56 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 57 | iolink_hw_drv_t * drv = pl->drv; 58 | 59 | if (drv != NULL) 60 | { 61 | os_mutex_lock (drv->mtx); 62 | drv->ops->configure_event (pl->drv, pl->arg, event, flag); 63 | os_mutex_unlock (drv->mtx); 64 | } 65 | } 66 | 67 | void iolink_pl_handler (iolink_port_t * port) 68 | { 69 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 70 | iolink_hw_drv_t * drv = pl->drv; 71 | 72 | if (drv != NULL) 73 | { 74 | os_mutex_lock (drv->mtx); 75 | drv->ops->pl_handler (pl->drv, pl->arg); 76 | os_mutex_unlock (drv->mtx); 77 | } 78 | } 79 | 80 | iolink_baudrate_t iolink_pl_get_baudrate (iolink_port_t * port) 81 | { 82 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 83 | iolink_hw_drv_t * drv = pl->drv; 84 | iolink_baudrate_t res; 85 | 86 | if (drv != NULL) 87 | { 88 | os_mutex_lock (drv->mtx); 89 | res = drv->ops->get_baudrate (pl->drv, pl->arg); 90 | os_mutex_unlock (drv->mtx); 91 | } 92 | else 93 | { 94 | res = IOLINK_BAUDRATE_NONE; 95 | } 96 | 97 | return res; 98 | } 99 | 100 | uint8_t iolink_pl_get_cycletime (iolink_port_t * port) 101 | { 102 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 103 | iolink_hw_drv_t * drv = pl->drv; 104 | uint8_t res; 105 | 106 | if (drv != NULL) 107 | { 108 | os_mutex_lock (drv->mtx); 109 | res = drv->ops->get_cycletime (pl->drv, pl->arg); 110 | os_mutex_unlock (drv->mtx); 111 | } 112 | else 113 | { 114 | res = 0u; 115 | } 116 | 117 | return res; 118 | } 119 | 120 | void iolink_pl_set_cycletime (iolink_port_t * port, uint8_t cycbyte) 121 | { 122 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 123 | iolink_hw_drv_t * drv = pl->drv; 124 | 125 | if (drv != NULL) 126 | { 127 | os_mutex_lock (drv->mtx); 128 | drv->ops->set_cycletime (pl->drv, pl->arg, cycbyte); 129 | os_mutex_unlock (drv->mtx); 130 | } 131 | } 132 | 133 | bool iolink_pl_get_data (iolink_port_t * port, uint8_t * rxdata, uint8_t len) 134 | { 135 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 136 | iolink_hw_drv_t * drv = pl->drv; 137 | bool res; 138 | 139 | if (drv != NULL) 140 | { 141 | os_mutex_lock (drv->mtx); 142 | res = drv->ops->get_data (pl->drv, pl->arg, rxdata, len); 143 | os_mutex_unlock (drv->mtx); 144 | } 145 | else 146 | { 147 | res = false; 148 | } 149 | 150 | return res; 151 | } 152 | 153 | void iolink_pl_get_error (iolink_port_t * port, uint8_t * cqerr, uint8_t * devdly) 154 | { 155 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 156 | iolink_hw_drv_t * drv = pl->drv; 157 | 158 | if (drv != NULL) 159 | { 160 | os_mutex_lock (drv->mtx); 161 | drv->ops->get_error (pl->drv, pl->arg, cqerr, devdly); 162 | os_mutex_unlock (drv->mtx); 163 | } 164 | } 165 | 166 | bool iolink_pl_init_sdci (iolink_port_t * port) 167 | { 168 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 169 | iolink_hw_drv_t * drv = pl->drv; 170 | bool res; 171 | 172 | if (drv != NULL) 173 | { 174 | os_mutex_lock (drv->mtx); 175 | res = drv->ops->init_sdci (pl->drv, pl->arg); 176 | os_mutex_unlock (drv->mtx); 177 | } 178 | else 179 | { 180 | res = false; 181 | } 182 | 183 | return res; 184 | } 185 | /* 186 | * PL services 187 | */ 188 | void PL_SetMode_req (iolink_port_t * port, iolink_pl_mode_t mode) 189 | { 190 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 191 | iolink_hw_drv_t * drv = pl->drv; 192 | 193 | if (drv != NULL) 194 | { 195 | os_mutex_lock (drv->mtx); 196 | drv->ops->set_mode (pl->drv, pl->arg, mode); 197 | os_mutex_unlock (drv->mtx); 198 | } 199 | } 200 | void PL_WakeUp_req (iolink_port_t * port) 201 | { 202 | #if IOLINK_HW == IOLINK_HW_MAX14819 203 | #endif 204 | } 205 | #if IOLINK_HW == IOLINK_HW_MAX14819 206 | void PL_Resend (iolink_port_t * port) 207 | { 208 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 209 | iolink_hw_drv_t * drv = pl->drv; 210 | 211 | if (drv != NULL) 212 | { 213 | os_mutex_lock (drv->mtx); 214 | drv->ops->send_msg (pl->drv, pl->arg); 215 | os_mutex_unlock (drv->mtx); 216 | } 217 | } 218 | 219 | void PL_Transfer_req ( 220 | iolink_port_t * port, 221 | uint8_t rxbytes, 222 | uint8_t txbytes, 223 | uint8_t * data) 224 | { 225 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 226 | iolink_hw_drv_t * drv = pl->drv; 227 | 228 | if (drv != NULL) 229 | { 230 | os_mutex_lock (drv->mtx); 231 | drv->ops->transfer_req (pl->drv, pl->arg, rxbytes, txbytes, data); 232 | os_mutex_unlock (drv->mtx); 233 | } 234 | } 235 | 236 | void PL_MessageDownload_req ( 237 | iolink_port_t * port, 238 | uint8_t rxbytes, 239 | uint8_t txbytes, 240 | uint8_t * data) 241 | { 242 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 243 | iolink_hw_drv_t * drv = pl->drv; 244 | 245 | if (drv != NULL) 246 | { 247 | os_mutex_lock (drv->mtx); 248 | drv->ops->dl_msg (pl->drv, pl->arg, rxbytes, txbytes, data); 249 | os_mutex_unlock (drv->mtx); 250 | } 251 | } 252 | 253 | void PL_EnableCycleTimer (iolink_port_t * port) 254 | { 255 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 256 | iolink_hw_drv_t * drv = pl->drv; 257 | 258 | if (drv != NULL) 259 | { 260 | os_mutex_lock (drv->mtx); 261 | drv->ops->enable_cycle_timer (pl->drv, pl->arg); 262 | os_mutex_unlock (drv->mtx); 263 | } 264 | } 265 | 266 | void PL_DisableCycleTimer (iolink_port_t * port) 267 | { 268 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 269 | iolink_hw_drv_t * drv = pl->drv; 270 | 271 | if (drv != NULL) 272 | { 273 | os_mutex_lock (drv->mtx); 274 | drv->ops->disable_cycle_timer (pl->drv, pl->arg); 275 | os_mutex_unlock (drv->mtx); 276 | } 277 | } 278 | 279 | #else 280 | void PL_Transfer_req (iolink_port_t * port, uint8_t data) 281 | { 282 | } 283 | #endif 284 | 285 | void iolink_pl_init (iolink_port_t * port, iolink_hw_drv_t * drv, void * arg) 286 | { 287 | iolink_pl_port_t * pl = iolink_get_pl_ctx (port); 288 | 289 | memset (pl, 0, sizeof (iolink_pl_port_t)); 290 | 291 | pl->drv = drv; 292 | pl->arg = arg; 293 | } 294 | -------------------------------------------------------------------------------- /src/iolink_pl.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | /** 17 | * @file 18 | * @brief Physical layer 19 | * 20 | */ 21 | 22 | #ifndef IOLINK_PL_H 23 | #define IOLINK_PL_H 24 | 25 | #if IOLINK_HW != IOLINK_HW_MAX14819 26 | #error "IO-Link HW not supported: ""IOLINK_HW" 27 | #endif /* IOLINK_HW */ 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | typedef struct iolink_pl_port 38 | { 39 | int fd; 40 | iolink_hw_drv_t * drv; 41 | void * arg; 42 | } iolink_pl_port_t; 43 | 44 | /* 45 | * Function interface 46 | */ 47 | void iolink_configure_pl_event ( 48 | iolink_port_t * port, 49 | os_event_t * event, 50 | uint32_t flag); 51 | void iolink_pl_handler (iolink_port_t * port); 52 | iolink_baudrate_t iolink_pl_get_baudrate (iolink_port_t * port); 53 | uint8_t iolink_pl_get_cycletime (iolink_port_t * port); 54 | void iolink_pl_set_cycletime (iolink_port_t * port, uint8_t cycbyte); 55 | bool iolink_pl_get_data (iolink_port_t * port, uint8_t * rxdata, uint8_t len); 56 | void iolink_pl_get_error (iolink_port_t * port, uint8_t * cqerr, uint8_t * devdly); 57 | bool iolink_pl_init_sdci (iolink_port_t * port); 58 | 59 | /* 60 | * PL Services 61 | */ 62 | void PL_SetMode_req (iolink_port_t * port, iolink_pl_mode_t mode); 63 | void PL_WakeUp_req (iolink_port_t * port); 64 | #if IOLINK_HW == IOLINK_HW_MAX14819 65 | void PL_Resend (iolink_port_t * port); 66 | void PL_Transfer_req ( 67 | iolink_port_t * port, 68 | uint8_t rxbytes, 69 | uint8_t txbytes, 70 | uint8_t * data); 71 | void PL_MessageDownload_req ( 72 | iolink_port_t * port, 73 | uint8_t rxbytes, 74 | uint8_t txbytes, 75 | uint8_t * data); 76 | void PL_EnableCycleTimer (iolink_port_t * port); 77 | void PL_DisableCycleTimer (iolink_port_t * port); 78 | #else 79 | void PL_Transfer_req (iolink_port_t * port, uint8_t data); 80 | #endif 81 | void iolink_pl_init (iolink_port_t * port, iolink_hw_drv_t * drv, void * arg); 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | 87 | #endif /* IOLINK_PL_H */ 88 | -------------------------------------------------------------------------------- /src/iolink_pl_hw_drv.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifndef IOLINK_PL_HW_DRV_H 17 | #define IOLINK_PL_HW_DRV_H 18 | 19 | #ifdef __rtk__ 20 | #include 21 | #endif 22 | #include "iolink_types.h" 23 | #include "iolink.h" 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | struct iolink_hw_drv; 30 | 31 | typedef struct iolink_hw_ops 32 | { 33 | iolink_baudrate_t (*get_baudrate) (struct iolink_hw_drv * iolink_hw, void * arg); 34 | uint8_t (*get_cycletime) (struct iolink_hw_drv * iolink_hw, void * arg); 35 | void (*set_cycletime) ( 36 | struct iolink_hw_drv * iolink_hw, 37 | void * arg, 38 | uint8_t cycbyte); 39 | bool (*set_mode) ( 40 | struct iolink_hw_drv * iolink_hw, 41 | void * arg, 42 | iolink_pl_mode_t mode); 43 | void (*enable_cycle_timer) (struct iolink_hw_drv * iolink_hw, void * arg); 44 | void (*disable_cycle_timer) (struct iolink_hw_drv * iolink_hw, void * arg); 45 | void (*get_error) ( 46 | struct iolink_hw_drv * iolink_hw, 47 | void * arg, 48 | uint8_t * cqerr, 49 | uint8_t * devdly); 50 | bool (*get_data) ( 51 | struct iolink_hw_drv * iolink_hw, 52 | void * arg, 53 | uint8_t * rxdata, 54 | uint8_t len); 55 | void (*dl_msg) ( 56 | struct iolink_hw_drv * iolink_hw, 57 | void * arg, 58 | uint8_t rxbytes, 59 | uint8_t txbytes, 60 | uint8_t * data); 61 | void (*send_msg) (struct iolink_hw_drv * iolink_hw, void * arg); 62 | void (*transfer_req) ( 63 | struct iolink_hw_drv * iolink_hw, 64 | void * arg, 65 | uint8_t rxbytes, 66 | uint8_t txbytes, 67 | uint8_t * data); 68 | bool (*init_sdci) (struct iolink_hw_drv * iolink_hw, void * arg); 69 | void (*configure_event) ( 70 | struct iolink_hw_drv * iolink_hw, 71 | void * arg, 72 | os_event_t * event, 73 | uint32_t flag); 74 | void (*pl_handler) (struct iolink_hw_drv * iolink_hw, void * arg); 75 | } iolink_hw_ops_t; 76 | 77 | typedef struct iolink_hw_drv 78 | { 79 | os_mutex_t * mtx; 80 | const iolink_hw_ops_t * ops; 81 | } iolink_hw_drv_t; 82 | 83 | void iolink_hw_init (iolink_hw_drv_t * iolink_hw); 84 | 85 | #ifdef __cplusplus 86 | } 87 | #endif 88 | 89 | #endif /* IOLINK_PL_HW_DRV_H */ 90 | -------------------------------------------------------------------------------- /src/iolink_sm.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | /** 17 | * @file 18 | * @brief System Management layer 19 | * 20 | */ 21 | 22 | #ifndef IOLINK_SM_H 23 | #define IOLINK_SM_H 24 | 25 | #include "iolink_main.h" 26 | #include "iolink_types.h" 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | /* Direct Parameter page, see IO-Link Interface Spec v1.1.3 chapter B.1.1 */ 33 | #define IOL_DIR_PARAMA_MASTER_CMD 0x00 34 | #define IOL_DIR_PARAMA_MASTER_CYCL 0x01 35 | #define IOL_DIR_PARAMA_MIN_CYCL 0x02 36 | #define IOL_DIR_PARAMA_MSEQ_CAP 0x03 37 | #define IOL_DIR_PARAMA_REV_ID 0x04 38 | #define IOL_DIR_PARAMA_PDI 0x05 39 | #define IOL_DIR_PARAMA_PDO 0x06 40 | #define IOL_DIR_PARAMA_VID_1 0x07 /* MSB */ 41 | #define IOL_DIR_PARAMA_VID_2 0x08 /* LSB */ 42 | #define IOL_DIR_PARAMA_DID_1 0x09 /* MSB */ 43 | #define IOL_DIR_PARAMA_DID_2 0x0A 44 | #define IOL_DIR_PARAMA_DID_3 0x0B /* LSB */ 45 | #define IOL_DIR_PARAMA_FID_1 0x0C /* MSB */ 46 | #define IOL_DIR_PARAMA_FID_2 0x0D /* LSB */ 47 | #define IOL_DIR_PARAMA_SYS_CMD 0x0F 48 | #define IOL_DIR_PARAMA_DUMMY_WURQ 0xFF /* Dummy used for first DL_Read_cnf */ 49 | 50 | /* MasterCommand, see IO-Link Interface Spec v1.1.3 chapter B.1.2 */ 51 | #define IOL_MASTERCMD_NONE 0x00 52 | #define IOL_MASTERCMD_FALLBACK 0x5A 53 | #define IOL_MASTERCMD_MASTER_IDENT 0x95 54 | #define IOL_MASTERCMD_DEVICE_IDENT 0x96 55 | #define IOL_MASTERCMD_DEVICE_STARTUP 0x97 56 | #define IOL_MASTERCMD_DEVICE_PD_OUT_OP 0x98 57 | #define IOL_MASTERCMD_DEVICE_OPERATE 0x99 58 | #define IOL_MASTERCMD_DEVICE_PREOP 0x9A 59 | 60 | /* Device parameter (Index assignment of data objects), 61 | * see IO-Link Interface Spec v1.1.3 chapter B.2.1 62 | */ 63 | #define IOL_DEV_PARAMA_DIRECT_PAR_P1 0x0000 64 | #define IOL_DEV_PARAMA_DIRECT_PAR_P2 0x0001 65 | #define IOL_DEV_PARAMA_SYSTEM_CMD 0x0002 66 | #define IOL_DEV_PARAMA_DATA_STOREAGE 0x0003 67 | #define IOL_DEV_PARAMA_DEV_ACCESS_LOCK 0x000C 68 | #define IOL_DEV_PARAMA_VENDOR_NAME 0x0010 69 | #define IOL_DEV_PARAMA_VENDOR_TEXT 0x0011 70 | #define IOL_DEV_PARAMA_PRODUCT_NAME 0x0012 71 | #define IOL_DEV_PARAMA_PRODUCT_ID 0x0013 72 | #define IOL_DEV_PARAMA_PRODUCT_TEXT 0x0014 73 | #define IOL_DEV_PARAMA_SERIAL_NUMBER 0x0015 74 | #define IOL_DEV_PARAMA_HARDWARE_REV 0x0016 75 | #define IOL_DEV_PARAMA_FIRMWARE_REV 0x0017 76 | #define IOL_DEV_PARAMA_ERROR_COUNT 0x0020 77 | #define IOL_DEV_PARAMA_DEVICE_STATUS 0x0024 78 | 79 | typedef enum iolink_sm_state 80 | { 81 | SM_STATE_PortInactive = 0, 82 | // SM_STATE_checkCompatibility, 83 | SM_STATE_waitonDLPreoperate, 84 | SM_STATE_checkSerNum, 85 | SM_STATE_wait, 86 | SM_STATE_SMOperate, 87 | SM_STATE_InspectionFault, 88 | SM_STATE_waitonDLOperate, 89 | SM_STATE_DIDO, 90 | SM_STATE_ReadComParameter, 91 | SM_STATE_CheckCompV10, 92 | SM_STATE_CheckVxy, 93 | SM_STATE_CheckComp, 94 | SM_STATE_RestartDevice, 95 | SM_STATE_waitForFallback, /* Not in spec. */ 96 | SM_STATE_write_master_cycl, /* Not in spec. */ 97 | SM_STATE_wait_devmode_cnf, /* NOt in spec. */ 98 | SM_STATE_LAST 99 | } iolink_sm_state_t; 100 | 101 | /** 102 | * SM state-machine events. 103 | * 104 | * SM events trigger state transitions. 105 | */ 106 | // TODO: rename 107 | typedef enum iolink_fsm_sm_event 108 | { 109 | SM_EVENT_NONE = 0, 110 | SM_EVENT_DL_Mode_STARTUP, /* T1 */ 111 | SM_EVENT_CompOK, /* T2 */ 112 | SM_EVENT_DL_Mode_COMLOST, /* T3 */ 113 | SM_EVENT_V10CompOK, /* T4 */ 114 | SM_EVENT_V10CompFault, /* T5 */ 115 | SM_EVENT_RevisionFault, /* T6 */ 116 | SM_EVENT_CompFault, /* T7 */ 117 | SM_EVENT_DL_Mode_PREOPERATE, /* T8 */ 118 | SM_EVENT_DL_Mode_OPERATE, /* T9 and T13 */ 119 | SM_EVENT_SerNumOK, /* T10 */ 120 | SM_EVENT_SerNumFault, /* T11 */ 121 | SM_EVENT_SM_Operate, /* T12 */ 122 | SM_EVENT_SM_Operate_v10, /* T12 for v1.0 device*/ 123 | SM_EVENT_SM_SetPortConfig_INACTIVE, /* T14 */ 124 | SM_EVENT_SM_SetPortConfig_CFGCOM_or_AUTOCOM, /* T15 */ 125 | SM_EVENT_SM_SetPortConfig_DI_or_DO, /* T16 */ 126 | SM_EVENT_CycTimeFault, /* T17 */ 127 | SM_EVENT_CycTimeFault_V10, /* T18 */ 128 | SM_EVENT_V10, /* T20 */ 129 | SM_EVENT_NOT_V10, /* T21 */ 130 | SM_EVENT_REVISION_OK, /* T22 */ 131 | SM_EVENT_RETRY_STARTUP, /* T23 and T25 */ 132 | SM_EVENT_WriteDone, /* T24 */ 133 | SM_EVENT_CNF_COMLOST, /* T3, COMLOST when waiting on DL_{Read,Write}_cnf() or 134 | AL_Read_cnf() */ 135 | SM_EVENT_WRITE_MASTER_CYCL_REQ, /* Not in spec */ 136 | SM_EVENT_WRITE_MASTER_CYCL_DONE, /* Not in spec */ 137 | SM_EVENT_LAST, 138 | } iolink_fsm_sm_event_t; 139 | 140 | typedef struct iolink_sm_port 141 | { 142 | iolink_sm_state_t state; 143 | iolink_mhmode_t mh_mode; 144 | iolink_mhmode_t comrate; 145 | iolink_smp_parameterlist_t config_paramlist; 146 | iolink_smp_parameterlist_t real_paramlist; 147 | iolink_dev_com_t dev_com; 148 | uint8_t dl_addr; 149 | uint8_t CompRetry; 150 | iolink_fsm_sm_event_t error_event; 151 | } iolink_sm_port_t; 152 | 153 | void DL_Mode_ind_baud (iolink_port_t * port, iolink_mhmode_t realmode); 154 | void DL_Mode_ind (iolink_port_t * port, iolink_mhmode_t realmode); 155 | iolink_error_t SM_Operate (iolink_port_t * port); 156 | iolink_error_t SM_SetPortConfig_req ( 157 | iolink_port_t * port, 158 | iolink_smp_parameterlist_t * parameterlist); 159 | void DL_Read_cnf (iolink_port_t * port, uint8_t value, iolink_status_t errorinfo); 160 | void DL_Write_cnf (iolink_port_t * port, iolink_status_t errorinfo); 161 | 162 | void DL_Write_Devicemode_cnf ( 163 | iolink_port_t * port, 164 | iolink_status_t errorinfo, 165 | iolink_dl_mode_t devicemode); 166 | 167 | /** 168 | * Initialise SM state-machine 169 | * 170 | * This function initialises the SM state-machine for one IO-Link port and 171 | * should be called when the stack is started. 172 | * 173 | * @param port port handle 174 | */ 175 | void iolink_sm_init (iolink_port_t * port); 176 | 177 | #ifdef __cplusplus 178 | } 179 | #endif 180 | 181 | #endif /* IOLINK_SM_H */ 182 | -------------------------------------------------------------------------------- /test/iolink_test.cpp: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #include 17 | 18 | int main (int argc, char * argv[]) 19 | { 20 | ::testing::InitGoogleTest (&argc, argv); 21 | int result = RUN_ALL_TESTS(); 22 | return result; 23 | } 24 | -------------------------------------------------------------------------------- /test/mocks.cpp: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #include "mocks.h" 17 | #include "test_util.h" 18 | #include "iolink_max14819.h" 19 | #include "iolink_sm.h" 20 | #include "iolink_al.h" 21 | #include "iolink_main.h" 22 | #include "iolink_types.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | uint8_t mock_iolink_revisionid = IOL_DIR_PARAM_REV_V11; 29 | uint8_t mock_iolink_master_cycletime = 0; 30 | uint8_t mock_iolink_min_cycletime = 79; 31 | uint16_t mock_iolink_vendorid = 1171; 32 | uint32_t mock_iolink_deviceid = 0x112345; 33 | uint16_t mock_iolink_functionid = 0x999A; 34 | iolink_transmission_rate_t mock_iolink_trans_rate = IOLINK_TRANSMISSION_RATE_COM1; 35 | 36 | uint8_t mock_iolink_restart_device_cnt = 0; 37 | uint8_t mock_iolink_mastercmd_master_ident_cnt = 0; 38 | uint8_t mock_iolink_mastercmd_preop_cnt = 0; 39 | uint8_t mock_iolink_write_devmode_inactive_cnt = 0; 40 | uint8_t mock_iolink_write_devmode_operate_cnt = 0; 41 | uint8_t mock_iolink_al_data[64] = {0}; 42 | uint8_t mock_iolink_al_data_len = 0; 43 | uint16_t mock_iolink_al_data_index = 0; 44 | uint8_t mock_iolink_al_data_subindex = 0; 45 | uint8_t mock_iolink_al_read_cnf_cnt = 0; 46 | uint8_t mock_iolink_al_read_req_cnt = 0; 47 | uint8_t mock_iolink_al_write_cnf_cnt = 0; 48 | uint8_t mock_iolink_al_write_req_cnt = 0; 49 | uint8_t mock_iolink_al_control_req_cnt = 0; 50 | uint8_t mock_iolink_al_control_ind_cnt = 0; 51 | uint8_t mock_iolink_dl_pdout_data[IOLINK_PD_MAX_SIZE] = {0}; 52 | uint8_t mock_iolink_dl_pdout_data_len = 0; 53 | uint8_t mock_iolink_dl_pdin_data[IOLINK_PD_MAX_SIZE] = {0}; 54 | uint8_t mock_iolink_dl_pdin_data_len = 0; 55 | uint8_t mock_iolink_dl_control_req_cnt = 0; 56 | uint8_t mock_iolink_dl_eventconf_req_cnt = 0; 57 | uint8_t mock_iolink_al_setoutput_req_cnt = 0; 58 | uint8_t mock_iolink_al_getinput_req_cnt = 0; 59 | uint8_t mock_iolink_al_getinputoutput_req_cnt = 0; 60 | uint8_t mock_iolink_al_newinput_inf_cnt = 0; 61 | uint8_t mock_iolink_al_event_cnt = 0; 62 | uint8_t mock_iolink_sm_operate_cnt = 0; 63 | uint8_t mock_iolink_ds_delete_cnt = 0; 64 | uint8_t mock_iolink_ds_startup_cnt = 0; 65 | uint8_t mock_iolink_ds_ready_cnt = 0; 66 | uint8_t mock_iolink_ds_fault_cnt = 0; 67 | iolink_ds_fault_t mock_iolink_ds_fault = IOLINK_DS_FAULT_NONE; 68 | uint8_t mock_iolink_od_start_cnt = 0; 69 | uint8_t mock_iolink_od_stop_cnt = 0; 70 | uint8_t mock_iolink_pd_start_cnt = 0; 71 | uint8_t mock_iolink_pd_stop_cnt = 0; 72 | diag_entry_t mock_iolink_al_events[6]; 73 | iolink_sm_portmode_t mock_iolink_sm_portmode = IOLINK_SM_PORTMODE_INACTIVE; 74 | iolink_smi_errortypes_t mock_iolink_al_read_errortype = IOLINK_SMI_ERRORTYPE_NONE; 75 | iolink_smi_errortypes_t mock_iolink_al_write_errortype = 76 | IOLINK_SMI_ERRORTYPE_NONE; 77 | iolink_controlcode_t mock_iolink_controlcode = IOLINK_CONTROLCODE_NONE; 78 | void (*mock_iolink_al_write_cnf_cb) ( 79 | iolink_port_t * port, 80 | iolink_smi_errortypes_t errortype); 81 | void (*mock_iolink_al_read_cnf_cb) ( 82 | iolink_port_t * port, 83 | uint8_t len, 84 | const uint8_t * data, 85 | iolink_smi_errortypes_t errortype); 86 | 87 | uint8_t mock_iolink_smi_cnf_cnt = 0; 88 | uint8_t mock_iolink_smi_joberror_cnt = 0; 89 | uint8_t mock_iolink_smi_portcfg_cnf_cnt = 0; 90 | uint8_t mock_iolink_smi_portstatus_cnf_cnt = 0; 91 | uint8_t mock_iolink_smi_portevent_ind_cnt = 0; 92 | uint8_t arg_block_buff[sizeof (arg_block_test_t) + 64]; 93 | arg_block_t * mock_iolink_smi_arg_block = (arg_block_t *)arg_block_buff; 94 | uint16_t mock_iolink_smi_arg_block_len = 0; 95 | iolink_arg_block_id_t mock_iolink_smi_ref_arg_block_id = 96 | IOLINK_ARG_BLOCK_ID_MASTERIDENT; 97 | 98 | iolink_smp_parameterlist_t mock_iolink_cfg_paraml; 99 | iolink_job_t mock_iolink_job; 100 | 101 | iolink_error_t mock_DL_Write_req (iolink_port_t * port, uint8_t address, uint8_t value) 102 | { 103 | (void)port; 104 | if (address == IOL_DIR_PARAMA_MASTER_CMD) 105 | { 106 | if (value == IOL_MASTERCMD_DEVICE_IDENT) 107 | mock_iolink_restart_device_cnt++; 108 | else if (value == IOL_MASTERCMD_MASTER_IDENT) 109 | mock_iolink_mastercmd_master_ident_cnt++; 110 | else if (value == IOL_MASTERCMD_DEVICE_PREOP) 111 | mock_iolink_mastercmd_preop_cnt++; 112 | } 113 | DL_Write_cnf (port, IOLINK_STATUS_NO_ERROR); 114 | return IOLINK_ERROR_NONE; 115 | } 116 | 117 | iolink_error_t mock_DL_Read_req (iolink_port_t * port, uint8_t address) 118 | { 119 | uint8_t data; 120 | 121 | switch (address) 122 | { 123 | case IOL_DIR_PARAMA_MASTER_CMD: 124 | data = 0; 125 | break; 126 | case IOL_DIR_PARAMA_MASTER_CYCL: 127 | data = mock_iolink_master_cycletime; 128 | break; 129 | case IOL_DIR_PARAMA_MIN_CYCL: 130 | data = mock_iolink_min_cycletime; 131 | break; 132 | case IOL_DIR_PARAMA_MSEQ_CAP: 133 | data = 0; 134 | break; 135 | case IOL_DIR_PARAMA_REV_ID: 136 | data = mock_iolink_revisionid; 137 | break; 138 | case IOL_DIR_PARAMA_PDI: 139 | data = 0; 140 | break; 141 | case IOL_DIR_PARAMA_PDO: 142 | data = 0; 143 | break; 144 | case IOL_DIR_PARAMA_VID_1: 145 | data = mock_iolink_vendorid >> 8; 146 | break; 147 | case IOL_DIR_PARAMA_VID_2: 148 | data = mock_iolink_vendorid & 0xFF; 149 | break; 150 | case IOL_DIR_PARAMA_DID_1: 151 | data = mock_iolink_deviceid >> 16; 152 | break; 153 | case IOL_DIR_PARAMA_DID_2: 154 | data = mock_iolink_deviceid >> 8; 155 | break; 156 | case IOL_DIR_PARAMA_DID_3: 157 | data = mock_iolink_deviceid & 0xFF; 158 | break; 159 | case IOL_DIR_PARAMA_FID_1: 160 | data = 0; 161 | break; 162 | case IOL_DIR_PARAMA_FID_2: 163 | data = 0; 164 | break; 165 | case IOL_DIR_PARAMA_SYS_CMD: 166 | data = 0; 167 | break; 168 | default: 169 | printf ("DL_Read: Address not valid: %u\n", address); 170 | return IOLINK_ERROR_ADDRESS_INVALID; 171 | } 172 | DL_Read_cnf (port, data, IOLINK_STATUS_NO_ERROR); 173 | 174 | // IOLINK_DL_EVENT_TIMEOUT; 175 | return IOLINK_ERROR_NONE; 176 | } 177 | 178 | iolink_error_t mock_DL_SetMode_req ( 179 | iolink_port_t * port, 180 | iolink_dl_mode_t mode, 181 | iolink_mode_vl_t * valuelist) 182 | { 183 | switch (mode) 184 | { 185 | case IOLINK_DLMODE_INACTIVE: 186 | break; 187 | case IOLINK_DLMODE_STARTUP: 188 | break; 189 | case IOLINK_DLMODE_PREOPERATE: 190 | break; 191 | case IOLINK_DLMODE_OPERATE: 192 | break; 193 | } 194 | return IOLINK_ERROR_NONE; 195 | } 196 | 197 | iolink_error_t mock_DL_Write_Devicemode_req ( 198 | iolink_port_t * port, 199 | iolink_dl_mode_t devicemode) 200 | { 201 | switch (devicemode) 202 | { 203 | case IOLINK_DLMODE_INACTIVE: 204 | mock_iolink_write_devmode_inactive_cnt++; 205 | break; 206 | case IOLINK_DLMODE_OPERATE: 207 | mock_iolink_write_devmode_operate_cnt++; 208 | break; 209 | default: 210 | CC_ASSERT (0); 211 | } 212 | 213 | DL_Write_Devicemode_cnf (port, IOLINK_STATUS_NO_ERROR, devicemode); 214 | 215 | return IOLINK_ERROR_NONE; 216 | } 217 | 218 | iolink_error_t mock_DL_ISDUTransport_req ( 219 | iolink_port_t * port, 220 | iolink_isdu_vl_t * valuelist) 221 | { 222 | if (valuelist->readwrite == IOLINK_RWDIRECTION_WRITE) 223 | { 224 | if (valuelist->length <= sizeof (mock_iolink_al_data)) 225 | { 226 | mock_iolink_al_data_len = valuelist->length; 227 | memcpy (mock_iolink_al_data, valuelist->data_write, valuelist->length); 228 | } 229 | else 230 | { 231 | CC_ASSERT (0); 232 | } 233 | } 234 | else if (valuelist->readwrite == IOLINK_RWDIRECTION_READ) 235 | { 236 | } 237 | else 238 | { 239 | CC_ASSERT (0); 240 | } 241 | 242 | return IOLINK_ERROR_NONE; 243 | } 244 | 245 | iolink_error_t mock_DL_EventConf_req (iolink_port_t * port) 246 | { 247 | mock_iolink_dl_eventconf_req_cnt++; 248 | return IOLINK_ERROR_NONE; 249 | } 250 | iolink_error_t mock_DL_ReadParam_req (iolink_port_t * port, uint16_t address) 251 | { 252 | return IOLINK_ERROR_NONE; 253 | } 254 | 255 | iolink_error_t mock_DL_WriteParam_req ( 256 | iolink_port_t * port, 257 | uint16_t address, 258 | uint8_t value) 259 | { 260 | mock_iolink_al_data_len = 1; 261 | mock_iolink_al_data[0] = value; 262 | 263 | return IOLINK_ERROR_NONE; 264 | } 265 | 266 | iolink_error_t mock_DL_Control_req ( 267 | iolink_port_t * port, 268 | iolink_controlcode_t controlcode) 269 | { 270 | mock_iolink_dl_control_req_cnt++; 271 | 272 | return IOLINK_ERROR_NONE; 273 | } 274 | 275 | iolink_error_t mock_DL_PDOutputGet_req ( 276 | iolink_port_t * port, 277 | uint8_t * len, 278 | uint8_t * data) 279 | { 280 | *len = mock_iolink_dl_pdout_data_len; 281 | memcpy (data, mock_iolink_dl_pdout_data, *len); 282 | 283 | return IOLINK_ERROR_NONE; 284 | } 285 | 286 | void mock_PL_SetMode_req (iolink_port_t * port, iolink_pl_mode_t mode) 287 | { 288 | } 289 | void mock_iolink_pl_init (iolink_port_t * port, iolink_hw_drv_t * drv, void * arg) 290 | { 291 | } 292 | 293 | iolink_error_t mock_AL_Read_req ( 294 | iolink_port_t * port, 295 | uint16_t index, 296 | uint8_t subindex, 297 | void (*al_read_cnf_cb) ( 298 | iolink_port_t * port, 299 | uint8_t len, 300 | const uint8_t * data, 301 | iolink_smi_errortypes_t errortype)) 302 | { 303 | mock_iolink_al_read_cnf_cb = al_read_cnf_cb; 304 | mock_iolink_al_read_req_cnt++; 305 | mock_iolink_al_data_index = index; 306 | mock_iolink_al_data_subindex = subindex; 307 | 308 | return IOLINK_ERROR_NONE; 309 | } 310 | 311 | void mock_AL_Read_cnf ( 312 | iolink_port_t * port, 313 | uint8_t len, 314 | const uint8_t * data, 315 | iolink_smi_errortypes_t errortype) 316 | { 317 | if (errortype == IOLINK_SMI_ERRORTYPE_NONE) 318 | { 319 | mock_iolink_al_data_len = len; 320 | memcpy (mock_iolink_al_data, data, len); 321 | } 322 | mock_iolink_al_read_cnf_cnt++; 323 | mock_iolink_al_read_errortype = errortype; 324 | } 325 | 326 | void mock_AL_Event_ind ( 327 | iolink_port_t * port, 328 | uint8_t event_cnt, 329 | diag_entry_t events[6]) 330 | { 331 | mock_iolink_al_event_cnt = event_cnt; 332 | memcpy (mock_iolink_al_events, events, event_cnt * sizeof (diag_entry_t)); 333 | } 334 | 335 | iolink_error_t mock_AL_Write_req ( 336 | iolink_port_t * port, 337 | uint16_t index, 338 | uint8_t subindex, 339 | uint8_t len, 340 | const uint8_t * data, 341 | void (*al_write_cb) (iolink_port_t * port, iolink_smi_errortypes_t errortype)) 342 | { 343 | mock_iolink_al_write_req_cnt++; 344 | mock_iolink_al_write_cnf_cb = al_write_cb; 345 | 346 | memcpy (mock_iolink_al_data, data, len); 347 | mock_iolink_al_data_index = index; 348 | mock_iolink_al_data_subindex = subindex; 349 | 350 | return IOLINK_ERROR_NONE; 351 | } 352 | 353 | void mock_AL_Write_cnf (iolink_port_t * port, iolink_smi_errortypes_t errortype) 354 | { 355 | mock_iolink_al_write_cnf_cnt++; 356 | mock_iolink_al_write_errortype = errortype; 357 | } 358 | 359 | iolink_error_t mock_AL_Control_req ( 360 | iolink_port_t * port, 361 | iolink_controlcode_t controlcode) 362 | { 363 | mock_iolink_al_control_req_cnt++; 364 | mock_iolink_controlcode = controlcode; 365 | 366 | return IOLINK_ERROR_NONE; 367 | } 368 | 369 | iolink_error_t mock_AL_Control_ind ( 370 | iolink_port_t * port, 371 | iolink_controlcode_t controlcode) 372 | { 373 | mock_iolink_al_control_ind_cnt++; 374 | mock_iolink_controlcode = controlcode; 375 | 376 | return IOLINK_ERROR_NONE; 377 | } 378 | 379 | iolink_error_t mock_AL_SetOutput_req ( 380 | iolink_port_t * port, 381 | uint8_t * data) 382 | { 383 | mock_iolink_al_setoutput_req_cnt++; 384 | 385 | return IOLINK_ERROR_NONE; 386 | } 387 | 388 | iolink_error_t mock_AL_GetInput_req ( 389 | iolink_port_t * port, 390 | uint8_t * len, 391 | uint8_t * data) 392 | { 393 | *len = mock_iolink_dl_pdin_data_len; 394 | memcpy (data, mock_iolink_dl_pdin_data, *len); 395 | mock_iolink_al_getinput_req_cnt++; 396 | 397 | return IOLINK_ERROR_NONE; 398 | } 399 | 400 | iolink_error_t mock_AL_GetInputOutput_req ( 401 | iolink_port_t * port, 402 | uint8_t * len, 403 | uint8_t * data) 404 | { 405 | uint8_t pdin_len; 406 | uint8_t * pdin_data; 407 | uint8_t * pdout_len; 408 | uint8_t * pdout_data; 409 | 410 | /* PDIn data length */ 411 | pdin_len = mock_iolink_dl_pdin_data_len; 412 | data[0] = pdin_len; 413 | /* PDIn_data[0] */ 414 | pdin_data = &data[1]; 415 | memcpy (pdin_data, mock_iolink_dl_pdin_data, pdin_len); 416 | /* PDOut data length */ 417 | pdout_len = &data[pdin_len + 1]; 418 | /* PDOut_data[0] */ 419 | pdout_data = &data[pdin_len + 2]; 420 | 421 | *pdout_len = mock_iolink_dl_pdout_data_len; 422 | memcpy (pdout_data, mock_iolink_dl_pdout_data, *pdout_len); 423 | 424 | *len = 1 + pdin_len + 1 + *pdout_len; 425 | 426 | mock_iolink_al_getinputoutput_req_cnt++; 427 | 428 | return IOLINK_ERROR_NONE; 429 | } 430 | 431 | iolink_error_t mock_AL_NewInput_ind (iolink_port_t * port) 432 | { 433 | mock_iolink_al_newinput_inf_cnt++; 434 | 435 | return IOLINK_ERROR_NONE; 436 | } 437 | 438 | iolink_error_t mock_DL_PDOutputUpdate_req ( 439 | iolink_port_t * port, 440 | uint8_t * outputdata) 441 | { 442 | return IOLINK_ERROR_NONE; 443 | } 444 | 445 | iolink_error_t mock_SM_Operate (iolink_port_t * port) 446 | { 447 | mock_iolink_sm_operate_cnt++; 448 | 449 | return IOLINK_ERROR_NONE; 450 | } 451 | void mock_SM_PortMode_ind (iolink_port_t * port, iolink_sm_portmode_t mode) 452 | { 453 | mock_iolink_sm_portmode = mode; 454 | } 455 | 456 | iolink_error_t mock_SM_SetPortConfig_req ( 457 | iolink_port_t * port, 458 | iolink_smp_parameterlist_t * parameterlist) 459 | { 460 | iolink_sm_port_t * sm = iolink_get_sm_ctx (port); 461 | iolink_smp_parameterlist_t * real_paramlist = &sm->real_paramlist; 462 | 463 | memcpy (&mock_iolink_cfg_paraml, parameterlist, sizeof (mock_iolink_cfg_paraml)); 464 | 465 | switch (mock_iolink_trans_rate) 466 | { 467 | case IOLINK_TRANSMISSION_RATE_COM1: 468 | sm->comrate = IOLINK_MHMODE_COM1; 469 | break; 470 | case IOLINK_TRANSMISSION_RATE_COM2: 471 | sm->comrate = IOLINK_MHMODE_COM2; 472 | break; 473 | case IOLINK_TRANSMISSION_RATE_COM3: 474 | sm->comrate = IOLINK_MHMODE_COM3; 475 | break; 476 | case IOLINK_TRANSMISSION_RATE_NOT_DETECTED: 477 | sm->comrate = IOLINK_MHMODE_INACTIVE; 478 | break; 479 | default: 480 | CC_ASSERT (0); 481 | break; 482 | } 483 | 484 | if (parameterlist->revisionid) 485 | real_paramlist->revisionid = parameterlist->revisionid; 486 | else 487 | real_paramlist->revisionid = mock_iolink_revisionid; 488 | if (parameterlist->cycletime) 489 | real_paramlist->cycletime = parameterlist->cycletime; 490 | else 491 | real_paramlist->cycletime = mock_iolink_min_cycletime; 492 | 493 | if (parameterlist->vendorid) 494 | real_paramlist->vendorid = parameterlist->vendorid; 495 | else 496 | real_paramlist->vendorid = mock_iolink_vendorid; 497 | 498 | if (parameterlist->deviceid) 499 | real_paramlist->deviceid = parameterlist->deviceid; 500 | else 501 | real_paramlist->deviceid = mock_iolink_deviceid; 502 | 503 | return IOLINK_ERROR_NONE; 504 | } 505 | 506 | void mock_DS_Fault (iolink_port_t * port, iolink_ds_fault_t fault) 507 | { 508 | mock_iolink_ds_fault = fault; 509 | mock_iolink_ds_fault_cnt++; 510 | } 511 | 512 | iolink_error_t mock_DS_Delete (iolink_port_t * port) 513 | { 514 | mock_iolink_ds_delete_cnt++; 515 | 516 | return IOLINK_ERROR_NONE; 517 | } 518 | 519 | iolink_error_t mock_DS_Startup (iolink_port_t * port) 520 | { 521 | mock_iolink_ds_startup_cnt++; 522 | 523 | return IOLINK_ERROR_NONE; 524 | } 525 | 526 | void mock_DS_Ready (iolink_port_t * port) 527 | { 528 | mock_iolink_ds_ready_cnt++; 529 | } 530 | 531 | iolink_error_t mock_OD_Start (iolink_port_t * port) 532 | { 533 | mock_iolink_od_start_cnt++; 534 | 535 | return IOLINK_ERROR_NONE; 536 | } 537 | 538 | iolink_error_t mock_OD_Stop (iolink_port_t * port) 539 | { 540 | mock_iolink_od_stop_cnt++; 541 | 542 | return IOLINK_ERROR_NONE; 543 | } 544 | 545 | iolink_error_t mock_PD_Start (iolink_port_t * port) 546 | { 547 | mock_iolink_pd_start_cnt++; 548 | 549 | return IOLINK_ERROR_NONE; 550 | } 551 | 552 | iolink_error_t mock_PD_Stop (iolink_port_t * port) 553 | { 554 | mock_iolink_pd_stop_cnt++; 555 | 556 | return IOLINK_ERROR_NONE; 557 | } 558 | 559 | void mock_SMI_cnf ( 560 | void * arg, 561 | uint8_t portnumber, 562 | iolink_arg_block_id_t ref_arg_block_id, 563 | uint16_t arg_block_len, 564 | arg_block_t * arg_block) 565 | { 566 | iolink_arg_block_id_t arg_block_id = arg_block->id; 567 | 568 | if (arg_block_id == IOLINK_ARG_BLOCK_ID_JOB_ERROR) 569 | mock_iolink_smi_joberror_cnt++; 570 | else if (ref_arg_block_id == IOLINK_ARG_BLOCK_ID_PORT_CFG_LIST) 571 | mock_iolink_smi_portcfg_cnf_cnt++; 572 | else if (arg_block_id == IOLINK_ARG_BLOCK_ID_PORT_STATUS_LIST) 573 | mock_iolink_smi_portstatus_cnf_cnt++; 574 | else if (arg_block_id == IOLINK_ARG_BLOCK_ID_PORT_EVENT) 575 | mock_iolink_smi_portevent_ind_cnt++; 576 | else 577 | mock_iolink_smi_cnf_cnt++; 578 | 579 | memcpy (mock_iolink_smi_arg_block, arg_block, arg_block_len); 580 | 581 | mock_iolink_smi_arg_block_len = arg_block_len; 582 | mock_iolink_smi_ref_arg_block_id = ref_arg_block_id; 583 | } 584 | 585 | static bool mock_os_mbox_post (os_mbox_t * mbox, void * msg, uint32_t time) 586 | { 587 | iolink_job_t * job = (iolink_job_t *)msg; 588 | 589 | CC_ASSERT (job != NULL); 590 | CC_ASSERT (job->port != NULL); 591 | 592 | if (job) 593 | { 594 | CC_ASSERT (job == &mock_iolink_job); 595 | } 596 | 597 | return false; 598 | } 599 | 600 | bool mock_iolink_post_job (iolink_port_t * port, iolink_job_t * job) 601 | { 602 | return mock_os_mbox_post (NULL, job, 0); 603 | } 604 | 605 | iolink_job_t * mock_iolink_fetch_avail_job (iolink_port_t * port) 606 | { 607 | iolink_job_t * job = &mock_iolink_job; 608 | 609 | job->port = port; 610 | 611 | return job; 612 | } 613 | 614 | iolink_job_t * mock_iolink_fetch_avail_api_job (iolink_port_t * port) 615 | { 616 | iolink_job_t * job = &mock_iolink_job; 617 | 618 | job->port = port; 619 | 620 | return job; 621 | } 622 | -------------------------------------------------------------------------------- /test/mocks.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifndef MOCKS_H 17 | #define MOCKS_H 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | #include "iolink_sm.h" 24 | #include "iolink_max14819.h" 25 | #include "osal.h" 26 | 27 | #include 28 | #include 29 | 30 | extern uint8_t mock_iolink_revisionid; 31 | extern uint8_t mock_iolink_master_cycletime; 32 | extern uint8_t mock_iolink_min_cycletime; 33 | extern uint16_t mock_iolink_vendorid; 34 | extern uint32_t mock_iolink_deviceid; 35 | extern uint16_t mock_iolink_functionid; 36 | extern iolink_transmission_rate_t mock_iolink_trans_rate; 37 | 38 | extern uint8_t mock_iolink_restart_device_cnt; 39 | extern uint8_t mock_iolink_mastercmd_master_ident_cnt; 40 | extern uint8_t mock_iolink_mastercmd_preop_cnt; 41 | extern uint8_t mock_iolink_write_devmode_inactive_cnt; 42 | extern uint8_t mock_iolink_write_devmode_operate_cnt; 43 | extern iolink_job_t mock_iolink_job; 44 | extern uint8_t mock_iolink_al_data[64]; 45 | extern uint8_t mock_iolink_al_data_len; 46 | extern uint16_t mock_iolink_al_data_index; 47 | extern uint8_t mock_iolink_al_data_subindex; 48 | extern uint8_t mock_iolink_al_read_cnf_cnt; 49 | extern uint8_t mock_iolink_al_read_req_cnt; 50 | extern uint8_t mock_iolink_al_write_cnf_cnt; 51 | extern uint8_t mock_iolink_al_write_req_cnt; 52 | extern uint8_t mock_iolink_al_control_req_cnt; 53 | extern uint8_t mock_iolink_al_control_ind_cnt; 54 | extern uint8_t mock_iolink_dl_pdout_data[IOLINK_PD_MAX_SIZE]; 55 | extern uint8_t mock_iolink_dl_pdout_data_len; 56 | extern uint8_t mock_iolink_dl_pdin_data[IOLINK_PD_MAX_SIZE]; 57 | extern uint8_t mock_iolink_dl_pdin_data_len; 58 | extern uint8_t mock_iolink_dl_control_req_cnt; 59 | extern uint8_t mock_iolink_dl_eventconf_req_cnt; 60 | extern uint8_t mock_iolink_al_getinput_req_cnt; 61 | extern uint8_t mock_iolink_al_getinputoutput_req_cnt; 62 | extern uint8_t mock_iolink_al_newinput_inf_cnt; 63 | extern iolink_smi_errortypes_t mock_iolink_al_read_errortype; 64 | extern iolink_smi_errortypes_t mock_iolink_al_write_errortype; 65 | extern iolink_controlcode_t mock_iolink_controlcode; 66 | extern uint8_t mock_iolink_al_event_cnt; 67 | extern uint8_t mock_iolink_sm_operate_cnt; 68 | extern uint8_t mock_iolink_ds_delete_cnt; 69 | extern uint8_t mock_iolink_ds_startup_cnt; 70 | extern uint8_t mock_iolink_ds_ready_cnt; 71 | extern uint8_t mock_iolink_ds_fault_cnt; 72 | extern iolink_ds_fault_t mock_iolink_ds_fault; 73 | extern uint8_t mock_iolink_od_start_cnt; 74 | extern uint8_t mock_iolink_od_stop_cnt; 75 | extern uint8_t mock_iolink_pd_start_cnt; 76 | extern uint8_t mock_iolink_pd_stop_cnt; 77 | extern diag_entry_t mock_iolink_al_events[6]; 78 | extern iolink_sm_portmode_t mock_iolink_sm_portmode; 79 | extern iolink_smp_parameterlist_t mock_iolink_cfg_paraml; 80 | 81 | extern arg_block_t * mock_iolink_smi_arg_block; 82 | extern uint8_t mock_iolink_smi_cnf_cnt; 83 | extern uint8_t mock_iolink_smi_joberror_cnt; 84 | extern uint8_t mock_iolink_smi_portcfg_cnf_cnt; 85 | extern uint8_t mock_iolink_smi_portstatus_cnf_cnt; 86 | extern uint8_t mock_iolink_smi_portevent_ind_cnt; 87 | extern uint16_t mock_iolink_smi_arg_block_len; 88 | extern iolink_arg_block_id_t mock_iolink_smi_ref_arg_block_id; 89 | 90 | extern void (*mock_iolink_al_write_cnf_cb) ( 91 | iolink_port_t * port, 92 | iolink_smi_errortypes_t errortype); 93 | extern void (*mock_iolink_al_read_cnf_cb) ( 94 | iolink_port_t * port, 95 | uint8_t len, 96 | const uint8_t * data, 97 | iolink_smi_errortypes_t errortype); 98 | 99 | iolink_error_t mock_DL_Write_req ( 100 | iolink_port_t * port, 101 | uint8_t address, 102 | uint8_t value); 103 | iolink_error_t mock_DL_Read_req (iolink_port_t * port, uint8_t address); 104 | iolink_error_t mock_DL_SetMode_req ( 105 | iolink_port_t * port, 106 | iolink_dl_mode_t mode, 107 | iolink_mode_vl_t * valuelist); 108 | iolink_error_t mock_DL_Write_Devicemode_req ( 109 | iolink_port_t * port, 110 | iolink_dl_mode_t devicemode); 111 | iolink_error_t mock_DL_EventConf_req (iolink_port_t * port); 112 | iolink_error_t mock_DL_ReadParam_req (iolink_port_t * port, uint16_t address); 113 | iolink_error_t mock_DL_WriteParam_req ( 114 | iolink_port_t * port, 115 | uint16_t address, 116 | uint8_t value); 117 | iolink_error_t mock_DL_ISDUTransport_req ( 118 | iolink_port_t * port, 119 | iolink_isdu_vl_t * valuelist); 120 | iolink_error_t mock_DL_Control_req ( 121 | iolink_port_t * port, 122 | iolink_controlcode_t controlcode); 123 | iolink_error_t mock_DL_PDOutputGet_req ( 124 | iolink_port_t * port, 125 | uint8_t * len, 126 | uint8_t * data); 127 | iolink_error_t mock_DL_PDOutputUpdate_req ( 128 | iolink_port_t * port, 129 | uint8_t * outputdata); 130 | iolink_error_t mock_DL_PDOutputUpdate ( 131 | iolink_port_t * port, 132 | uint8_t len, 133 | uint8_t * outputdata); 134 | iolink_error_t mock_AL_Read_req ( 135 | iolink_port_t * port, 136 | uint16_t index, 137 | uint8_t subindex, 138 | void (*al_read_cnf_cb) ( 139 | iolink_port_t * port, 140 | uint8_t len, 141 | const uint8_t * data, 142 | iolink_smi_errortypes_t errortype)); 143 | void mock_AL_Read_cnf ( 144 | iolink_port_t * port, 145 | uint8_t len, 146 | const uint8_t * data, 147 | iolink_smi_errortypes_t errortype); 148 | iolink_error_t mock_AL_Write_req ( 149 | iolink_port_t * port, 150 | uint16_t index, 151 | uint8_t subindex, 152 | uint8_t len, 153 | const uint8_t * data, 154 | void (*al_write_cb) (iolink_port_t * port, iolink_smi_errortypes_t errortype)); 155 | void mock_AL_Write_cnf (iolink_port_t * port, iolink_smi_errortypes_t errortype); 156 | void mock_AL_Event_ind ( 157 | iolink_port_t * port, 158 | uint8_t event_cnt, 159 | diag_entry_t events[6]); 160 | iolink_error_t mock_AL_Control_req ( 161 | iolink_port_t * port, 162 | iolink_controlcode_t controlcode); 163 | iolink_error_t mock_AL_Control_ind ( 164 | iolink_port_t * port, 165 | iolink_controlcode_t controlcode); 166 | iolink_error_t mock_AL_SetOutput_req ( 167 | iolink_port_t * port, 168 | uint8_t * data); 169 | iolink_error_t mock_AL_GetInputOutput_req ( 170 | iolink_port_t * port, 171 | uint8_t * len, 172 | uint8_t * data); 173 | iolink_error_t mock_AL_GetInput_req ( 174 | iolink_port_t * port, 175 | uint8_t * len, 176 | uint8_t * data); 177 | iolink_error_t mock_AL_NewInput_ind (iolink_port_t * port); 178 | 179 | void mock_PL_SetMode_req (iolink_port_t * port, iolink_pl_mode_t mode); 180 | void mock_iolink_pl_init (iolink_port_t * port, iolink_hw_drv_t * drv, void * arg); 181 | 182 | iolink_error_t mock_SM_Operate (iolink_port_t * port); 183 | 184 | void mock_SM_PortMode_ind (iolink_port_t * port, iolink_sm_portmode_t mode); 185 | 186 | void mock_DS_Fault (iolink_port_t * port, iolink_ds_fault_t fault); 187 | iolink_error_t mock_DS_Delete (iolink_port_t * port); 188 | iolink_error_t mock_DS_Startup (iolink_port_t * port); 189 | 190 | void mock_DS_Ready (iolink_port_t * port); 191 | 192 | iolink_error_t mock_OD_Start (iolink_port_t * port); 193 | iolink_error_t mock_OD_Stop (iolink_port_t * port); 194 | iolink_error_t mock_PD_Start (iolink_port_t * port); 195 | iolink_error_t mock_PD_Stop (iolink_port_t * port); 196 | 197 | void mock_SMI_cnf ( 198 | void * arg, 199 | uint8_t portnumber, 200 | iolink_arg_block_id_t ref_arg_block_id, 201 | uint16_t arg_block_len, 202 | arg_block_t * arg_block); 203 | 204 | iolink_error_t mock_SM_SetPortConfig_req ( 205 | iolink_port_t * port, 206 | iolink_smp_parameterlist_t * parameterlist); 207 | 208 | bool mock_iolink_post_job (iolink_port_t * port, iolink_job_t * job); 209 | iolink_job_t * mock_iolink_fetch_avail_job (iolink_port_t * port); 210 | iolink_job_t * mock_iolink_fetch_avail_api_job (iolink_port_t * port); 211 | 212 | #ifdef __cplusplus 213 | } 214 | #endif 215 | 216 | #endif /* MOCKS_H */ 217 | -------------------------------------------------------------------------------- /test/test_spi_usb.cpp: -------------------------------------------------------------------------------- 1 | #include "iolink_options.h" 2 | #include "osal.h" 3 | #include 4 | 5 | #include "test_util.h" 6 | #include "osal_spi.h" 7 | 8 | extern "C" uint32_t _iolink_calc_current_transfer_size ( 9 | uint32_t n_bytes_to_transfer, 10 | uint32_t n_bytes_transferred); 11 | 12 | class SPI_USB_Test : public TestBase 13 | { 14 | protected: 15 | // Override default setup 16 | virtual void SetUp() 17 | { 18 | TestBase::SetUp(); // Re-use default setup 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /test/test_util.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2019 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifndef TEST_UTIL_H 17 | #define TEST_UTIL_H 18 | 19 | #include 20 | #include "mocks.h" 21 | #include "iolink_options.h" 22 | #include "iolink_main.h" 23 | 24 | #include /* memset */ 25 | 26 | #ifndef ARRAY_SIZE 27 | #define ARRAY_SIZE(ar) (sizeof (ar) / sizeof (ar[0])) 28 | #endif /* ARRAY_SIZE */ 29 | 30 | #define APP_MASTER_THREAD_STACK_SIZE (4 * 1024) 31 | #define APP_MASTER_THREAD_PRIO 6 32 | #define APP_DL_THREAD_STACK_SIZE 1500 33 | #define APP_DL_THREAD_PRIO (APP_MASTER_THREAD_PRIO + 1) 34 | 35 | typedef struct 36 | { 37 | uint16_t eventcode; 38 | iolink_event_instance_t instance; 39 | iolink_event_mode_t mode; 40 | iolink_event_type_t type; 41 | iolink_event_source_t source; 42 | } al_event_t; 43 | 44 | typedef struct arg_block_test 45 | { 46 | arg_block_t arg_block; 47 | uint8_t data[sizeof (arg_block_pdinout_t) - sizeof (arg_block_t)]; 48 | } arg_block_test_t; 49 | 50 | class TestBase : public ::testing::Test 51 | { 52 | protected: 53 | virtual void SetUp() 54 | { 55 | iolink_port_cfg_t port_cfgs[] = { 56 | { 57 | .name = "/ioltest1/0", 58 | .mode = NULL, 59 | }, 60 | { 61 | .name = "/ioltest1/1", 62 | .mode = NULL, 63 | }, 64 | }; 65 | iolink_m_cfg_t m_cfg = { 66 | .cb_arg = NULL, 67 | .cb_smi = mock_SMI_cnf, 68 | .cb_pd = NULL, 69 | .port_cnt = NELEMENTS (port_cfgs), 70 | .port_cfgs = port_cfgs, 71 | .master_thread_prio = APP_MASTER_THREAD_PRIO, 72 | .master_thread_stack_size = APP_MASTER_THREAD_STACK_SIZE, 73 | .dl_thread_prio = APP_DL_THREAD_PRIO, 74 | .dl_thread_stack_size = APP_DL_THREAD_STACK_SIZE, 75 | }; 76 | m = iolink_m_init (&m_cfg); 77 | 78 | portnumber = 1; 79 | port = iolink_get_port (m, portnumber); 80 | 81 | /* Reset mock variables */ 82 | mock_iolink_revisionid = IOL_DIR_PARAM_REV_V11; 83 | mock_iolink_master_cycletime = 0; 84 | mock_iolink_min_cycletime = 79; 85 | mock_iolink_vendorid = 1171; 86 | mock_iolink_deviceid = 0x112345; 87 | mock_iolink_functionid = 0x999A; 88 | mock_iolink_trans_rate = IOLINK_TRANSMISSION_RATE_COM1; 89 | 90 | mock_iolink_restart_device_cnt = 0; 91 | mock_iolink_mastercmd_master_ident_cnt = 0; 92 | mock_iolink_mastercmd_preop_cnt = 0; 93 | mock_iolink_write_devmode_inactive_cnt = 0; 94 | mock_iolink_write_devmode_operate_cnt = 0; 95 | memset (mock_iolink_al_data, 0, sizeof (mock_iolink_al_data)); 96 | mock_iolink_al_data_len = 0; 97 | mock_iolink_al_data_index = 0; 98 | mock_iolink_al_data_subindex = 0; 99 | mock_iolink_al_read_cnf_cnt = 0; 100 | mock_iolink_al_read_req_cnt = 0; 101 | mock_iolink_al_write_cnf_cnt = 0; 102 | mock_iolink_al_write_req_cnt = 0; 103 | mock_iolink_al_control_req_cnt = 0; 104 | mock_iolink_al_control_ind_cnt = 0; 105 | memset (mock_iolink_dl_pdout_data, 0, sizeof (mock_iolink_dl_pdout_data)); 106 | mock_iolink_dl_pdout_data_len = 0; 107 | memset (mock_iolink_dl_pdin_data, 0, sizeof (mock_iolink_dl_pdin_data)); 108 | mock_iolink_dl_pdin_data_len = 0; 109 | mock_iolink_dl_control_req_cnt = 0; 110 | mock_iolink_dl_eventconf_req_cnt = 0; 111 | mock_iolink_al_event_cnt = 0; 112 | mock_iolink_sm_operate_cnt = 0; 113 | mock_iolink_al_getinput_req_cnt = 0; 114 | mock_iolink_al_getinputoutput_req_cnt = 0; 115 | mock_iolink_al_newinput_inf_cnt = 0; 116 | mock_iolink_ds_delete_cnt = 0; 117 | mock_iolink_ds_startup_cnt = 0; 118 | mock_iolink_ds_ready_cnt = 0; 119 | mock_iolink_ds_fault_cnt = 0; 120 | mock_iolink_ds_fault = IOLINK_DS_FAULT_NONE; 121 | mock_iolink_od_start_cnt = 0; 122 | mock_iolink_od_stop_cnt = 0; 123 | mock_iolink_pd_start_cnt = 0; 124 | mock_iolink_pd_stop_cnt = 0; 125 | mock_iolink_sm_portmode = IOLINK_SM_PORTMODE_INACTIVE; 126 | mock_iolink_al_read_errortype = IOLINK_SMI_ERRORTYPE_NONE; 127 | mock_iolink_al_write_errortype = IOLINK_SMI_ERRORTYPE_NONE; 128 | mock_iolink_controlcode = IOLINK_CONTROLCODE_NONE; 129 | memset (mock_iolink_al_events, 0, sizeof (mock_iolink_al_events)); 130 | memset (&mock_iolink_cfg_paraml, 0, sizeof (mock_iolink_cfg_paraml)); 131 | 132 | mock_iolink_smi_cnf_cnt = 0; 133 | mock_iolink_smi_joberror_cnt = 0; 134 | mock_iolink_smi_portcfg_cnf_cnt = 0; 135 | mock_iolink_smi_portstatus_cnf_cnt = 0; 136 | mock_iolink_smi_portevent_ind_cnt = 0; 137 | mock_iolink_smi_arg_block_len = 0; 138 | mock_iolink_smi_ref_arg_block_id = IOLINK_ARG_BLOCK_ID_MASTERIDENT; 139 | memset (mock_iolink_smi_arg_block, 0, sizeof (arg_block_test_t) + 64); 140 | } 141 | virtual void TearDown() 142 | { 143 | iolink_m_deinit (&m); 144 | } 145 | 146 | iolink_m_t * m = NULL; 147 | iolink_port_t * port; 148 | uint8_t portnumber; 149 | }; 150 | 151 | inline std::string FormatHexInt (int value) 152 | { 153 | std::stringstream ss; 154 | ss << std::hex << std::showbase << value; 155 | return ss.str(); 156 | } 157 | 158 | inline std::string FormatByte (uint8_t value) 159 | { 160 | std::stringstream ss; 161 | ss << std::setfill ('0') << std::setw (2) << std::hex << std::showbase 162 | << static_cast (value); 163 | return ss.str(); 164 | } 165 | 166 | template 167 | ::testing::AssertionResult ArraysMatchN ( 168 | const T (*expected), 169 | const T (*actual), 170 | const size_t size) 171 | { 172 | for (size_t i (0); i < size; ++i) 173 | { 174 | if (expected[i] != actual[i]) 175 | { 176 | return ::testing::AssertionFailure() 177 | << "actual[" << i << "] (" 178 | << FormatByte (static_cast (actual[i])) << ") != expected[" 179 | << i << "] (" << FormatByte (static_cast (expected[i])) 180 | << ")"; 181 | } 182 | } 183 | 184 | return ::testing::AssertionSuccess(); 185 | } 186 | 187 | template 188 | ::testing::AssertionResult ArraysMatch ( 189 | const T (&expected)[size], 190 | const T (&actual)[size]) 191 | { 192 | for (size_t i (0); i < size; ++i) 193 | { 194 | if (expected[i] != actual[i]) 195 | { 196 | return ::testing::AssertionFailure() 197 | << "actual[" << i << "] (" 198 | << FormatByte (static_cast (actual[i])) << ") != expected[" 199 | << i << "] (" << FormatByte (static_cast (expected[i])) 200 | << ")"; 201 | } 202 | } 203 | 204 | return ::testing::AssertionSuccess(); 205 | } 206 | inline ::testing::AssertionResult EventMatch ( 207 | al_event_t * e1, 208 | diag_entry_t * diag, 209 | uint8_t event_cnt) 210 | { 211 | int i; 212 | al_event_t e; 213 | al_event_t * e2 = &e; 214 | 215 | for (i = 0; i < event_cnt; i++) 216 | { 217 | e2->eventcode = diag[i].event_code; 218 | e2->instance = (iolink_event_instance_t) (diag[i].event_qualifier & 0x7); 219 | e2->source = (iolink_event_source_t) ((diag[i].event_qualifier >> 3) & 0x1); 220 | e2->type = (iolink_event_type_t) ((diag[i].event_qualifier >> 4) & 0x3); 221 | e2->mode = (iolink_event_mode_t) ((diag[i].event_qualifier >> 6) & 0x3); 222 | if (e1->eventcode != e2->eventcode) 223 | return ::testing::AssertionFailure() 224 | << i << ": eventcode: (" << e2->eventcode << ") != expected (" 225 | << e1->eventcode << ")"; 226 | if (e1->instance != e2->instance) 227 | return ::testing::AssertionFailure() 228 | << i << ": instance: (" << +e2->instance << ") != expected (" 229 | << +e1->instance << ")"; 230 | if (e1->mode != e2->mode) 231 | return ::testing::AssertionFailure() 232 | << i << ": mode: (" << +e2->mode << ") != expected (" 233 | << +e1->mode << ")"; 234 | if (e1->type != e2->type) 235 | return ::testing::AssertionFailure() 236 | << i << ": type: (" << +e2->type << ") != expected (" 237 | << +e1->type << ")"; 238 | if (e1->source != e2->source) 239 | return ::testing::AssertionFailure() 240 | << i << ": source: (" << e2->source << ") != expected (" 241 | << e1->source << ")"; 242 | e1++; 243 | } 244 | 245 | return ::testing::AssertionSuccess(); 246 | } 247 | 248 | inline ::testing::AssertionResult PortConfigMatch ( 249 | const portconfiglist_t * expected, 250 | const portconfiglist_t * actual) 251 | { 252 | if (expected->portmode != actual->portmode) 253 | return ::testing::AssertionFailure() 254 | << "portmode (" << actual->portmode << ") != expected (" 255 | << expected->portmode << ")"; 256 | if (expected->validation_backup != actual->validation_backup) 257 | return ::testing::AssertionFailure() 258 | << "validation_backup (" << actual->validation_backup 259 | << ") != expected (" << expected->validation_backup << ")"; 260 | if (expected->iq_behavior != actual->iq_behavior) 261 | return ::testing::AssertionFailure() 262 | << "iq_behavior (" << actual->iq_behavior << ") != expected (" 263 | << expected->iq_behavior << ")"; 264 | if (expected->port_cycle_time != actual->port_cycle_time) 265 | return ::testing::AssertionFailure() 266 | << "port_cycle_time (" << actual->port_cycle_time 267 | << ") != expected (" << expected->port_cycle_time << ")"; 268 | if (expected->vendorid != actual->vendorid) 269 | return ::testing::AssertionFailure() 270 | << "vendorid (" << actual->vendorid << ") != expected (" 271 | << expected->vendorid << ")"; 272 | if (expected->deviceid != actual->deviceid) 273 | return ::testing::AssertionFailure() 274 | << "deviceid (" << actual->deviceid << ") != expected (" 275 | << expected->deviceid << ")"; 276 | if (expected->in_buffer_len != actual->in_buffer_len) 277 | return ::testing::AssertionFailure() 278 | << "in_buffer_len (" << actual->in_buffer_len << ") != expected (" 279 | << expected->in_buffer_len << ")"; 280 | if (expected->out_buffer_len != actual->out_buffer_len) 281 | return ::testing::AssertionFailure() 282 | << "out_buffer_len (" << actual->out_buffer_len 283 | << ") != expected (" << expected->out_buffer_len << ")"; 284 | 285 | return ::testing::AssertionSuccess(); 286 | } 287 | 288 | #endif /* TEST_UTIL_H */ 289 | -------------------------------------------------------------------------------- /version.h.in: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * _ _ _ 3 | * _ __ | |_ _ | | __ _ | |__ ___ 4 | * | '__|| __|(_)| | / _` || '_ \ / __| 5 | * | | | |_ _ | || (_| || |_) |\__ \ 6 | * |_| \__|(_)|_| \__,_||_.__/ |___/ 7 | * 8 | * www.rt-labs.com 9 | * Copyright 2021 rt-labs AB, Sweden. 10 | * 11 | * This software is dual-licensed under GPLv3 and a commercial 12 | * license. See the file LICENSE.md distributed with this software for 13 | * full license information. 14 | ********************************************************************/ 15 | 16 | #ifndef VERSION_H 17 | #define VERSION_H 18 | 19 | #define ILINK_VERSION_MAJOR @ILINK_VERSION_MAJOR@ 20 | #define ILINK_VERSION_MINOR @ILINK_VERSION_MINOR@ 21 | #define ILINK_VERSION_PATCH @ILINK_VERSION_PATCH@ 22 | 23 | #endif /* VERSION_H */ 24 | --------------------------------------------------------------------------------