mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-12-19 08:21:27 +01:00
The isprint() checks printability across all 256 characters, some of the upper 128 characters are printable and produce artifacts on UART. Call isascii() first to only consider the bottom 7bit ASCII characters as printable, and then check their printability using isprint(). This fixes a rare misprint in case the ID page content is uninitialized or corrupted. Signed-off-by: Marek Vasut <marek.vasut@mailbox.org> Reviewed-by: Christoph Niedermaier <cniedermaier@dh-electronics.com>
249 lines
6.3 KiB
C
249 lines
6.3 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright 2022 Marek Vasut <marex@denx.de>
|
|
* Copyright 2022 DENX Software Engineering GmbH, Philip Oberfichtner <pro@denx.de>
|
|
*/
|
|
|
|
#include <dm.h>
|
|
#include <env.h>
|
|
#include <i2c_eeprom.h>
|
|
#include <net.h>
|
|
#include <u-boot/crc.h>
|
|
|
|
#include "dh_common.h"
|
|
|
|
static int on_dh_som_serial_number(const char *name, const char *value, enum env_op op,
|
|
int flags)
|
|
{
|
|
env_set("SN", value);
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_ENV_CALLBACK(dh_som_serial_number, on_dh_som_serial_number);
|
|
|
|
static int on_SN(const char *name, const char *value, enum env_op op, int flags)
|
|
{
|
|
env_set("dh_som_serial_number", value);
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_ENV_CALLBACK(SN, on_SN);
|
|
|
|
bool dh_mac_is_in_env(const char *env)
|
|
{
|
|
unsigned char enetaddr[6];
|
|
|
|
return eth_env_get_enetaddr(env, enetaddr);
|
|
}
|
|
|
|
int dh_get_mac_is_enabled(const char *alias)
|
|
{
|
|
ofnode node = ofnode_path(alias);
|
|
|
|
if (!ofnode_valid(node))
|
|
return -EINVAL;
|
|
|
|
if (!ofnode_is_enabled(node))
|
|
return -ENODEV;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dh_read_eeprom_id_page(u8 *eeprom_buffer, const char *alias)
|
|
{
|
|
struct eeprom_id_page *eip = (struct eeprom_id_page *)eeprom_buffer;
|
|
struct udevice *dev;
|
|
size_t payload_len;
|
|
int eeprom_size;
|
|
u16 crc16_calc;
|
|
u16 crc16_eip;
|
|
u8 crc8_calc;
|
|
ofnode node;
|
|
int ret;
|
|
|
|
node = ofnode_path(alias);
|
|
|
|
ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, node, &dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
eeprom_size = i2c_eeprom_size(dev);
|
|
if (eeprom_size < 0) {
|
|
printf("%s: Error getting EEPROM ID page size! ret = %d\n", __func__, ret);
|
|
return eeprom_size;
|
|
}
|
|
|
|
if (eeprom_size == 0 || eeprom_size > DH_EEPROM_ID_PAGE_MAX_SIZE) {
|
|
eeprom_size = DH_EEPROM_ID_PAGE_MAX_SIZE;
|
|
printf("Get invalid EEPROM ID page size %d bytes! Try to read %d bytes.\n",
|
|
eeprom_size, DH_EEPROM_ID_PAGE_MAX_SIZE);
|
|
}
|
|
|
|
ret = i2c_eeprom_read(dev, 0x0, eeprom_buffer, eeprom_size);
|
|
if (ret) {
|
|
printf("%s: Error reading EEPROM ID page! ret = %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Validate header ID */
|
|
if (eip->hdr.id[0] != 'D' || eip->hdr.id[1] != 'H' || eip->hdr.id[2] != 'E') {
|
|
printf("%s: Error validating header ID! (got %c%c%c (0x%02x 0x%02x 0x%02x) != expected DHE)\n",
|
|
__func__, (isascii(eip->hdr.id[0]) && isprint(eip->hdr.id[0])) ? eip->hdr.id[0] : '.',
|
|
(isascii(eip->hdr.id[1]) && isprint(eip->hdr.id[1])) ? eip->hdr.id[1] : '.',
|
|
(isascii(eip->hdr.id[2]) && isprint(eip->hdr.id[2])) ? eip->hdr.id[2] : '.',
|
|
eip->hdr.id[0], eip->hdr.id[1], eip->hdr.id[2]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Validate header checksum */
|
|
crc8_calc = crc8(0xff, eeprom_buffer, offsetof(struct eeprom_id_page, hdr.crc8_hdr));
|
|
if (eip->hdr.crc8_hdr != crc8_calc) {
|
|
printf("%s: Error validating header checksum! (got 0x%02x != calc 0x%02x)\n",
|
|
__func__, eip->hdr.crc8_hdr, crc8_calc);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Validate header version
|
|
* The payload is defined by the version specified in the header.
|
|
* Currently only version 0x10 is defined, so take the length of
|
|
* the only defined payload as the payload length.
|
|
*/
|
|
if (eip->hdr.version != DH_EEPROM_ID_PAGE_V1_0) {
|
|
printf("%s: Error validating version! (0x%02X is not supported)\n",
|
|
__func__, eip->hdr.version);
|
|
return -EINVAL;
|
|
}
|
|
payload_len = sizeof(eip->pl);
|
|
|
|
/* Validate payload checksum */
|
|
crc16_eip = (eip->hdr.crc16_pl[1] << 8) | eip->hdr.crc16_pl[0];
|
|
crc16_calc = crc16(0xffff, eeprom_buffer + sizeof(eip->hdr), payload_len);
|
|
if (crc16_eip != crc16_calc) {
|
|
printf("%s: Error validating data checksum! (got 0x%02x != calc 0x%02x)\n",
|
|
__func__, crc16_eip, crc16_calc);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dh_get_value_from_eeprom_buffer(enum eip_request_values request, u8 *data, int data_len,
|
|
struct eeprom_id_page *eip)
|
|
{
|
|
char fin_chr;
|
|
u8 soc_coded;
|
|
char soc_chr;
|
|
|
|
if (!eip)
|
|
return -EINVAL;
|
|
|
|
fin_chr = (eip->pl.item_prefix & DH_ITEM_PREFIX_FIN_BIT) ?
|
|
DH_ITEM_PREFIX_FIN_FLASHED_CHR : DH_ITEM_PREFIX_FIN_HALF_CHR;
|
|
soc_coded = eip->pl.item_prefix & 0xf;
|
|
|
|
/* Copy requested data */
|
|
switch (request) {
|
|
case DH_MAC0:
|
|
if (!is_valid_ethaddr(eip->pl.mac0))
|
|
return -EINVAL;
|
|
|
|
if (data_len >= sizeof(eip->pl.mac0))
|
|
memcpy(data, eip->pl.mac0, sizeof(eip->pl.mac0));
|
|
else
|
|
return -EINVAL;
|
|
break;
|
|
case DH_MAC1:
|
|
if (!is_valid_ethaddr(eip->pl.mac1))
|
|
return -EINVAL;
|
|
|
|
if (data_len >= sizeof(eip->pl.mac1))
|
|
memcpy(data, eip->pl.mac1, sizeof(eip->pl.mac1));
|
|
else
|
|
return -EINVAL;
|
|
break;
|
|
case DH_ITEM_NUMBER:
|
|
if (data_len < 8) /* String length must be 7 characters + string termination */
|
|
return -EINVAL;
|
|
|
|
if (soc_coded == DH_ITEM_PREFIX_NXP)
|
|
soc_chr = DH_ITEM_PREFIX_NXP_CHR;
|
|
else if (soc_coded == DH_ITEM_PREFIX_ST)
|
|
soc_chr = DH_ITEM_PREFIX_ST_CHR;
|
|
else
|
|
return -EINVAL;
|
|
|
|
snprintf(data, data_len, "%c%c%05d", fin_chr, soc_chr,
|
|
(eip->pl.item_num[0] << 16) | (eip->pl.item_num[1] << 8) |
|
|
eip->pl.item_num[2]);
|
|
break;
|
|
case DH_SERIAL_NUMBER:
|
|
/*
|
|
* data_len must be greater than the size of eip->pl.serial,
|
|
* because there is a string termination needed.
|
|
*/
|
|
if (data_len <= sizeof(eip->pl.serial))
|
|
return -EINVAL;
|
|
|
|
data[sizeof(eip->pl.serial)] = 0;
|
|
memcpy(data, eip->pl.serial, sizeof(eip->pl.serial));
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dh_get_mac_from_eeprom(unsigned char *enetaddr, const char *alias)
|
|
{
|
|
struct udevice *dev;
|
|
int ret;
|
|
ofnode node;
|
|
|
|
node = ofnode_path(alias);
|
|
if (!ofnode_valid(node)) {
|
|
printf("%s: ofnode for %s not found!", __func__, alias);
|
|
return -ENOENT;
|
|
}
|
|
|
|
ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, node, &dev);
|
|
if (ret) {
|
|
printf("%s: Cannot find EEPROM! ret = %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = i2c_eeprom_read(dev, 0xfa, enetaddr, 0x6);
|
|
if (ret) {
|
|
printf("%s: Error reading EEPROM! ret = %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
if (!is_valid_ethaddr(enetaddr)) {
|
|
printf("%s: Address read from EEPROM is invalid!\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
__weak int dh_setup_mac_address(struct eeprom_id_page *eip)
|
|
{
|
|
unsigned char enetaddr[6];
|
|
|
|
if (dh_mac_is_in_env("ethaddr"))
|
|
return 0;
|
|
|
|
if (dh_get_mac_is_enabled("ethernet0"))
|
|
return 0;
|
|
|
|
if (!dh_get_value_from_eeprom_buffer(DH_MAC0, enetaddr, sizeof(enetaddr), eip))
|
|
return eth_env_set_enetaddr("ethaddr", enetaddr);
|
|
|
|
if (!dh_get_mac_from_eeprom(enetaddr, "eeprom0"))
|
|
return eth_env_set_enetaddr("ethaddr", enetaddr);
|
|
|
|
printf("%s: Unable to set mac address!\n", __func__);
|
|
return -ENXIO;
|
|
}
|