static int i2c_eeprom_set_pointer(I2CEEPROM *i2c_eeprom, uint8_t *eeprom_memory_address) { int bytes_written = 0; if (i2c_eeprom == NULL || i2c_eeprom->file < 0) { log_error("I2C EEPROM structure uninitialized"); return -1; } bytes_written = robust_write(i2c_eeprom->file, eeprom_memory_address, 2); if (bytes_written != 2) { // We only use debug here to not spam the log with errors. // This is the expected case if an extension is not present. log_debug("Error setting EEPROM address pointer: %s (%d)", get_errno_name(errno), errno); i2c_eeprom_destroy(i2c_eeprom); return -1; } return bytes_written; }
// TODO: If we want "real parallel accessibility" of the EEPROM we need to // lock a mutex in the init function and unlock it in the release function int i2c_eeprom_create(I2CEEPROM *i2c_eeprom, int extension) { GPIOREDPin pullup = {GPIO_RED_PORT_B, GPIO_RED_PIN_6}; log_debug("Initializing I2C EEPROM for extension %d", extension); if (i2c_eeprom == NULL || extension < 0 || extension > 1) { log_error("Initialization of I2C EEPROM for extension %d failed (malformed parameters)", extension); return -1; } // Enable pullups gpio_red_mux_configure(pullup, GPIO_RED_MUX_OUTPUT); gpio_red_output_clear(pullup); // Initialize I2C EEPROM structure i2c_eeprom->extension = extension; switch (extension) { case 0: i2c_eeprom->address_pin.port_index = GPIO_RED_PORT_G; i2c_eeprom->address_pin.pin_index = GPIO_RED_PIN_9; break; case 1: i2c_eeprom->address_pin.port_index = GPIO_RED_PORT_G; i2c_eeprom->address_pin.pin_index = GPIO_RED_PIN_13; break; } // enable I2C bus with GPIO_RED gpio_red_mux_configure(i2c_eeprom->address_pin, GPIO_RED_MUX_OUTPUT); i2c_eeprom_deselect(i2c_eeprom); i2c_eeprom->file = open(I2C_EEPROM_BUS, O_RDWR); if (i2c_eeprom->file < 0) { log_error("Initialization of I2C EEPROM for extension %d failed (Unable to open I2C bus: %s (%d))", extension, get_errno_name(errno), errno); return -1; } if (ioctl(i2c_eeprom->file, I2C_SLAVE, I2C_EEPROM_DEVICE_ADDRESS) < 0) { log_error("Initialization of I2C EEPROM for extension %d failed (Unable to access I2C device on the bus: %s (%d))", extension, get_errno_name(errno), errno); i2c_eeprom_destroy(i2c_eeprom); return -1; } return 0; }
int i2c_eeprom_read(I2CEEPROM *i2c_eeprom, uint16_t eeprom_memory_address, uint8_t *buffer_to_store, int bytes_to_read) { int bytes_read = 0; uint8_t mem_address[2] = {eeprom_memory_address >> 8, eeprom_memory_address & 0xFF}; if (i2c_eeprom == NULL || i2c_eeprom->file < 0) { log_error("I2C EEPROM structure uninitialized\n"); return -1; } i2c_eeprom_select(i2c_eeprom); if (i2c_eeprom_set_pointer(i2c_eeprom, mem_address) < 0) { return -1; } bytes_read = robust_read(i2c_eeprom->file, buffer_to_store, bytes_to_read); if (bytes_read != bytes_to_read) { log_error("EEPROM read failed: %s (%d)", get_errno_name(errno), errno); i2c_eeprom_destroy(i2c_eeprom); return -1; } i2c_eeprom_deselect(i2c_eeprom); return bytes_read; } int i2c_eeprom_write(I2CEEPROM *i2c_eeprom, uint16_t eeprom_memory_address, uint8_t *buffer_to_write, int bytes_to_write) { int i; int rc; char bytes_written = 0; uint8_t write_byte[3] = {0}; if (i2c_eeprom == NULL || i2c_eeprom->file < 0) { log_error("I2C EEPROM structure uninitialized\n"); return -1; } for (i = 0; i < bytes_to_write; i++) { write_byte[0] = eeprom_memory_address >> 8; write_byte[1] = eeprom_memory_address & 0xFF; write_byte[2] = buffer_to_write[i]; i2c_eeprom_select(i2c_eeprom); rc = robust_write(i2c_eeprom->file, write_byte, 3); i2c_eeprom_deselect(i2c_eeprom); // Wait at least 5ms between writes (see m24128-bw.pdf) usleep(5*1000); if (rc != 3) { log_error("EEPROM write failed (pos(%d), length(%d), expected length(%d): %s (%d)", i, rc, 3, get_errno_name(errno), errno); i2c_eeprom_destroy(i2c_eeprom); return -1; } eeprom_memory_address++; bytes_written++; } return bytes_written; }
int red_extension_init(void) { uint8_t buf[4]; int i, j, ret; ExtensionBaseConfig base_config[2]; // First we remove the Ethernet Extension kernel module (if there is one) // to make sure that there isn't a collision between SPI select and I2C select. red_ethernet_extension_rmmod(); // Then we deselect all EEPROMS for (i = 0; i < EXTENSION_NUM_MAX; i++) { for (j = 0; j < extension_startup.num_configs; j++) { red_extension_configure_pin(&extension_startup.config[j], i); } } // Now we can try to find the configurations for (i = 0; i < EXTENSION_NUM_MAX; i++) { I2CEEPROM i2c_eeprom; log_debug("Checking for presence of Extension at position %d", i); int eeprom_length = 0; uint8_t eeprom_buffer[EEPROM_SIZE]; base_config[i].extension = i; base_config[i].type = EXTENSION_TYPE_NONE; if (i2c_eeprom_create(&i2c_eeprom, i) < 0) { return -1; } if ((eeprom_length = red_extension_read_eeprom_from_fs(eeprom_buffer, i)) > 2) { int start_addr = eeprom_buffer[0] | (eeprom_buffer[1] << 8); if (eeprom_length + start_addr >= EEPROM_SIZE) { log_warn("Found malformed EEPROM config (start=%d, length=%d) for extension %d", start_addr, eeprom_length, i); } else { log_info("Found new EEPROM config (start=%d, length=%d) for extension %d", start_addr, eeprom_length, i); if (i2c_eeprom_write(&i2c_eeprom, start_addr, eeprom_buffer+2, eeprom_length-2) < 0) { log_warn("Writing EEPROM config for extension %d failed", i); } else { log_debug("Wrote EEPROM config (start=%d, length=%d) for extension %d", start_addr, eeprom_length, i); } } } if (i2c_eeprom_read(&i2c_eeprom, EXTENSION_EEPROM_TYPE_LOCATION, buf, EXTENSION_EEPROM_TYPE_SIZE) < EXTENSION_EEPROM_TYPE_SIZE) { log_info("Could not find Extension at position %d", i); continue; } base_config[i].type = (buf[0] << 0) | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); // If there is an extension that is either not configured (Extension type NONE) // Or that we currently don't support (WIFI), we will log it, but try to // continue finding extensions. We can support an extension at position 1 if // there is an unsupported extension at position 0. if (base_config[i].type == EXTENSION_TYPE_NONE) { log_warn("Could not find Extension at position %d (Type None)", i); continue; } if ((base_config[i].type != EXTENSION_TYPE_ETHERNET) && (base_config[i].type != EXTENSION_TYPE_RS485)) { log_warn("Extension at position %d not supported (type %d)", i, base_config[i].type); continue; } switch (base_config[i].type) { case EXTENSION_TYPE_RS485: ret = red_extension_read_rs485_config(&i2c_eeprom, (ExtensionRS485Config *) &base_config[i]); i2c_eeprom_destroy(&i2c_eeprom); if (ret < 0) { log_warn("Could not read RS485 config, ignoring extension at position %d", i); continue; } if (red_extension_save_rs485_config_to_fs((ExtensionRS485Config *) &base_config[i]) < 0) { log_warn("Could not save RS485 config. RS485 Extension at position %d will not show up in Brick Viewer", i); } break; case EXTENSION_TYPE_ETHERNET: ret = red_extension_read_ethernet_config(&i2c_eeprom, (ExtensionEthernetConfig *) &base_config[i]); i2c_eeprom_destroy(&i2c_eeprom); if (ret < 0) { log_warn("Could not read Ethernet config, ignoring extension at position %d", i); continue; } if (red_extension_save_ethernet_config_to_fs((ExtensionEthernetConfig *) &base_config[i]) < 0) { log_warn("Could not save Ethernet config. Ethernet Extension at position %d will not show up in Brick Viewer", i); } break; } } // Configure the pins and initialize extensions for (i = 0; i < EXTENSION_NUM_MAX; i++) { switch (base_config[i].type) { case EXTENSION_TYPE_RS485: log_info("Found RS485 Extension at position %d", i); for (j = 0; j < extension_rs485_pin_config.num_configs; j++) { red_extension_configure_pin(&extension_rs485_pin_config.config[j], i); } if (red_rs485_extension_init((ExtensionRS485Config *) &base_config[i]) < 0) { continue; } _red_extension_type[i] = EXTENSION_TYPE_RS485; break; case EXTENSION_TYPE_ETHERNET: log_info("Found Ethernet Extension at position %d", i); for (j = 0; j < extension_ethernet_pin_config.num_configs; j++) { red_extension_configure_pin(&extension_ethernet_pin_config.config[j], i); } if (red_ethernet_extension_init((ExtensionEthernetConfig *) &base_config[i]) < 0) { continue; } _red_extension_type[i] = EXTENSION_TYPE_ETHERNET; break; } } return 0; }