diff --git a/cmd_parser.c b/cmd_parser.c index 5271dd4..10fb3cc 100644 --- a/cmd_parser.c +++ b/cmd_parser.c @@ -759,18 +759,25 @@ void parse_mtu(void) write_char('\n'); } -void sfp_print_measurements(uint8_t sfp) +bool sfp_print_measurements(uint8_t sfp) { - print_string("Options: "); print_byte(sfp_read_reg(sfp, 92)); write_char('\n'); + if (!sfp_read_block(sfp, 92, 1)) + return false; + + print_string("Options: "); print_byte(sfp_buf[0]); write_char('\n'); if (!(sfp_options[sfp] & 0x40)) - return; - print_string("Temp: "); print_byte(sfp_read_reg(sfp, 224)); print_byte(sfp_read_reg(sfp, 225)); write_char('\n'); - print_string("Vcc: "); print_byte(sfp_read_reg(sfp, 226)); print_byte(sfp_read_reg(sfp, 227)); write_char('\n'); - print_string("TX Bias: "); print_byte(sfp_read_reg(sfp, 228)); print_byte(sfp_read_reg(sfp, 229)); write_char('\n'); - print_string("TX Power: "); print_byte(sfp_read_reg(sfp, 230)); print_byte(sfp_read_reg(sfp, 231)); write_char('\n'); - print_string("RX Power: "); print_byte(sfp_read_reg(sfp, 232)); print_byte(sfp_read_reg(sfp, 233)); write_char('\n'); - print_string("Laser: "); print_byte(sfp_read_reg(sfp, 234)); print_byte(sfp_read_reg(sfp, 235)); write_char('\n'); - print_string("State: "); print_byte(sfp_read_reg(sfp, 238)); write_char('\n'); + return true; + if (!sfp_read_block(sfp, 224, 16)) + return false; + print_string("Temp: "); print_byte(sfp_buf[0]); print_byte(sfp_buf[1]); write_char('\n'); + print_string("Vcc: "); print_byte(sfp_buf[2]); print_byte(sfp_buf[3]); write_char('\n'); + print_string("TX Bias: "); print_byte(sfp_buf[4]); print_byte(sfp_buf[5]); write_char('\n'); + print_string("TX Power: "); print_byte(sfp_buf[6]); print_byte(sfp_buf[7]); write_char('\n'); + print_string("RX Power: "); print_byte(sfp_buf[8]); print_byte(sfp_buf[9]); write_char('\n'); + print_string("Laser: "); print_byte(sfp_buf[10]); print_byte(sfp_buf[11]); write_char('\n'); + print_string("State: "); print_byte(sfp_buf[14]); write_char('\n'); + + return true; } @@ -788,11 +795,15 @@ void parse_sfp(void) print_string(" - empty\n"); continue; } - print_string(" - Rate: "); print_byte(sfp_read_reg(slot, 12)); - print_string(" Encoding: "); print_byte(sfp_read_reg(slot, 11)); + if (!sfp_read_block(slot, 11, 2)) { + print_string(" - I2C read failed on this slot\n"); + continue; + } + print_string(" - Rate: "); print_byte(sfp_buf[1]); + print_string(" Encoding: "); print_byte(sfp_buf[0]); write_char('\n'); - sfp_print_info(slot); - sfp_print_measurements(slot); + if (!sfp_print_info(slot) || !sfp_print_measurements(slot)) + print_string("I2C read failed on this slot\n"); } return; } diff --git a/httpd/httpd.c b/httpd/httpd.c index 8d98b8f..21de471 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -42,6 +42,15 @@ __xdata uint32_t cont_addr; // HTTP header properties __xdata uint8_t boundary[72]; + +// a client may split the request anywhere, including inside a boundary or a +// part header, so a configuration upload is parsed only once it is complete; +// sized for a full config sector plus the multipart framing around it +#define CONFIG_UPLOAD_BUF (CONFIG_LEN + 384) +__xdata uint8_t config_upload; +__xdata uint8_t config_buf[CONFIG_UPLOAD_BUF]; +__xdata uint16_t cfg_pos, cfg_hdr, cfg_body, cfg_end, cfg_last; +__xdata uint8_t cfg_bl; __xdata uint8_t * __xdata content_type = 0; __xdata uint8_t * __xdata session = 0; @@ -78,6 +87,7 @@ inline uint8_t is_separator(uint8_t c) void httpd_init(void) __banked { + config_upload = 0; // xdata is not zeroed by the startup code __xdata struct httpd_state * __xdata s = &(uip_conn->appstate); // Start listening to port 80 uip_listen(HTONS(80)); @@ -316,6 +326,65 @@ void gen_random_bytes(__xdata uint8_t *b, uint8_t bytes) } +/* 0: body incomplete, 1: configuration stored, 2: malformed */ +static uint8_t config_take(void) +{ + cfg_bl = strlen_x(boundary); + + // the body is complete once the closing boundary has arrived + cfg_last = 0; + while (1) { + if (cfg_last + cfg_bl + 1 >= write_len) + return 0; + if (strstart_x(&config_buf[cfg_last], boundary) + && strstart(&config_buf[cfg_last + cfg_bl], "--")) + break; + cfg_last++; + } + + // every part lies ahead of the closing boundary, so it bounds the walk + cfg_pos = 0; + while (cfg_pos < cfg_last) { + if (!strstart_x(&config_buf[cfg_pos], boundary)) { + cfg_pos++; + continue; + } + cfg_hdr = cfg_pos + cfg_bl; + cfg_body = cfg_hdr; + while (1) { + if (cfg_body + 3 >= cfg_last) + return 2; + if (strstart(&config_buf[cfg_body], "\r\n\r\n")) + break; + cfg_body++; + } + cfg_end = cfg_body; + cfg_body += 4; + // reaching cfg_last is a match: the last part ends at the closing boundary + while (cfg_end < cfg_last && !strstart_x(&config_buf[cfg_end], boundary)) + cfg_end++; + while (cfg_hdr + 8 < cfg_body) { + // the part carrying a filename holds the configuration + if (strstart(&config_buf[cfg_hdr], "filename")) { + // the payload plus its terminator must fit the sector + if (cfg_end - cfg_body + 1 > CONFIG_LEN) + return 2; + config_buf[cfg_end] = 0; + flash_region.addr = CONFIG_START; + flash_sector_erase(); + flash_region.addr = CONFIG_START; + flash_region.len = cfg_end - cfg_body + 1; + flash_write_bytes(config_buf + cfg_body); + return 1; + } + cfg_hdr++; + } + cfg_pos = cfg_end; + } + return 2; +} + + /* * Reads post data from the http stream and writes it into flash memory * Input: the current position in the TCP buffer (uip_appdata) @@ -438,6 +507,7 @@ void handle_post(void) return; } print_string("Firmware upload started."); + config_upload = 0; uptr = FIRMWARE_UPLOAD_START; verify_crc = 1; max_upload = 1024576; @@ -446,12 +516,10 @@ void handle_post(void) send_unauthorized(); return; } - dbg_string("Configuration upload, erasing config mem!\n"); - uptr = CONFIG_START; + dbg_string("Configuration upload\n"); verify_crc = 0; - max_upload = 2048; - flash_region.addr = CONFIG_START; - flash_sector_erase(); + config_upload = 1; + write_len = 0; } // Check for other POST requests, which are not multipart, below } else { @@ -505,6 +573,32 @@ void handle_post(void) send_bad_request(); return; } + if (config_upload) { + cfg_pos = uip_len - (p - uip_appdata); + if (write_len + cfg_pos >= CONFIG_UPLOAD_BUF) { + print_string("Configuration too large, aborting.\n"); + config_upload = 0; + s->tstate = TSTATE_NONE; + send_bad_request(); + return; + } + memcpy(config_buf + write_len, p, cfg_pos); + write_len += cfg_pos; + uint8_t taken = config_take(); + + if (!taken) { + s->tstate = TSTATE_MULTIPART; + return; + } + config_upload = 0; + s->tstate = TSTATE_NONE; + if (taken == 2) { + send_bad_request(); + return; + } + slen = strtox(outbuf, "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n"); + return; + } // We skip the intial parts as part of the header do { p = skip_boundary(p); diff --git a/httpd/page_impl.c b/httpd/page_impl.c index 36b3444..171a674 100644 --- a/httpd/page_impl.c +++ b/httpd/page_impl.c @@ -178,10 +178,12 @@ void reg_to_html_long(register uint16_t reg) void send_sfp_info(uint8_t sfp) { // This loops over the Vendor-name, Vendor OUI, Vendor PN and Vendor rev ASCII fields - for (uint8_t i = 20; i < 60; i++) { - if (i >= 36 && i < 40) // Skip Non-ASCII codes + for (uint8_t i = 16; i < 64; i++) { + if (!(i & 0xf)) + sfp_read_block(sfp, i, 16); + if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes continue; - uint8_t c = sfp_read_reg(sfp, i); + uint8_t c = sfp_buf[i & 0xf]; if (c && c != 0xa0) // a0 is the byte read from a non-existant I2C EEPROM char_to_html(c); } @@ -194,32 +196,10 @@ void sfp_send_data(uint8_t slot, uint8_t reg, uint8_t len) if (len > 16) return; - if (reg & 0x80) { // Configure SFP readings address (0x51) as I2C device address - reg &= 0x7f; - REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | (len - 1) & 0xf, 0x51 >> 5, (0x51 << 3) & 0xff); - } else { - REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | (len - 1) & 0xf, 0x50 >> 5, (0x50 << 3) & 0xff); - } + sfp_read_block(slot, reg, len); - reg_read_m(RTL837X_REG_I2C_CTRL); - sfr_mask_data(1, 0xfc, i2c_bus_from_scl_pin(machine.sfp_port[slot].i2c.scl) << 5 | i2c_bus_from_sda_pin(machine.sfp_port[slot].i2c.sda) << 2); - reg_write_m(RTL837X_REG_I2C_CTRL); - - REG_WRITE(RTL837X_REG_I2C_IN, 0, 0, 0, reg); - - // Execute I2C Read - reg_bit_set(RTL837X_REG_I2C_CTRL, 0); - - // Wait for execution to finish - do { - reg_read_m(RTL837X_REG_I2C_CTRL); - } while (sfr_data[3] & 0x1); - - for (uint8_t i = 0; i < len; i++) { - if (!(i & 0x3)) - reg_read_m(RTL837X_REG_I2C_OUT + i); - byte_to_html(sfr_data[3 - (i & 0x3)]); - } + for (uint8_t i = 0; i < len; i++) + byte_to_html(sfp_buf[i]); } @@ -936,7 +916,7 @@ found_end: if (valid_len > (TCP_OUTBUF_SIZE - slen)) { cont_len = valid_len - (TCP_OUTBUF_SIZE - slen); valid_len = TCP_OUTBUF_SIZE - slen; - cont_addr = valid_len; + cont_addr = CONFIG_START + valid_len; } flash_region.addr = CONFIG_START; diff --git a/machine.c b/machine.c index c27b92a..d7d4823 100644 --- a/machine.c +++ b/machine.c @@ -444,6 +444,87 @@ __code const struct machine machine = { }; void machine_custom_init(void) { } + +#elif defined MACHINE_PCB_SWTG018AS_V2_1_0 // Sold as Sodola SL902 / Horaco "SWTGW218AS"; the SWTGW218AS label also covers other PCBs with different SFP and LED wiring (see MACHINE_SWTGW218AS) + +__code const struct machine machine = { + .machine_name = "SWTGW218AS (SWTG018AS-V2.1.0)", + .isRTL8373 = 1, + .mac_flash_offset = 0x1FC000, + .min_port = 0, + .max_port = 8, + .n_sfp = 1, + .log_to_phys_port = {1, 2, 3, 4, 5, 6, 7, 8, 9}, + .phys_to_log_port = {0, 1, 2, 3, 4, 5, 6, 7, 8}, + .is_sfp = {0, 0, 0, 0, 0, 0, 0, 0, 1}, + .sfp_port[0].pin_detect = GPIO38, // pulled low on module insert + .sfp_port[0].pin_los = GPIO_NA, // no LOS pin wired + .sfp_port[0].pin_tx_disable = GPIO_NA, + .sfp_port[0].sds = 1, + .sfp_port[0].i2c = { .sda = GPIO39_I2C_SDA4, .scl = GPIO40_I2C_SCL3_MDC1 }, + .reset_pin = GPIO54_ACL_BIT2_EN, + .high_leds = { .mux = LED_27 | LED_28_SYS | LED_29, .enable = LED_28_SYS | LED_29 }, + .port_led_set = { 0, 0, 0, 0, 0, 0, 0, 0, 1}, + // LED wiring matches the SWTG018AS-A V2.0 (same PCB family) + .led_sets = { + { /* RJ45: First LED, yellow, second LED: green */ + LEDS_2G5 | LEDS_LINK, + LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT, + 0, + 0, + }, { /* SFP set (superseded by the raw register override in machine_custom_init) */ + LEDS_2G5 | LEDS_1G | LEDS_100M | LEDS_10M | LEDS_LINK | LEDS_ACT | LEDS_10G, + 0, + 0, + 0, + }}, + .led_mux_custom = 1, + .led_mux = { 0x00, 0x01, 0x04, 0x05, 0x08, // 65e0 + 0x09, 0x0c, 0x09, 0x0d, 0x10, // 65e4 + 0x11, 0x0e, 0x14, 0x11, 0x12, // 65e8 + 0x15, 0x15, 0x16, 0x18, 0x19, // 65ec + 0x1a, 0x19, 0x1d, 0x1e, 0x1c, // 65f0 + 0x1d, 0x20, 0x21 }, +}; + +// The LED-set encoding cannot express this board's bi-color SFP LED (green <= 2.5G, +// blue at 10G), so program the LED register block with the values the stock firmware +// uses. Runs after leds_setup() and overrides the values computed there. +// The final entry routes the blue-LED pin to the LED controller via PIN_MUX_0; +// as a GPIO (the default) no LED register can light it. PIN_MUX_1/2 stay +// untouched so SFP detect (GPIO38) and i2c remain GPIOs. +static __code const struct { uint16_t reg; uint32_t val; } custom_init_regs[] = { + { 0x6520, 0x0023e430UL }, // LED_MODE + { 0x6524, 0xff001400UL }, // LED3_0_SET3 + { 0x6528, 0x00100000UL }, // LED3_0_SET1 + { 0x652c, 0x007f013fUL }, // LED3_2_SET3 + { 0x6530, 0x02000400UL }, // LED1_0_SET3 + { 0x6534, 0x01400141UL }, // LED3_2_SET2 + { 0x6538, 0x01440170UL }, // LED1_0_SET2 + { 0x653c, 0x18000041UL }, // LED3_2_SET1 + { 0x6540, 0x01400155UL }, // LED1_0_SET1 + { 0x6544, 0x01411000UL }, // LED3_2_SET0 + { 0x6548, 0x01740141UL }, // LED1_0_SET0 + { 0x654c, 0x00010000UL }, // LED_PORT_SET_SEL + { 0x65d8, 0x3ffb6dffUL }, // LED_GLB_ACTIVE + { 0x65dc, 0x7f24977fUL }, // LED_GLB_IO_EN + { 0x65e0, 0x08144040UL }, // LED_GLB_MUX_1 + { 0x65e4, 0x10349309UL }, // LED_GLB_MUX_2 + { 0x65e8, 0x12454391UL }, // LED_GLB_MUX_3 + { 0x65ec, 0x19616555UL }, // LED_GLB_MUX_4 + { 0x65f0, 0x1c79d65aUL }, // LED_GLB_MUX_5 + { 0x65f4, 0x0002181dUL }, // LED_GLB_MUX_6 + { 0x7f8c, 0x20db6880UL }, // PIN_MUX_0 +}; + +void machine_custom_init(void) { + uint8_t i; + // REG_SET is a multi-statement macro without a do-while wrapper: braces required + for (i = 0; i < sizeof(custom_init_regs) / sizeof(custom_init_regs[0]); i++) { + REG_SET(custom_init_regs[i].reg, custom_init_regs[i].val); + } +} + #elif defined MACHINE_LIANGUO_ZX_SWTGW215AS // Has PCB branded PCB-SWTG115AS-V2.0 but is labeled and reports as a ZX-SWTGW215AS, seems to be identical to the "real" ZX-SWTGW215AS except for the LEDs __code const struct machine machine = { .machine_name = "Lianguo ZX-SWTGW215AS", diff --git a/machine.h b/machine.h index 523beea..29fe39f 100644 --- a/machine.h +++ b/machine.h @@ -18,6 +18,7 @@ // #define MACHINE_HG0402XG_V1_1 // #define MACHINE_SWTG018AS_A_V_2_0 // #define MACHINE_SWTGW218AS +// #define MACHINE_PCB_SWTG018AS_V2_1_0 // #define MACHINE_PCB_K0402WS_V3 // #define MACHINE_K0501W_V2_0 // #define MACHINE_LIANGUO_ZX_SWTGW215AS diff --git a/rtl837x_common.h b/rtl837x_common.h index 0fd6f35..4e48be5 100644 --- a/rtl837x_common.h +++ b/rtl837x_common.h @@ -130,6 +130,7 @@ extern __xdata struct uip_eth_addr uip_ethaddr; // Headers for calls in the common code area (HOME/BANK0) void print_string_no_syslog(__code char *p); +void print_string_newline_no_syslog(__code char *p); void print_string(__code char *p); void print_string_x(__xdata char *p); void print_long(uint32_t a); @@ -154,7 +155,8 @@ void sleep(uint16_t t); void write_char_no_syslog(char c); void write_char(char c); void print_reg(uint16_t reg); -uint8_t sfp_read_reg(uint8_t slot, uint8_t reg); +bool sfp_read_block(uint8_t slot, uint8_t reg, uint8_t len) __banked __reentrant; +extern __xdata uint8_t sfp_buf[16]; void reg_bit_set(uint16_t reg_addr, char bit); void reg_bit_clear(uint16_t reg_addr, char bit); uint8_t reg_bit_test(uint16_t reg_addr, char bit); @@ -169,11 +171,13 @@ uint16_t strlen_x(register __xdata const char *s); uint16_t strtox(register __xdata uint8_t *dst, register __code const char *s); uint16_t strcpy(register __xdata uint8_t *dst, register const char *s); char strcmp(register __xdata const uint8_t *a, register __code const uint8_t *b); +bool strstart(__xdata const uint8_t *a, __code const uint8_t *b); +bool strstart_x(__xdata const uint8_t *a, __xdata const uint8_t *b); void tcpip_output(void); uint8_t read_flash(uint8_t bank, __code uint8_t *addr); void get_random_32(void); void read_reg_timer(__xdata uint32_t * tmr); -void sfp_print_info(uint8_t sfp); +bool sfp_print_info(uint8_t sfp); bool gpio_pin_test(uint8_t pin); void set_sys_led_state(uint8_t state); void sds_read(uint8_t sds_id, uint8_t page, uint8_t reg); diff --git a/rtl837x_pins.c b/rtl837x_pins.c index e340c26..81ec887 100644 --- a/rtl837x_pins.c +++ b/rtl837x_pins.c @@ -1,6 +1,11 @@ #include "rtl837x_pins.h" #include "rtl837x_common.h" +#include "rtl837x_sfr.h" #include "rtl837x_regs.h" +#include "machine.h" + +extern __code const struct machine machine; +extern __xdata uint8_t sfr_data[4]; uint8_t i2c_bus_from_sda_pin(uint8_t sda_pin) __banked { @@ -119,3 +124,56 @@ void gpio_output_setup(uint8_t pin, __xdata uint8_t initial_val) __banked{ reg_bit_set(gpio_direction_reg(pin), (pin % 32)); } + + +/* + * Read up to 16 consecutive registers of the EEPROM via I2C into sfp_buf + */ +bool sfp_read_block(uint8_t slot, uint8_t reg, uint8_t len) __banked __reentrant +{ + uint8_t dev; + uint8_t val; + + len--; + if (len > 15) + return false; + + dev = (reg & 0x80) ? 0x51 : 0x50; // 0x51 holds the diagnostics, 0x50 the module data + reg &= 0x7f; + + REG_WRITE(RTL837X_REG_I2C_IN, 0, 0, 0, reg); + + REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, + 0x1 << (I2C_MEM_ADDR_WIDTH - 16) | len, + (dev >> 5) | i2c_bus_from_scl_pin(machine.sfp_port[slot].i2c.scl) << 5 + | i2c_bus_from_sda_pin(machine.sfp_port[slot].i2c.sda) << 2, + ((dev << 3) & 0xff) | 0x1); + + do { + reg_read(RTL837X_REG_I2C_CTRL); + } while (SFR_DATA_0 & 0x1); + + if (SFR_DATA_0 & 0x2) + return false; + + for (uint8_t i = 0; i <= len; i++) { + switch (i & 0x3) { + case 0: + reg_read(RTL837X_REG_I2C_OUT + i); + val = SFR_DATA_0; + break; + case 1: + val = SFR_DATA_8; + break; + case 2: + val = SFR_DATA_16; + break; + default: + val = SFR_DATA_24; + break; + } + sfp_buf[i] = val; + } + + return true; +} diff --git a/rtlplayground.c b/rtlplayground.c index ac3f2b5..b359c3f 100644 --- a/rtlplayground.c +++ b/rtlplayground.c @@ -140,6 +140,7 @@ __xdata char sfp_module_vendor[2][17]; __xdata char sfp_module_model[2][17]; __xdata char sfp_module_serial[2][17]; __xdata uint8_t sfp_options[2]; +__xdata uint8_t sfp_buf[16]; /* scratch for one I2C transaction, the controller reads at most 16 bytes */ __xdata uint8_t sfp_speed[2]; __xdata uint8_t sfp_quirks[2]; __xdata bool button_last; @@ -292,6 +293,12 @@ void print_string_no_syslog(__code char *p) write_char_no_syslog(*p++); } +void print_string_newline_no_syslog(__code char *p) +{ + write_char_no_syslog('\n'); + print_string_no_syslog(p); +} + void print_string_x(__xdata char *p) { while (*p) @@ -363,6 +370,32 @@ char strcmp(register __xdata const uint8_t *a, register __code const uint8_t *b) } +/* + * True when b is a prefix of a. Unlike strcmp() the byte after the match is not + * compared, and unlike is_word_x() it need not be a separator. + */ +bool strstart(__xdata const uint8_t *a, __code const uint8_t *b) +{ + uint8_t i = 0; + + while (b[i] && (b[i] == a[i])) + i++; + + return !b[i]; +} + + +bool strstart_x(__xdata const uint8_t *a, __xdata const uint8_t *b) +{ + uint8_t i = 0; + + while (b[i] && (b[i] == a[i])) + i++; + + return !b[i]; +} + + void print_short(uint16_t a) { // allocating the registers first improves the sdcc code here @@ -1061,37 +1094,6 @@ void sds_config(uint8_t sds, uint8_t mode) } -/* - * Read a register of the EEPROM via I2C - */ -uint8_t sfp_read_reg(uint8_t slot, uint8_t reg) -{ - if (reg & 0x80) { // Configure SFP readings address (0x51) as I2C device address - reg &= 0x7f; - REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | 0, 0x51 >> 5, (0x51 << 3) & 0xff); - } else { - REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | 0, 0x50 >> 5, (0x50 << 3) & 0xff); - } - - reg_read_m(RTL837X_REG_I2C_CTRL); - sfr_mask_data(1, 0xfc, i2c_bus_from_scl_pin(machine.sfp_port[slot].i2c.scl) << 5 | i2c_bus_from_sda_pin(machine.sfp_port[slot].i2c.sda) << 2); - reg_write_m(RTL837X_REG_I2C_CTRL); - - REG_WRITE(RTL837X_REG_I2C_IN, 0, 0, 0, reg); - - // Execute I2C Read - reg_bit_set(RTL837X_REG_I2C_CTRL, 0); - - // Wait for execution to finish - do { - reg_read_m(RTL837X_REG_I2C_CTRL); - } while (sfr_data[3] & 0x1); - - reg_read_m(RTL837X_REG_I2C_OUT); - return sfr_data[3]; -} - - /* * Adds TX Header to uip_buf and calls nic_tx_packet to send the packet * over the wire @@ -1251,36 +1253,46 @@ static inline uint8_t sfp_rate_to_sds_config(register uint8_t rate) } -void sfp_print_info(uint8_t sfp) +bool sfp_print_info(uint8_t sfp) { // This loops over the Vendor-name, Vendor OUI, Vendor PN and Vendor rev ASCII fields - for (uint8_t i = 20; i < 60; i++) { - if (i >= 36 && i < 40) // Skip Non-ASCII codes + for (uint8_t i = 16; i < 64; i++) { + if (!(i & 0xf) && !sfp_read_block(sfp, i, 16)) + return false; + if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes continue; - uint8_t c = sfp_read_reg(sfp, i); + uint8_t c = sfp_buf[i & 0xf]; if (c) write_char(c); } print_string("\n"); + + return true; } // Normalize strings from EEPROM by removing any trailing spaces; this allows simpler comparisons -void sfp_read_field(__xdata char *dst, uint8_t sfp, uint8_t start, uint8_t length) __reentrant +bool sfp_read_field(__xdata char *dst, uint8_t sfp, uint8_t start, uint8_t length) __reentrant { - dst[length] = '\0'; + if (!sfp_read_block(sfp, start, length)) + return false; - for (uint8_t i = 0; i < length; i++) - dst[i] = sfp_read_reg(sfp, start + i); + dst[length] = '\0'; + memcpy(dst, sfp_buf, length); while (length > 0 && dst[--length] == ' ') dst[length] = '\0'; + + return true; } -void sfp_get_info(uint8_t sfp) +bool sfp_get_info(uint8_t sfp) { - sfp_read_field(sfp_module_vendor[sfp], sfp, 20, 16); - sfp_read_field(sfp_module_model[sfp], sfp, 40, 16); - sfp_read_field(sfp_module_serial[sfp], sfp, 68, 16); + if (!sfp_read_field(sfp_module_vendor[sfp], sfp, 20, 16)) + return false; + if (!sfp_read_field(sfp_module_model[sfp], sfp, 40, 16)) + return false; + + return sfp_read_field(sfp_module_serial[sfp], sfp, 68, 16); } void sfp_apply_quirks(uint8_t sfp) __reentrant @@ -1299,7 +1311,7 @@ void sfp_apply_quirks(uint8_t sfp) __reentrant if (!(sfp_options[sfp] & 0x40)) { // The module reports that DDM is not implemented, but try a dummy read to confirm // 0xff would mean a failed I2C read or an impossible (per spec) voltage greater than 6.5V - if (sfp_read_reg(sfp, 226) != 0xff) { + if (sfp_read_block(sfp, 226, 1) && sfp_buf[0] != 0xff) { sfp_options[sfp] |= 0x40; } } @@ -1323,6 +1335,45 @@ void setup_sfp_gpio(void) } } +static bool sfp_module_read(uint8_t sfp) +{ + uint8_t rate; + + // Read Reg 11: Encoding, see SFF-8472 and SFF-8024 + // Read Reg 12: Signalling rate (including overhead) in 100Mbit: 0xd: 1Gbit, 0x67:10Gbit + delay(100); // Delay, because some modules need time to wake up + if (!sfp_read_block(sfp, 11, 2)) + return false; + + rate = sfp_buf[1]; + if (sfp_speed[sfp] == SFP_SPEED_100M) + rate = 0x1; + else if (sfp_speed[sfp] == SFP_SPEED_1G) + rate = 0xc; + else if (sfp_speed[sfp] == SFP_SPEED_2G5) + rate = 0x19; + else if (sfp_speed[sfp] == SFP_SPEED_10G) + rate = 0x69; + print_string(" Rate: "); print_byte(rate); // Normally 1, but 0 for DAC, can be ignored? + print_string(" Encoding: "); print_byte(sfp_buf[0]); + print_string(" Module: "); + if (!sfp_print_info(sfp)) + return false; + print_string("\n"); + + if (!sfp_read_block(sfp, 92, 1)) + return false; + sfp_options[sfp] = sfp_buf[0]; + if (!sfp_get_info(sfp)) + return false; + + sfp_apply_quirks(sfp); + sds_config(machine.sfp_port[sfp].sds, sfp_rate_to_sds_config(rate)); + + return true; +} + + void handle_sfp(void) { for (uint8_t sfp = 0; sfp < machine.n_sfp; sfp++) { @@ -1330,26 +1381,10 @@ void handle_sfp(void) if (sfp_pins_last & (0x1 << (sfp << 2))) { sfp_pins_last &= ~(0x01 << (sfp << 2)); print_string("\n Slot: "); write_char('1' + sfp); - // Read Reg 11: Encoding, see SFF-8472 and SFF-8024 - // Read Reg 12: Signalling rate (including overhead) in 100Mbit: 0xd: 1Gbit, 0x67:10Gbit - delay(100); // Delay, because some modules need time to wake up - uint8_t rate = sfp_read_reg(sfp, 12); - if (sfp_speed[sfp] == SFP_SPEED_100M) - rate = 0x1; - else if (sfp_speed[sfp] == SFP_SPEED_1G) - rate = 0xc; - else if (sfp_speed[sfp] == SFP_SPEED_2G5) - rate = 0x19; - else if (sfp_speed[sfp] == SFP_SPEED_10G) - rate = 0x69; - print_string(" Rate: "); print_byte(rate); // Normally 1, but 0 for DAC, can be ignored? - print_string(" Encoding: "); print_byte(sfp_read_reg(sfp, 11)); - print_string(" Module: "); sfp_print_info(sfp); - print_string("\n"); - sfp_options[sfp] = sfp_read_reg(sfp, 92); - sfp_get_info(sfp); - sfp_apply_quirks(sfp); - sds_config(machine.sfp_port[sfp].sds, sfp_rate_to_sds_config(rate)); + if (!sfp_module_read(sfp)) { + print_string("SFP: an I2C read failed, retrying on the next poll\n"); + sfp_pins_last |= 0x01 << (sfp << 2); + } } } else { if (!(sfp_pins_last & (0x1 << (sfp << 2)))) { diff --git a/syslog.c b/syslog.c index ad97fd2..989e8cc 100644 --- a/syslog.c +++ b/syslog.c @@ -30,16 +30,16 @@ void syslog_start(void) __banked uip_ipaddr(server_ip, state.server_ip[0], state.server_ip[1], state.server_ip[2], state.server_ip[3]); state.syslog_conn = uip_udp_new(&server_ip, HTONS(514)); if (state.syslog_conn == 0) { - print_string_no_syslog("Failed to create a new UDP client\n"); + print_string_newline_no_syslog("Failed to create a new UDP client"); return; } - print_string_no_syslog("Started syslog to IP "); + print_string_newline_no_syslog("Started syslog to IP "); itoa(state.server_ip[0]); write_char('.'); itoa(state.server_ip[1]); write_char('.'); itoa(state.server_ip[2]); write_char('.'); itoa(state.server_ip[3]); write_char('\n'); state.enabled = 1; } else { - print_string_no_syslog("Syslog is already running\n"); + print_string_newline_no_syslog("Syslog is already running"); } } @@ -49,9 +49,9 @@ void syslog_stop(void) __banked if (state.syslog_conn != 0) { uip_udp_remove(state.syslog_conn); state.syslog_conn = 0; - print_string_no_syslog("Stopped syslog\n"); + print_string_newline_no_syslog("Stopped syslog"); } else { - print_string_no_syslog("Syslog is not running\n"); + print_string_newline_no_syslog("Syslog is not running"); } } diff --git a/uip/uip.c b/uip/uip.c index ee07a04..6a194c1 100644 --- a/uip/uip.c +++ b/uip/uip.c @@ -234,7 +234,7 @@ __xdata struct uip_stats uip_stat; #endif /* UIP_STATISTICS == 1 */ #if UIP_LOGGING == 1 -#define UIP_LOG(m) print_string_no_syslog(m); +#define UIP_LOG(m) print_string_newline_no_syslog(m); #else #define UIP_LOG(m) #endif /* UIP_LOGGING == 1 */