/**************************************************************************** * apps/netutils/plcatool/plcatool.c * * SPDX-License-Identifier: Apache-2.0 * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include "oa_tc14.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define ARGS_REMAIN(argc, pos) ((argc) - (pos)) #define PLCATOOL_CFG_SET_BIT 0x8000 #define PLCATOOL_CFG_SET(cfg, field, val) \ do {(cfg)->field = PLCATOOL_CFG_SET_BIT | ((val) & 0xff);} while (0) #define PLCATOOL_CFG_VAL(cfg, field) ((cfg)->field & 0xff) #define NODE_ID_MIN 0 #define NODE_ID_MAX 255 #define NODE_CNT_MIN 1 #define NODE_CNT_MAX 255 #define TO_TMR_MIN 0 #define TO_TMR_MAX 255 #define BURST_CNT_MIN 0 #define BURST_CNT_MAX 255 #define BURST_TMR_MIN 0 #define BURST_TMR_MAX 255 /**************************************************************************** * Private Types ****************************************************************************/ enum plcatool_cmd_e { PLCA_CMD_SET, PLCA_CMD_GET, PLCA_CMD_STATUS, PLCA_CMD_HELP }; /* Lower 8 bits are value, the most significant bit indicates whether set */ struct plcatool_cfg_s { enum plcatool_cmd_e cmd; FAR char *ifname; uint8_t phy; uint16_t enable; uint16_t node_cnt; uint16_t node_id; uint16_t to_tmr; uint16_t burst_cnt; uint16_t burst_tmr; }; /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int get_num(FAR const char *str, FAR int *result); static int parse_args(int argc, FAR char *argv[], FAR struct plcatool_cfg_s *cfg); static int write_plca_mmd(FAR const char *ifname, int phy, uint16_t address, uint16_t data); static int read_plca_mmd(FAR const char *ifname, int phy, uint16_t address, FAR uint16_t *data); static int verify_support(FAR struct plcatool_cfg_s *cfg, FAR bool *supported); static int plcatool_set(FAR struct plcatool_cfg_s *cfg); static int plcatool_get(FAR struct plcatool_cfg_s *cfg); static int plcatool_status(FAR struct plcatool_cfg_s *cfg); static void plcatool_usage(bool err); /**************************************************************************** * Private Functions ****************************************************************************/ static int get_num(FAR const char *str, FAR int *result) { FAR char *endptr; *result = (int)strtol(str, &endptr, 0); if (*endptr != '\0') { return ERROR; } return OK; } static int parse_args(int argc, FAR char *argv[], FAR struct plcatool_cfg_s *cfg) { FAR const char *cmd; int argpos = 1; if (argc < 2) { return ERROR; } if (strcmp(argv[argpos++], "--phy") == 0) { int phynum; if (ARGS_REMAIN(argc, argpos) < 3) /* at least phynum, cmd, intf */ { return ERROR; } if (get_num(argv[argpos++], &phynum)) { return ERROR; } if (0 <= phynum && phynum <= 31) { cfg->phy = phynum; } else { return ERROR; } } else { argpos = 1; cfg->phy = 0; } cmd = argv[argpos++]; if (strcmp(cmd, "set") == 0) { cfg->cmd = PLCA_CMD_SET; if (ARGS_REMAIN(argc, argpos) < 3) /* name, at least one param-value */ { return ERROR; } cfg->ifname = argv[argpos++]; while (ARGS_REMAIN(argc, argpos) >= 2) /* at least one param-value */ { FAR const char *param = argv[argpos++]; FAR const char *value = argv[argpos++]; if (strcmp(param, "enable") == 0) { if (cfg->enable) { return ERROR; } if (strcmp(value, "on") == 0) { PLCATOOL_CFG_SET(cfg, enable, 1); } else if (strcmp(value, "off") == 0) { PLCATOOL_CFG_SET(cfg, enable, 0); } else { return ERROR; } } else if (strcmp(param, "node-id") == 0) { int n; if (cfg->node_id) { return ERROR; } if (get_num(value, &n)) { return ERROR; } if (NODE_ID_MIN <= n && n <= NODE_ID_MAX) { PLCATOOL_CFG_SET(cfg, node_id, n); } else { fprintf(stderr, "node-id out of range\n"); return ERROR; } } else if (strcmp(param, "node-cnt") == 0) { int n; if (cfg->node_cnt) { return ERROR; } if (get_num(value, &n)) { fprintf(stderr, "Not a valid integer\n"); return ERROR; } if (NODE_CNT_MIN <= n && n <= NODE_CNT_MAX) { PLCATOOL_CFG_SET(cfg, node_cnt, n); } else { fprintf(stderr, "node-cnt out of range\n"); return ERROR; } } else if (strcmp(param, "to-tmr") == 0) { int n; if (cfg->to_tmr) { return ERROR; } if (get_num(value, &n)) { fprintf(stderr, "Not a valid integer\n"); return ERROR; } if (TO_TMR_MIN <= n && n <= TO_TMR_MAX) { PLCATOOL_CFG_SET(cfg, to_tmr, n); } else { fprintf(stderr, "to_tmr out of range\n"); return ERROR; } } else if (strcmp(param, "burst-cnt") == 0) { int n; if (cfg->burst_cnt) { return ERROR; } if (get_num(value, &n)) { fprintf(stderr, "Not a valid integer\n"); return ERROR; } if (BURST_CNT_MIN <= n && n <= BURST_CNT_MAX) { PLCATOOL_CFG_SET(cfg, burst_cnt, n); } else { fprintf(stderr, "burst-cnt out of range\n"); return ERROR; } } else if (strcmp(param, "burst-tmr") == 0) { int n; if (cfg->burst_tmr) { return ERROR; } if (get_num(value, &n)) { fprintf(stderr, "Not a valid integer\n"); return ERROR; } if (BURST_TMR_MIN <= n && n <= BURST_TMR_MAX) { PLCATOOL_CFG_SET(cfg, burst_tmr, n); } else { fprintf(stderr, "burst-tmr out of range\n"); return ERROR; } } else { return ERROR; } } if (ARGS_REMAIN(argc, argpos) != 0) { return ERROR; } } else if (strcmp(cmd, "get") == 0) { cfg->cmd = PLCA_CMD_GET; if (ARGS_REMAIN(argc, argpos) != 1) /* ifname */ { return ERROR; } cfg->ifname = argv[argpos++]; if (strlen(cfg->ifname) > IFNAMSIZ) { fprintf(stderr, "No such interface\n"); return ERROR; } } else if (strcmp(cmd, "status") == 0) { cfg->cmd = PLCA_CMD_STATUS; if (ARGS_REMAIN(argc, argpos) != 1) /* ifname */ { return ERROR; } cfg->ifname = argv[argpos++]; if (strlen(cfg->ifname) > IFNAMSIZ) { fprintf(stderr, "No such interface\n"); return ERROR; } } else if (strcmp(cmd, "-h") == 0) { cfg->cmd = PLCA_CMD_HELP; if (ARGS_REMAIN(argc, argpos) != 0) { return ERROR; } } else { return ERROR; } return OK; } static int write_plca_mmd(FAR const char *ifname, int phy, uint16_t address, uint16_t data) { struct ifreq req; int retval; int sockfd = socket(AF_INET, SOCK_DGRAM, 0); strcpy(req.ifr_name, ifname); req.ifr_ifru.ifru_mii_data.phy_id = mdio_phy_id_c45(OA_TC14_PLCA_MMD, phy); req.ifr_ifru.ifru_mii_data.reg_num = address; req.ifr_ifru.ifru_mii_data.val_in = data; retval = ioctl(sockfd, SIOCSMIIREG, (unsigned long)(&req)); if (retval) { close(sockfd); fprintf(stderr, "Write unsuccessful\n"); return ERROR; } close(sockfd); return OK; } static int read_plca_mmd(FAR const char *ifname, int phy, uint16_t address, FAR uint16_t *data) { struct ifreq req; int retval; int sockfd = socket(AF_INET, SOCK_DGRAM, 0); strcpy(req.ifr_name, ifname); req.ifr_ifru.ifru_mii_data.phy_id = mdio_phy_id_c45(OA_TC14_PLCA_MMD, phy); req.ifr_ifru.ifru_mii_data.reg_num = address; retval = ioctl(sockfd, SIOCGMIIREG, (unsigned long)(&req)); if (retval) { close(sockfd); fprintf(stderr, "Read unsuccessful\n"); return ERROR; } *data = req.ifr_ifru.ifru_mii_data.val_out; close(sockfd); return OK; } static int verify_support(FAR struct plcatool_cfg_s *cfg, FAR bool *supported) { uint16_t reg; if (read_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_IDVER_ADDR, ®)) { return ERROR; } *supported = reg == OA_TC14_IDVER_VAL ? true : false; return OK; } static int plcatool_set(FAR struct plcatool_cfg_s *cfg) { uint16_t reg; bool supported; /* Verify the correct address map code */ if (verify_support(cfg, &supported)) { return EIO; } if (!supported) { fprintf(stderr, "Device not supported\n"); return EINVAL; } /* node-cnt, node-id */ if (cfg->node_cnt || cfg->node_id) { if (read_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_CTRL1_ADDR, ®)) { return EIO; } if (cfg->node_cnt) { int val = PLCATOOL_CFG_VAL(cfg, node_cnt); reg &= ~OA_TC14_CTRL1_NCNT_MASK; reg |= oa_tc14_field(val, CTRL1_NCNT); } if (cfg->node_id) { int val = PLCATOOL_CFG_VAL(cfg, node_id); reg &= ~OA_TC14_CTRL1_ID_MASK; reg |= oa_tc14_field(val, CTRL1_ID); } if (write_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_CTRL1_ADDR, reg)) { return EIO; } } /* to-tmr */ if (cfg->to_tmr) { int val = PLCATOOL_CFG_VAL(cfg, to_tmr); reg = oa_tc14_field(val, TOTMR_TOT); if (write_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_TOTMR_ADDR, reg)) { return EIO; } } /* burst-cnt, burst-tmr */ if (cfg->burst_cnt || cfg->burst_tmr) { if (read_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_BURST_ADDR, ®)) { return EIO; } if (cfg->burst_cnt) { int val = PLCATOOL_CFG_VAL(cfg, burst_cnt); reg &= ~OA_TC14_BURST_MAXBC_MASK; reg |= oa_tc14_field(val, BURST_MAXBC); } if (cfg->burst_tmr) { int val = PLCATOOL_CFG_VAL(cfg, burst_tmr); reg &= ~OA_TC14_BURST_BTMR_MASK; reg |= oa_tc14_field(val, BURST_BTMR); } if (write_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_BURST_ADDR, reg)) { return EIO; } } /* enable */ if (cfg->enable) { int val; if (read_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_CTRL0_ADDR, ®)) { return EIO; } val = PLCATOOL_CFG_VAL(cfg, enable); reg &= ~OA_TC14_CTRL0_EN_MASK; reg |= oa_tc14_field(val, CTRL0_EN); if (write_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_CTRL0_ADDR, reg)) { return EIO; } read_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_CTRL0_ADDR, ®); } return OK; } static int plcatool_get(FAR struct plcatool_cfg_s *cfg) { uint16_t ctrl0; uint16_t ctrl1; uint16_t totmr; uint16_t burst; bool supported; /* Verify the correct address map code */ if (verify_support(cfg, &supported)) { return EIO; } if (!supported) { fprintf(stderr, "Device not supported\n"); return EINVAL; } if (read_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_CTRL0_ADDR, &ctrl0) || read_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_CTRL1_ADDR, &ctrl1) || read_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_TOTMR_ADDR, &totmr) || read_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_BURST_ADDR, &burst)) { return EIO; } cfg->enable = oa_tc14_get_field(ctrl0, CTRL0_EN); cfg->node_id = oa_tc14_get_field(ctrl1, CTRL1_ID); cfg->node_cnt = oa_tc14_get_field(ctrl1, CTRL1_NCNT); cfg->to_tmr = oa_tc14_get_field(totmr, TOTMR_TOT); cfg->burst_cnt = oa_tc14_get_field(burst, BURST_MAXBC); cfg->burst_tmr = oa_tc14_get_field(burst, BURST_BTMR); printf("PLCA settings for %s\n", cfg->ifname); printf("\tEnabled: %s\n", cfg->enable ? "Yes" : "No"); printf("\tlocal node ID: %d (%s)\n", cfg->node_id, cfg->node_id == 0 ? "coordinator" : (cfg->node_id == 255 ? "unconfigured" : "follower")); printf("\tNode count: %d%s\n", cfg->node_cnt, cfg->node_id ? " (ignored)" : ""); printf("\tTO timer: %d BT\n", cfg->to_tmr); printf("\tBurst count: %d (%s)\n", cfg->burst_cnt, cfg->burst_cnt > 0 ? "enabled" : "disabled"); printf("\tBurst timer: %d BT\n", cfg->burst_tmr); return OK; } static int plcatool_status(FAR struct plcatool_cfg_s *cfg) { uint16_t status; bool supported; /* Verify the correct address map code */ if (verify_support(cfg, &supported)) { return EIO; } if (!supported) { fprintf(stderr, "Device not supported\n"); return EINVAL; } if (read_plca_mmd(cfg->ifname, cfg->phy, OA_TC14_STATUS_ADDR, &status)) { return EIO; } printf("PLCA status of %s\n", cfg->ifname); printf("\tStatus: %s\n", oa_tc14_get_field(status, STATUS_PST) ? "on" : "off"); return OK; } static void plcatool_usage(bool err) { FAR FILE *out = err ? stderr : stdout; fprintf(out, "Usage:\n"); fprintf(out, " plcatool [--phy N] status \n"); fprintf(out, " plcatool [--phy N] get \n"); fprintf(out, " plcatool [--phy N] set " "[ ] ...\n"); fprintf(out, " Accepted pairs:\n"); fprintf(out, " enable on | off\n"); fprintf(out, " node-id N in [%d .. %d]\n", NODE_ID_MIN, NODE_ID_MAX); fprintf(out, " node-cnt N in [%d .. %d]\n", NODE_CNT_MIN, NODE_CNT_MAX); fprintf(out, " to-tmr N in [%d .. %d]\n", TO_TMR_MIN, TO_TMR_MAX); fprintf(out, " burst-cnt N in [%d .. %d]\n", BURST_CNT_MIN, BURST_CNT_MAX); fprintf(out, " burst-tmr N in [%d .. %d]\n", BURST_TMR_MIN, BURST_TMR_MAX); fprintf(out, " plcatool -h\n"); fprintf(out, " --phy N defaults to 0\n"); } /**************************************************************************** * Public Functions ****************************************************************************/ int main(int argc, FAR char *argv[]) { struct plcatool_cfg_s cfg; memset(&cfg, 0, sizeof(cfg)); int err = parse_args(argc, argv, &cfg); if (err) { plcatool_usage(err); return EINVAL; } switch (cfg.cmd) { case PLCA_CMD_SET: return plcatool_set(&cfg); case PLCA_CMD_GET: return plcatool_get(&cfg); case PLCA_CMD_STATUS: return plcatool_status(&cfg); case PLCA_CMD_HELP: plcatool_usage(false); break; } return OK; }