From ef06dec1a561e4ff7e1ac33629fbed52468c50e8 Mon Sep 17 00:00:00 2001 From: fengzero Date: Fri, 7 Jul 2023 07:23:16 +0000 Subject: [PATCH] serial: smart link --- CMakeLists.txt | 6 + include/neuron/connection/neu_smart_link.h | 25 ++ src/connection/connection.c | 6 + src/connection/smart_link.c | 282 +++++++++++++++++++++ 4 files changed, 319 insertions(+) create mode 100644 include/neuron/connection/neu_smart_link.h create mode 100644 src/connection/smart_link.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 52d0b7b4..3750f3f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,12 @@ set(NEURON_BASE_SOURCES src/utils/async_queue.c src/utils/mem_cache.c ${PERSIST_SOURCES}) + +if (SMART_LINK) + message(STATUS "using smart link") + add_definitions(-DNEU_SMART_LINK) + set(NEURON_BASE_SOURCES ${NEURON_BASE_SOURCES} src/connection/smart_link.c) +endif() add_library(neuron-base SHARED) target_sources(neuron-base PRIVATE ${NEURON_BASE_SOURCES} ${NEURON_SRC_PARSE}) diff --git a/include/neuron/connection/neu_smart_link.h b/include/neuron/connection/neu_smart_link.h new file mode 100644 index 00000000..e4ddd540 --- /dev/null +++ b/include/neuron/connection/neu_smart_link.h @@ -0,0 +1,25 @@ +/** + * NEURON IIoT System for Industry 4.0 + * Copyright (C) 2020-2023 EMQ Technologies Co., Ltd All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + **/ + +#ifndef NEURON_CONNECTION_SMART_LINK_H +#define NEURON_CONNECTION_SMART_LINK_H + +int neu_conn_smart_link_auto_set(const char *dev_path); + +#endif \ No newline at end of file diff --git a/src/connection/connection.c b/src/connection/connection.c index b4ea7b43..885b3b19 100644 --- a/src/connection/connection.c +++ b/src/connection/connection.c @@ -854,6 +854,12 @@ static void conn_connect(neu_conn_t *conn) break; } case NEU_CONN_TTY_CLIENT: { +#ifdef NEU_SMART_LINK +#include "connection/neu_smart_link.h" + int ret = + neu_conn_smart_link_auto_set(conn->param.params.tty_client.device); + zlog_notice(conn->param.log, "smart link ret: %d", ret); +#endif struct termios tty_opt = { 0 }; fd = open(conn->param.params.tty_client.device, O_RDWR | O_NOCTTY, 0); diff --git a/src/connection/smart_link.c b/src/connection/smart_link.c new file mode 100644 index 00000000..4d1b4ccb --- /dev/null +++ b/src/connection/smart_link.c @@ -0,0 +1,282 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "json/json.h" + +#include "connection/neu_smart_link.h" + +#define DEVICE_CONF_PATH "/etc/config/device.conf" +#define SERIAL_CONF_PATH "/etc/config/serial.conf" +#define SMART_LINK_PATH "/dev/smartlink" + +#define TYPE 'S' +#define UART_RS232 _IOWR(TYPE, 1, int) +#define UART_RS485 _IOWR(TYPE, 0, int) + +enum SERIAL_MODE { SM_RS232, SM_RS422, SM_RS485 }; + +static int get_port(const char *path); +static int get_port_property(int port, int *mode, int *baudrate); +static int read_file(const char *path, char **buf); +static int set_serial_mode(const char *dev_path, int baudrate, int mode); + +int neu_conn_smart_link_auto_set(const char *dev_path) +{ + int mode = 0; + int baudrate = 0; + int port = get_port(dev_path); + if (port <= 0) { + return -1; + } + printf("get port %d\n", port); + + int ret = get_port_property(port, &mode, &baudrate); + if (ret != 0) { + return -1; + } + + printf("get port property %d %d\n", mode, baudrate); + + ret = set_serial_mode(dev_path, baudrate, mode); + + return ret; +} + +static int read_file(const char *path, char **buf) +{ + FILE *fp = NULL; + + fp = fopen(path, "r"); + if (fp == NULL) { + return -1; + } + + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + rewind(fp); + + *buf = calloc(1, size + 1); + if (buf == NULL) { + fclose(fp); + return -1; + } + + int ret = fread(*buf, 1, size, fp); + if (ret != size) { + fclose(fp); + free(*buf); + return -1; + } + fclose(fp); + + return ret; +} + +static int get_port(const char *dev_path) +{ + json_t * root = NULL; + json_error_t error; + char * buf = NULL; + int ret = read_file(DEVICE_CONF_PATH, &buf); + if (ret <= 0) { + return -1; + } + + root = json_loads(buf, JSON_DECODE_ANY, &error); + if (root == NULL) { + free(buf); + return -1; + } + + neu_json_elem_t e_tty = { + .name = "tty_name", + .t = NEU_JSON_OBJECT, + }; + + ret = neu_json_decode_value(root, &e_tty); + if (ret != 0 || json_is_array((json_t *) e_tty.v.val_object) == false) { + free(buf); + json_decref(root); + return -1; + } + + int index = 0; + json_t *e_dev = NULL; + json_array_foreach(e_tty.v.val_object, index, e_dev) + { + neu_json_elem_t e_dd = { + .name = "device", + .t = NEU_JSON_STR, + }; + + ret = neu_json_decode_value(e_dev, &e_dd); + if (ret != 0) { + free(buf); + json_decref(root); + return -1; + } + + if (strcmp(e_dd.v.val_str, dev_path) == 0) { + neu_json_elem_t e_port = { + .name = "port", + .t = NEU_JSON_INT, + }; + + ret = neu_json_decode_value(e_dev, &e_port); + if (ret != 0) { + free(buf); + json_decref(root); + return -1; + } + + free(buf); + json_decref(root); + free(e_dd.v.val_str); + return e_port.v.val_int; + } + free(e_dd.v.val_str); + } + + free(buf); + json_decref(root); + return -1; +} + +static int get_port_property(int port, int *mode, int *baudrate) +{ + json_t * root = NULL; + json_error_t error; + char * buf = NULL; + int ret = read_file(SERIAL_CONF_PATH, &buf); + if (ret <= 0) { + return -1; + } + + root = json_loads(buf, JSON_DECODE_ANY, &error); + if (root == NULL) { + free(buf); + return -1; + } + + json_t *e_port = json_array_get(root, port - 1); + if (e_port == NULL) { + json_decref(root); + free(buf); + return -1; + } + + neu_json_elem_t elems[] = { + { + .name = "mode", + .t = NEU_JSON_INT, + }, + { + .name = "baudrate", + .t = NEU_JSON_INT, + }, + }; + + ret = neu_json_decode_by_json(e_port, NEU_JSON_ELEM_SIZE(elems), elems); + if (ret != 0) { + free(buf); + json_decref(root); + return -1; + } + + *mode = elems[0].v.val_int; + *baudrate = elems[1].v.val_int; + + json_decref(root); + free(buf); + return 0; +} + +static int set_serial_mode(const char *dev_path, int baudrate, int mode) +{ + static pthread_mutex_t mode_switch_mtx = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&mode_switch_mtx); + + int serial_fd, switch_fd; + int tty_num, para; + int ret = -1; + + struct serial_rs485 conf; + conf.flags &= !SER_RS485_ENABLED; + + switch (baudrate) { + case 38400: + conf.delay_rts_after_send = 1; + break; + case 57600: + conf.delay_rts_after_send = 4; + break; + case 115200: + conf.delay_rts_after_send = 6; + break; + default: + conf.delay_rts_after_send = 0; + break; + } + + if ((serial_fd = open(dev_path, O_RDWR | O_NOCTTY | O_NDELAY)) < 0) { + fprintf(stderr, "open %s failed, fd: %d, strerr: %s\n", dev_path, + serial_fd, strerror(errno)); + goto out; + } + + if (mode != SM_RS232) { + if (ioctl(serial_fd, TIOCMGET, ¶) != 0) { + fprintf(stderr, "ioctl failed, strerr: %s\n", strerror(errno)); + goto out2; + } + if (mode == SM_RS422) { + para |= TIOCM_DTR; + conf.flags |= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND; + } else if (mode == SM_RS485) { + para &= ~TIOCM_DTR; + conf.flags |= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND; + } + if (ioctl(serial_fd, TIOCMSET, ¶) != 0) { + fprintf(stderr, "ioctl failed, strerr: %s\n", strerror(errno)); + goto out2; + } + } + ioctl(serial_fd, TIOCSRS485, &conf); + + if ((switch_fd = open(SMART_LINK_PATH, O_RDWR)) < 0) { + fprintf(stderr, "open %s failed, ret: %d, strerr: %s\n", + SMART_LINK_PATH, switch_fd, strerror(errno)); + goto out2; + } + + sscanf(dev_path, "%*[a-zA-Z/]%d", &tty_num); + printf("tty_num: %d\n", tty_num); + if (mode == SM_RS232) { + ret = ioctl(switch_fd, UART_RS232, tty_num); + } else { + ret = ioctl(switch_fd, UART_RS485, tty_num); + } + if (ret < 0) { + fprintf(stderr, "ioctl failed, ret: %d, strerr: %s\n", ret, + strerror(errno)); + } + + close(switch_fd); +out2: + close(serial_fd); +out: + pthread_mutex_unlock(&mode_switch_mtx); + return ret; +}