/********************************************************************\ Name: submaster.c Created by: Stefan Ritt Contents: MSCB program for Cygnal Ethernet sub-master running on Cygnal C8051F120 \********************************************************************/ #include #include #include #include #include "mscbemb.h" #include "net.h" #include "git-revision.h" #include "fe_cc.h" #define SUBM_VERSION 5 // used for PC-Submaster communication char code git_revision[] = GIT_REVISION; unsigned int idata git_revision_int; /*------------------------------------------------------------------*/ #define MSCB_NET_PORT 1177 char host_name[20]; // used for DHCP char password[20]; // used for access control /* our current MAC address */ unsigned char eth_src_hw_addr[ETH_ADDR_LEN]; typedef struct { unsigned short size; unsigned short seq_num; unsigned char flags; unsigned char version; } UDP_HEADER; extern bit submaster_configured; // set after being configured bit dhcp_configured; // set if we have a valid IP address unsigned char exclusive_addr[4]; // used for exclusive access (download) unsigned short exclusive_port; unsigned short exclusive_timer; // timer to reset exclusive access unsigned char addr_mode; sbit CS8900A_RESET = P0 ^ 5; sbit RS485_ENABLE = RS485_EN_PIN; bit addressed; // true if node addressed bit reboot; // flag used to reboot bit master; // distiguish between RS485 client/master extern bit flash_client; // used for EEPROM flashing extern bit flash_program; // used for upgrading firmware extern bit mem_command; // used for read/write memory commands unsigned char n_interrupt; // counter for RS232 interrupts sbit led_1 = LED_1; /* Address modes */ #define ADDR_NONE 0 #define ADDR_NODE 1 #define ADDR_GROUP 2 #define ADDR_ALL 3 /*------------------------------------------------------------------*/ void hardware_init(void); int udp_send(char socket_no, int size); unsigned short interprete(unsigned char *buf, unsigned char *rb) reentrant; unsigned char rs485_send_master(unsigned char len, unsigned char flags); unsigned char rs485_send_client(unsigned char len); unsigned char get_n_interrupt(); extern MSCB_INFO_VAR *variables; unsigned char g_n_variables, g_var_size; extern void user_write(unsigned char index, unsigned char *buf) reentrant; extern unsigned char user_read(unsigned char index, unsigned char *buf) reentrant; extern CLI_CFG cli_cfg; extern unsigned char board_present(unsigned char subadr); /*------------------------------------------------------------------*/ #define UDP_TX_SIZE 1030 // 1024+6 bytes header #define UDP_RX_SIZE 275 // 256+13+6 bytes header (MCMD_WRITE_MEM) #define RS485_TX_SIZE 269 // 256+13 #define RS485_RX_SIZE 1024 unsigned char udp_tx_buf[UDP_TX_SIZE]; unsigned char udp_rx_buf[UDP_RX_SIZE]; unsigned char rs485_tx_buf[RS485_TX_SIZE]; unsigned char rs485_rx_buf[RS485_RX_SIZE]; unsigned char rs485_tx_bit9[4]; unsigned char n_udp_rx, n_rs485_tx, i_rs485_tx; unsigned short i_rs485_rx; unsigned char invert_led = 0; /*------------------------------------------------------------------*/ void submaster_configure() { SUBM_CFG code *subm_cfg; SFRPAGE = LEGACY_PAGE; subm_cfg = 0x00; // first sector PSCTL = 0x04; // select scratchpad area submaster_configured = (subm_cfg->magic == 0x1234); PSCTL = 0x00; // unselect scratchpad area if (!submaster_configured) { /* set default values for host name and MAC address */ strcpy(host_name, "MSCBFFF"); eth_src_hw_addr[0] = 0x00; eth_src_hw_addr[1] = 0x50; eth_src_hw_addr[2] = 0xC2; eth_src_hw_addr[3] = 0x46; eth_src_hw_addr[4] = 0xDF; eth_src_hw_addr[5] = 0xFF; password[0] = 0; } else { PSCTL = 0x04; // select scratchpad area strcpy(host_name, subm_cfg->host_name); strcpy(password, subm_cfg->password); memcpy(eth_src_hw_addr, subm_cfg->eth_mac_addr, ETH_ADDR_LEN); PSCTL = 0x00; // unselect scratchpad area } exclusive_port = 0; dhcp_configured = 0; /* count variables */ for (g_n_variables = 0; variables[g_n_variables].width > 0 ; g_n_variables++); } /*------------------------------------------------------------------*/ unsigned char flash_write_subm(SUBM_CFG *new_cfg) { SUBM_CFG xdata * idata subm_cfg_write; if (new_cfg->magic != 0x1234) return 0; DISABLE_INTERRUPTS; SFRPAGE = LEGACY_PAGE; /* Disable watchdog timer */ watchdog_disable(); /* erase scratchpad area */ subm_cfg_write = 0x00; // first sector FLSCL = FLSCL | 1; // enable flash writes/erases PSCTL = 0x07; // allow write and erase to scratchpad area subm_cfg_write->host_name[0] = 0; /* program scratchpad area */ PSCTL = 0x05; // allow write to scratchpad area memcpy(subm_cfg_write, new_cfg, sizeof(SUBM_CFG)); FLSCL = FLSCL & ~1; // disable flash writes/erases PSCTL = 0x00; // unselect scratchpad area ENABLE_INTERRUPTS; watchdog_enable(10); /* read back configuration */ submaster_configure(); return 1; } /*------------------------------------------------------------------*/ sbit led = LED_0; void serial_int(void) interrupt 4 using 1 { unsigned short cmd_len, n; if (TI0) { TI0 = 0; /* transmit next unsigned char */ i_rs485_tx++; if (i_rs485_tx < n_rs485_tx) { /* set bit9 according to array */ if (i_rs485_tx < 4 && rs485_tx_bit9[i_rs485_tx]) TB80 = 1; else TB80 = 0; DELAY_US(INTERCHAR_DELAY); SBUF0 = rs485_tx_buf[i_rs485_tx]; } else { /* end of buffer */ RS485_ENABLE = 0; i_rs485_tx = n_rs485_tx = 0; } } // submaster code if (RI0 && master) { /* put received unsigned char into buffer */ if (i_rs485_rx < RS485_RX_SIZE) rs485_rx_buf[i_rs485_rx++] = SBUF0; RI0 = 0; } // client code if (RI0 && !master) { if (!RB80 && !addressed) { RI0 = 0; i_rs485_rx = 0; return; // discard data if not bit9 and not addressed } RB80 = 0; rs485_rx_buf[i_rs485_rx++] = SBUF0; RI0 = 0; /* check for padding character */ if (i_rs485_rx == 1 && rs485_rx_buf[0] == 0) { i_rs485_rx = 0; return; } // obtain command length n = rs485_rx_buf[0] & 0x07; // length is three LSB if (n == 7 && i_rs485_rx > 1) { n = rs485_rx_buf[1]+3; // variable length acknowledge one byte if ((n & 0x80) && i_rs485_rx > 2) n = (rs485_rx_buf[1] & ~0x80) << 8 | (rs485_rx_buf[2]) + 4; // two bytes } else n += 2; // add CMD and CRC cmd_len = n; if (i_rs485_rx == RS485_RX_SIZE) { // check for buffer overflow i_rs485_rx = 0; return; // don't interprete command } if (i_rs485_rx < cmd_len) // return if command not yet complete return; if (rs485_rx_buf[i_rs485_rx - 1] != crc8(rs485_rx_buf, i_rs485_rx - 1)) { i_rs485_rx = 0; return; // return if CRC code does not match } if (rs485_rx_buf[0] == MCMD_WRITE_MEM || rs485_rx_buf[0] == MCMD_READ_MEM) { // send command to main loop mem_command = 1; i_rs485_rx = 0; return; } // signal incoming data if (addressed) led_blink(0, 1, 50); n_interrupt++; n = interprete(rs485_rx_buf, rs485_tx_buf); // interprete command if (n > 0) { // signal outgoing data led_blink(1, 1, 50); rs485_send_client(n); } if (reboot) { /* reboot */ SFRPAGE = LEGACY_PAGE; RSTSRC = 0x10; } i_rs485_rx = 0; } } /*------------------------------------------------------------------*/ /* dummy to be called by getchar() */ void yield() { } /*------------------------------------------------------------------*/ /* return nubmer of interrupts (needed for 1-wire readout) */ unsigned char get_n_interrupt() { return n_interrupt; } /*------------------------------------------------------------------*/ extern void watchdog_int(void) reentrant; /* wd_refresh gets called from 100Hz timer interrupt inside netlib.lib */ void wd_refresh() { /* pass call to watchdog_int in mscbutil.c */ watchdog_int(); /* check exclusive mode timeout */ if (exclusive_timer > 0) exclusive_timer--; if (exclusive_timer == 0) exclusive_port = 0; } /*------------------------------------------------------------------*/ #define A2HEX(x) (x >= '0' && x <= '9' ? x - '0' : (x >= 'a' && x <= 'f' ? x - 'a' + 10 : 0)) void git_revision_conv() { char *p; for (p = git_revision ; *p != 0 ; p++); for ( ; p > git_revision && *p != '-' ; p--); if (p == git_revision) return; p += 2; git_revision_int = A2HEX(p[0])<<12 | A2HEX(p[1])<<8 | A2HEX(p[2])<<4 | A2HEX(p[3]); } unsigned char execute(char socket_no, unsigned char *buf, unsigned char *rbuf) { unsigned long xdata up_time; if (buf[0] == MCMD_INIT) { reboot = 1; return 0; } if (buf[0] == MCMD_ECHO) { /* return echo */ rbuf[0] = MCMD_ACK; rbuf[1] = SUBM_VERSION; rbuf[2] = git_revision_int >> 8; rbuf[3] = git_revision_int & 0xFF; up_time = uptime(); rbuf[7] = up_time & 0xFF; up_time >>= 8; rbuf[6] = up_time & 0xFF; up_time >>= 8; rbuf[5] = up_time & 0xFF; up_time >>= 8; rbuf[4] = up_time & 0xFF; up_time >>= 8; return 8; } if (buf[0] == MCMD_TOKEN) { /* check password */ if (password[0] == 0 || strcmp(buf+1, password) == 0) rbuf[0] = MCMD_ACK; else rbuf[0] = 0xFF; return 1; } if (buf[0] == MCMD_FLASH) { /* update configuration in flash */ if (flash_write_subm((void*)(buf+1))) { rbuf[0] = MCMD_ACK; rbuf[1] = 0; // reserved for future use reboot = 1; return 2; } else { rbuf[0] = 0xFF; return 1; } } if (buf[0] == MCMD_FREEZE) { PSOCKET_INFO socket_ptr; socket_ptr = &sock_info[socket_no]; if (buf[1] == 1) { memcpy(exclusive_addr, socket_ptr->ip_dest_addr, 4); exclusive_port = socket_ptr->dest_port; exclusive_timer = 1000; // expires after 10 sec. } else { exclusive_port = 0; exclusive_timer = 0; } rbuf[0] = MCMD_ACK; return 1; } return 0; } /*------------------------------------------------------------------*/ unsigned char rs485_send_master(unsigned char len, unsigned char flags) { unsigned char i, j; /* clear receive buffer */ i_rs485_rx = 0; /* send buffer to RS485 */ SFRPAGE = UART0_PAGE; memset(rs485_tx_bit9, 0, sizeof(rs485_tx_bit9)); /* set all bit9 if BIT9 flag */ if (flags & RS485_FLAG_BIT9) for (i=0 ; i rx_old) { i = 0; rx_old = i_rs485_rx; } /* check for PING acknowledge (single unsigned char) */ if (rs485_tx_buf[0] == MCMD_PING16 && i_rs485_rx == 1 && rs485_rx_buf[0] == MCMD_ACK) { memcpy(rbuf, rs485_rx_buf, 1); return 1; } /* check for READ error acknowledge */ if ((rs485_tx_buf[0] == MCMD_READ+1 || (rs485_tx_buf[0] == MCMD_ADDR_NODE16 && rs485_tx_buf[4] == MCMD_READ+1)) && i_rs485_rx == 1 && rs485_rx_buf[0] == MCMD_ACK) { memcpy(rbuf, rs485_rx_buf, 1); return 1; } /* check for READ range error acknowledge */ if ((rs485_tx_buf[0] == MCMD_READ+2 || (rs485_tx_buf[0] == MCMD_ADDR_NODE16 && rs485_tx_buf[4] == MCMD_READ+2)) && i_rs485_rx == 1 && rs485_rx_buf[0] == MCMD_ACK) { memcpy(rbuf, rs485_rx_buf, 1); return 1; } /* check for normal acknowledge */ if (i_rs485_rx > 0 && (rs485_rx_buf[0] & MCMD_ACK) == MCMD_ACK) { n = rs485_rx_buf[0] & 0x07; // length is three LSB if (n == 7 && i_rs485_rx > 1) { n = rs485_rx_buf[1]+3; // variable length acknowledge one byte if ((n & 0x80) && i_rs485_rx > 2) n = (rs485_rx_buf[1] & ~0x80) << 8 | (rs485_rx_buf[2]) + 4; // two bytes } else n += 2; // add ACK and CRC if (i_rs485_rx == n) { memcpy(rbuf, rs485_rx_buf, n); return n; } } watchdog_refresh(0); delay_us(100); } /* send timeout */ if (i == to) { rbuf[0] = 0xFF; return 1; } return 0; } /*------------------------------------------------------------------*/ void open_udp_socket() { signed char socket_no; PSOCKET_INFO socket_ptr; socket_no = mn_open(null_addr, MSCB_NET_PORT, 0, NO_OPEN, PROTO_UDP, STD_TYPE, udp_rx_buf, UDP_RX_SIZE); socket_ptr = &sock_info[socket_no]; socket_ptr->send_ptr = udp_tx_buf; socket_ptr->send_len = UDP_TX_SIZE - 1; } /*------------------------------------------------------------------*/ int udp_receive(unsigned char **data_ptr, char *socket_no_ptr) { unsigned char packet_type; PSOCKET_INFO socket_ptr; UDP_HEADER *pudp; int recvd; unsigned char dhcp_state; /* check DHCP status, renew if expired */ dhcp_state = dhcp_lease.dhcp_state; if (dhcp_state == DHCP_DEAD) return (DHCP_LEASE_EXPIRED); /* renew with the previous lease time */ if (dhcp_state == DHCP_RENEWING || dhcp_state == DHCP_REBINDING) (void) mn_dhcp_renew(dhcp_lease.org_lease_time); /* receive packet */ packet_type = mn_ip_recv(); if (packet_type & UDP_TYPE) { /* prepare socket 0 to accept packets from any destination */ sock_info[0].dest_port = 0; memset(sock_info[0].ip_dest_addr, 0, IP_ADDR_LEN); memset(udp_rx_buf, 0, 16); recvd = mn_udp_recv(&socket_ptr); if (socket_ptr == NULL) return 0; pudp = (UDP_HEADER *)socket_ptr->recv_ptr; /* check correct size */ if (pudp->size + sizeof(UDP_HEADER) != recvd) return 0; /* check correct version */ if (pudp->version != SUBM_VERSION && *((unsigned char *)(pudp+1)) != MCMD_ECHO) { return 0; } /* store our own sequence number */ socket_ptr->SEG_SEQ.NUMW[0] = pudp->seq_num; *data_ptr = socket_ptr->recv_ptr; *socket_no_ptr = socket_ptr->socket_no; return socket_ptr->recv_len; } return 0; } /*------------------------------------------------------------------*/ int udp_send(unsigned char socket_no, int size) { PSOCKET_INFO socket_ptr; UDP_HEADER *pudp; unsigned int n; if (socket_no < 0 || socket_no >= NUM_SOCKETS) return 0; if (size > UDP_TX_SIZE) return 0; socket_ptr = &sock_info[socket_no]; /* set-up UDP header */ pudp = (UDP_HEADER *)udp_tx_buf; pudp->version = SUBM_VERSION; pudp->size = size; pudp->seq_num = socket_ptr->SEG_SEQ.NUMW[0]; pudp->flags = 0; /* copy data to tx buffer */ socket_ptr->send_len = size+sizeof(UDP_HEADER); n = mn_udp_send(socket_ptr); if (n < sizeof(UDP_HEADER)) return n; return n-sizeof(UDP_HEADER); } /*------------------------------------------------------------------*/ void submaster_init(void) { addressed = 0; flash_program = 0; reboot = 0; master = 0; i_rs485_rx = 0; git_revision_conv(); // read configuration from flash submaster_configure(); // initialize the CMX Micronet variables // also needed if we don't have Ethernet since // it initializes the periodic timer mn_init(); #ifdef HAVE_ETHERNET open_udp_socket(); #endif } /*------------------------------------------------------------------*/ unsigned short interprete_mem(unsigned char *buf, unsigned char *rb, int rb_size) { unsigned short i, n, buflen; unsigned long adr, bufsize; unsigned char subadr; buflen = (buf[0] & 0x07); if (buflen == 7) { if (buf[1] & 0x80) // two byte legnth buflen = ((buf[1] & 0x7F) << 8 | buf[2]) + 4; // add command, length and CRC else buflen = (buf[1] & 0x7F) + 3; // add command, length and CRC } else buflen += 2; // add command and CRC // check CRC if (crc8(buf, buflen-1) != buf[buflen-1]) return 0; n = 0; if (buf[0] == MCMD_WRITE_MEM) { bufsize = buflen - 9; // minus cmd, len, subadr, adr and CRC subadr = buf[3]; adr = buf[4]; adr = (adr << 8) | buf[5]; adr = (adr << 8) | buf[6]; adr = (adr << 8) | buf[7]; /* check for RAM access */ if ((adr >> 28) == (MSCB_BASE_RAM >> 28)) { adr = adr & 0x0FFFFFFF; backplane_spi_cs(0); gpio_board_select(subadr); if (((adr >> 16) & 0x0FFF) > 0) { backplane_spi_write_msb(WD_SPI_WRITE32); backplane_spi_write_msb((adr >> 24) & 0xFF); backplane_spi_write_msb((adr >> 16) & 0xFF); backplane_spi_write_msb((adr >> 8) & 0xFF); backplane_spi_write_msb((adr >> 0) & 0xFF); } else if (((adr >> 8) & 0xFF) > 0) { backplane_spi_write_msb(WD_SPI_WRITE16); backplane_spi_write_msb((adr >> 8) & 0xFF); backplane_spi_write_msb((adr >> 0) & 0xFF); } else { backplane_spi_write_msb(WD_SPI_WRITE8); backplane_spi_write_msb((adr >> 0) & 0xFF); } for (i=0 ; i rb_size) return 0; memset(rb+3, 0, bufsize); /* check for RAM access */ if ((adr >> 28) == (MSCB_BASE_RAM >> 28)) { adr = adr & 0x0FFFFFFF; backplane_spi_cs(0); gpio_board_select(subadr); if (((adr >> 16) & 0x0FFF) > 0) { backplane_spi_write_msb(WD_SPI_READ32); backplane_spi_write_msb((adr >> 24) & 0xFF); backplane_spi_write_msb((adr >> 16) & 0xFF); backplane_spi_write_msb((adr >> 8) & 0xFF); backplane_spi_write_msb((adr >> 0) & 0xFF); } else if (((adr >> 8) & 0xFF) > 0) { backplane_spi_write_msb(WD_SPI_READ16); backplane_spi_write_msb((adr >> 8) & 0xFF); backplane_spi_write_msb((adr >> 0) & 0xFF); } else { backplane_spi_write_msb(WD_SPI_READ8); backplane_spi_write_msb((adr >> 0) & 0xFF); } backplane_spi_write_msb(bufsize & 0xFF); for (i=0 ; i> 8) & 0x7F); rb[2] = bufsize & 0xFF; rb[3+bufsize] = crc8(rb, bufsize+3); n = bufsize+4; } else return 0; } return n; } /*------------------------------------------------------------------*/ // called from main loop to process memory read/write over SPI void process_mem_command(void) { unsigned short n; n = interprete_mem(rs485_rx_buf, rs485_tx_buf, sizeof(rs485_tx_buf)); if (n > 0) { // signal outgoing data led_blink(1, 1, 50); rs485_send_client1(n); } } /*------------------------------------------------------------------*/ unsigned short interprete(unsigned char *buf, unsigned char *rb) reentrant { unsigned char i, j, size, n, ch; unsigned short buflen; unsigned long u; buflen = (buf[0] & 0x07); if (buflen == 7) { if (buf[1] & 0x80) // two byte legnth buflen = ((buf[1] & 0x7F) << 8 | buf[2]) + 4; // add command, length and CRC else buflen = (buf[1] & 0x7F) + 3; // add command, length and CRC } else buflen += 2; // add command and CRC // check CRC if (crc8(buf, buflen-1) != buf[buflen-1]) return 0; n = 0; switch (buf[0]) { case MCMD_ADDR_NODE16: case MCMD_PING16: if ((((unsigned short)buf[1] << 8) | buf[2]) == cli_cfg.node_addr) { addressed = 1; addr_mode = ADDR_NODE; led_blink(0, 1, 50); if (buf[0] == MCMD_PING16) rb[n++] = MCMD_ACK; } else { addressed = 0; addr_mode = ADDR_NONE; } break; case MCMD_ADDR_GRP16: if (((buf[1] << 8) | buf[2]) == cli_cfg.group_addr) { addressed = 1; addr_mode = ADDR_GROUP; led_blink(0, 1, 50); } else { addressed = 0; addr_mode = ADDR_NONE; } break; case MCMD_ADDR_BC: addressed = 1; addr_mode = ADDR_ALL; led_blink(0, 1, 50); break; case MCMD_INIT: reboot = 1; break; case MCMD_GET_INFO: /* general info */ rb[n++] = MCMD_ACK + 7; // send acknowledge, variable data length rb[n++] = 24; // data length rb[n++] = PROTOCOL_VERSION; // send protocol version rb[n++] = g_n_variables; // send number of variables rb[n++] = cli_cfg.node_addr >> 8; // send node address rb[n++] = cli_cfg.node_addr & 0xFF; rb[n++] = cli_cfg.group_addr >> 8; // send group address rb[n++] = cli_cfg.group_addr & 0xFF; rb[n++] = git_revision_int >> 8; // send revision rb[n++] = git_revision_int & 0xFF; for (i = 0; i < 16; i++) // send node name rb[n++] = cli_cfg.node_name[i]; rb[n] = crc8(rb, n); n++; break; case MCMD_GET_INFO + 1: /* send variable info */ if (buf[1] < g_n_variables) { MSCB_INFO_VAR *pvar; pvar = variables + buf[1]; rb[n++] = MCMD_ACK + 7; // send acknowledge, variable data length rb[n++] = 13; // data length rb[n++] = pvar->width; rb[n++] = pvar->unit; rb[n++] = pvar->prefix; rb[n++] = pvar->status; rb[n++] = pvar->flags; for (i = 0; i < 8; i++) // send variable name rb[n++] = pvar->name[i]; rb[n] = crc8(rb, n); n++; } else { /* just send dummy ack */ rb[0] = MCMD_ACK; rb[1] = 0; n = 2; } break; case MCMD_GET_UPTIME: /* send uptime */ u = uptime(); rb[0] = MCMD_ACK + 4; rb[1] = *(((unsigned char *)&u) + 0); rb[2] = *(((unsigned char *)&u) + 1); rb[3] = *(((unsigned char *)&u) + 2); rb[4] = *(((unsigned char *)&u) + 3); rb[5] = crc8(rb, 5); n = 6; break; case MCMD_SET_ADDR: if (buf[1] == ADDR_SET_NODE) /* complete node address */ cli_cfg.node_addr = (buf[2] << 8) | buf[3]; else if (buf[1] == ADDR_SET_HIGH) /* only high byte node address */ *((unsigned char *)(&cli_cfg.node_addr)) = (buf[2] << 8) | buf[3]; else if (buf[1] == ADDR_SET_GROUP) /* group address */ cli_cfg.group_addr = (buf[2] << 8) | buf[3]; flash_client = 1; break; case MCMD_SET_NAME: /* set node name in RAM */ for (i = 0; i < 16 && i < buf[1]; i++) cli_cfg.node_name[i] = buf[2 + i]; cli_cfg.node_name[15] = 0; flash_client = 1; break; case MCMD_UPGRADE: if (!master) { rb[0] = MCMD_ACK + 1; rb[1] = 1; rb[2] = crc8(rb, 2); n = 3; flash_program = 1; } else { rb[0] = MCMD_ACK + 1; rb[1] = 2; rb[2] = crc8(rb, 2); n = 3; } break; case MCMD_FLASH: flash_client = 1; break; case MCMD_ECHO: rb[0] = MCMD_ACK + 1; rb[1] = buf[1]; rb[2] = crc8(rb, 2); n = 3; break; } if ((buf[0] & 0xF8) == MCMD_READ) { if (buf[0] == MCMD_READ + 1) { // single variable ch = buf[1]; if (ch < g_n_variables) { n = variables[ch].width; // number of bytes to return if (variables[ch].flags & MSCBF_DATALESS) { // obtain data from user buffer rb[0] = MCMD_ACK + 7; n = user_read(ch, rb+2); rb[1] = n; n += 2; rb[n] = crc8(rb, n); n++; } else { if (n > 6) { /* variable length buffer */ rb[0] = MCMD_ACK + 7; // send acknowledge, variable data length rb[1] = n; // send data length for (i = 0; i < n; i++) // copy user data rb[2+i] = ((char *) variables[ch].ud)[i]; n += 2; } else { rb[0] = MCMD_ACK + n; for (i = 0; i < n; i++) // copy user data rb[1+i] = ((char *) variables[ch].ud)[i]; n += 1; } rb[n] = crc8(rb, n); // generate CRC code n++; } } else { /* just send dummy ack to indicate error */ rb[0] = MCMD_ACK; n = 1; } } else if (buf[0] == MCMD_READ + 2) { // variable range if (buf[1] < g_n_variables && buf[2] < g_n_variables && buf[1] <= buf[2]) { /* calculate number of bytes to return */ for (i = buf[1], size = 0; i <= buf[2]; i++) size += variables[i].width; n = 0; rb[n++] = MCMD_ACK + 7; // send acknowledge, variable data length if (size < 0x80) rb[n++] = size; // send data length one byte else { rb[n++] = 0x80 | size / 0x100; // send data length two bytes rb[n++] = size & 0xFF; } /* loop over all variables */ for (i = buf[1]; i <= buf[2]; i++) { for (j = 0; j < variables[i].width; j++) { // send user data if (variables[i].flags & MSCBF_DATALESS) { rb[n++] = 0; // only send dataless variables if read individually } else { rb[n++] = ((char *) variables[i].ud)[j]; } } } rb[n] = crc8(rb, n); n++; } else { /* just send dummy ack to indicate error */ rb[0] = MCMD_ACK; n = 1; } } } if ((buf[0] & 0xF8) == MCMD_WRITE_NA || (buf[0] & 0xF8) == MCMD_WRITE_ACK) { n = buf[0] & 0x07; if (n == 0x07) { // variable length j = 1; n = buf[1]; ch = buf[2]; } else { j = 0; ch = buf[1]; } n--; // data size (minus channel) if (ch < g_n_variables) { /* don't exceed variable width */ if (n > variables[ch].width) n = variables[ch].width; for (i = 0; i < n; i++) if (!(variables[ch].flags & MSCBF_DATALESS)) { if (variables[ch].unit == UNIT_STRING) { if (n > 4) /* copy bytes in normal order */ ((char *) variables[ch].ud)[i] = buf[2 + j + i]; else /* copy bytes in reverse order (got swapped on host) */ ((char *) variables[ch].ud)[i] = buf[buflen - 2 - i]; } else /* copy LSB bytes, needed for BYTE if DWORD is sent */ ((char *) variables[ch].ud)[i] = buf[buflen - 1 - variables[ch].width + i]; } user_write(ch, buf); if ((buf[0] & 0xF8) == MCMD_WRITE_ACK) { rb[0] = MCMD_ACK; rb[1] = buf[buflen - 1]; n = 2; } } } return n; } /*------------------------------------------------------------------*/ void submaster_yield(void) { unsigned char *ptr, *return_buf, flags, cmd, *buf, len; unsigned short n, bsize; char socket_no; UDP_HEADER *pudp; // if Ethernet link is active if (mn_get_link_status() && !dhcp_configured) { // obtain IP address mn_dhcp_start(NULL, DHCP_DEFAULT_LEASE_TIME); } /* figure out if DHCP is configured */ if (dhcp_lease.dhcp_state == DHCP_OK && mn_get_link_status()) dhcp_configured = 1; else dhcp_configured = 0; /* invert first LED if DHCP is ok */ if (dhcp_configured && !invert_led) { led_mode(0, 1); led_set(0, LED_OFF); invert_led = 1; } if (!dhcp_configured && invert_led) { led_mode(0, 0); led_set(0, LED_OFF); invert_led = 0; } /* receive a UDP package, open socket if necessary */ if ((n = udp_receive(&ptr, &socket_no)) > 0) { pudp = (UDP_HEADER *)ptr; /* set up return buffer just after UDP header */ return_buf = (unsigned char *)(((UDP_HEADER *)udp_tx_buf)+1); /* check for exclusive mode */ if (exclusive_port && (exclusive_port != sock_info[socket_no].dest_port || memcmp(exclusive_addr, sock_info[socket_no].ip_dest_addr, 4))) return; if (exclusive_port && exclusive_port == sock_info[socket_no].dest_port && memcmp(exclusive_addr, sock_info[socket_no].ip_dest_addr, 4) == 0) exclusive_timer = 1000; // expires after 10 sec. inactivity /* discard packets which are too large */ if (n > sizeof(rs485_tx_buf) + sizeof(UDP_HEADER)) return; buf = (unsigned char *)(pudp+1); cmd = (pudp->flags & RS485_FLAG_CMD) > 0; bsize = pudp->size; flags = pudp->flags; /* don't continue if invalid size */ if (n != bsize + sizeof(UDP_HEADER)) return; /* don't continue if incorrect version */ if (pudp->version != SUBM_VERSION && buf[0] != MCMD_ECHO) return; /* signal incoming data */ led_blink(0, 1, 50); /* execute commands directly */ if (cmd) { n = execute(socket_no, buf, return_buf); if (n > 0) udp_send(socket_no, n); } else { /* check for memory commands */ if (buf[0] == MCMD_WRITE_MEM || buf[0] == MCMD_READ_MEM) { n = interprete_mem(buf, return_buf, UDP_TX_SIZE-sizeof(UDP_HEADER)); if (n > 0) udp_send(socket_no, n); return; } /* check if packet is for this node */ n = interprete(buf, return_buf); if (n > 0) udp_send(socket_no, n); /* process command locally if addressed */ if (addressed) { len = (buf[0] & 0x07) + 2; /* process any command after address */ if (bsize > len) { buf += len; master = 1; // prohibits upgrade /* check for memory commands */ if (buf[0] == MCMD_WRITE_MEM || buf[0] == MCMD_READ_MEM) { n = interprete_mem(buf, return_buf, UDP_TX_SIZE-sizeof(UDP_HEADER)); if (n > 0) udp_send(socket_no, n); master = 0; return; } n = interprete(buf, return_buf); if (n > 0) udp_send(socket_no, n); master = 0; } } /* if not addressed, send command to external RS485 bus */ if (!addressed) { memcpy(rs485_tx_buf, buf, bsize); /* follwing data over RS-485 is received in master mode */ master = 1; if (rs485_send_master(bsize, flags)) { /* wait until sent */ while (n_rs485_tx) watchdog_refresh(0); /* wait for data to be received */ n = rs485_receive_master(flags, return_buf); if (n > 0) { if (n != 1 || return_buf[0] != 0xFF) led_blink(1, 1, 50); udp_send(socket_no, n); } /* change baud rate if requested */ if (rs485_tx_buf[0] == MCMD_ADDR_BC && rs485_tx_buf[2] == MCMD_SET_BAUD) { uart_init(0, rs485_tx_buf[3]); } } master = 0; } else if (addr_mode == ADDR_GROUP || addr_mode == ADDR_ALL) { /* if we are addressed, but the command is a group or broadcast command, send command to RS485 external bus */ memcpy(rs485_tx_buf, buf, bsize); /* follwing data over RS-485 is received in master mode */ master = 1; rs485_send_master(bsize, flags); /* wait until sent */ while (n_rs485_tx) watchdog_refresh(0); /* we do not expect any reply for group or broadcast commands */ /* change baud rate if requested */ if (rs485_tx_buf[0] == MCMD_ADDR_BC && rs485_tx_buf[2] == MCMD_SET_BAUD) { uart_init(0, rs485_tx_buf[3]); } master = 0; } } } if (reboot) { /* wait until any packet sent */ delay_ms(100); /* reboot */ SFRPAGE = LEGACY_PAGE; RSTSRC = 0x10; } }