Home | History | Annotate | Download | only in common
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * (C) Copyright 2011
      4  * Holger Brunck, Keymile GmbH Hannover, holger.brunck (at) keymile.com
      5  */
      6 
      7 #include <common.h>
      8 #include <cli_hush.h>
      9 #include <i2c.h>
     10 #include "common.h"
     11 
     12 #define MAC_STR_SZ	20
     13 
     14 static int ivm_calc_crc(unsigned char *buf, int len)
     15 {
     16 	const unsigned short crc_tab[16] = {
     17 		0x0000, 0xCC01, 0xD801, 0x1400,
     18 		0xF001, 0x3C00, 0x2800, 0xE401,
     19 		0xA001, 0x6C00, 0x7800, 0xB401,
     20 		0x5000, 0x9C01, 0x8801, 0x4400};
     21 
     22 	unsigned short crc     = 0;   /* final result */
     23 	unsigned short r1      = 0;   /* temp */
     24 	unsigned char  byte    = 0;   /* input buffer */
     25 	int	i;
     26 
     27 	/* calculate CRC from array data */
     28 	for (i = 0; i < len; i++) {
     29 		byte = buf[i];
     30 
     31 		/* lower 4 bits */
     32 		r1 = crc_tab[crc & 0xF];
     33 		crc = ((crc) >> 4) & 0x0FFF;
     34 		crc = crc ^ r1 ^ crc_tab[byte & 0xF];
     35 
     36 		/* upper 4 bits */
     37 		r1 = crc_tab[crc & 0xF];
     38 		crc = (crc >> 4) & 0x0FFF;
     39 		crc = crc ^ r1 ^ crc_tab[(byte >> 4) & 0xF];
     40 	}
     41 	return crc;
     42 }
     43 
     44 static int ivm_set_value(char *name, char *value)
     45 {
     46 	char tempbuf[256];
     47 
     48 	if (value != NULL) {
     49 		sprintf(tempbuf, "%s=%s", name, value);
     50 		return set_local_var(tempbuf, 0);
     51 	} else {
     52 		unset_local_var(name);
     53 	}
     54 	return 0;
     55 }
     56 
     57 static int ivm_get_value(unsigned char *buf, int len, char *name, int off,
     58 				int check)
     59 {
     60 	unsigned short	val;
     61 	unsigned char	valbuf[30];
     62 
     63 	if ((buf[off + 0] != buf[off + 2]) &&
     64 	    (buf[off + 2] != buf[off + 4])) {
     65 		printf("%s Error corrupted %s\n", __func__, name);
     66 		val = -1;
     67 	} else {
     68 		val = buf[off + 0] + (buf[off + 1] << 8);
     69 		if ((val == 0) && (check == 1))
     70 			val = -1;
     71 	}
     72 	sprintf((char *)valbuf, "%x", val);
     73 	ivm_set_value(name, (char *)valbuf);
     74 	return val;
     75 }
     76 
     77 #define INV_BLOCKSIZE		0x100
     78 #define INV_DATAADDRESS		0x21
     79 #define INVENTORYDATASIZE	(INV_BLOCKSIZE - INV_DATAADDRESS - 3)
     80 
     81 #define IVM_POS_SHORT_TEXT		0
     82 #define IVM_POS_MANU_ID			1
     83 #define IVM_POS_MANU_SERIAL		2
     84 #define IVM_POS_PART_NUMBER		3
     85 #define IVM_POS_BUILD_STATE		4
     86 #define IVM_POS_SUPPLIER_PART_NUMBER	5
     87 #define IVM_POS_DELIVERY_DATE		6
     88 #define IVM_POS_SUPPLIER_BUILD_STATE	7
     89 #define IVM_POS_CUSTOMER_ID		8
     90 #define IVM_POS_CUSTOMER_PROD_ID	9
     91 #define IVM_POS_HISTORY			10
     92 #define IVM_POS_SYMBOL_ONLY		11
     93 
     94 static char convert_char(char c)
     95 {
     96 	return (c < ' ' || c > '~') ? '.' : c;
     97 }
     98 
     99 static int ivm_findinventorystring(int type,
    100 					unsigned char *const string,
    101 					unsigned long maxlen,
    102 					unsigned char *buf)
    103 {
    104 	int xcode = 0;
    105 	unsigned long cr = 0;
    106 	unsigned long addr = INV_DATAADDRESS;
    107 	unsigned long size = 0;
    108 	unsigned long nr = type;
    109 	int stop = 0;	/* stop on semicolon */
    110 
    111 	memset(string, '\0', maxlen);
    112 	switch (type) {
    113 	case IVM_POS_SYMBOL_ONLY:
    114 		nr = 0;
    115 		stop = 1;
    116 	break;
    117 	default:
    118 		nr = type;
    119 		stop = 0;
    120 	}
    121 
    122 	/* Look for the requested number of CR. */
    123 	while ((cr != nr) && (addr < INVENTORYDATASIZE)) {
    124 		if (buf[addr] == '\r')
    125 			cr++;
    126 		addr++;
    127 	}
    128 
    129 	/*
    130 	 * the expected number of CR was found until the end of the IVM
    131 	 *  content --> fill string
    132 	 */
    133 	if (addr < INVENTORYDATASIZE) {
    134 		/* Copy the IVM string in the corresponding string */
    135 		for (; (buf[addr] != '\r')			&&
    136 			((buf[addr] != ';') ||  (!stop))	&&
    137 			(size < (maxlen - 1)			&&
    138 			(addr < INVENTORYDATASIZE)); addr++) {
    139 			size += sprintf((char *)string + size, "%c",
    140 						convert_char (buf[addr]));
    141 		}
    142 
    143 		/*
    144 		 * copy phase is done: check if everything is ok. If not,
    145 		 * the inventory data is most probably corrupted: tell
    146 		 * the world there is a problem!
    147 		 */
    148 		if (addr == INVENTORYDATASIZE) {
    149 			xcode = -1;
    150 			printf("Error end of string not found\n");
    151 		} else if ((size > (maxlen - 1)) &&
    152 			   (buf[addr] != '\r')) {
    153 			xcode = -1;
    154 			printf("string too long till next CR\n");
    155 		}
    156 	} else {
    157 		/*
    158 		 * some CR are missing...
    159 		 * the inventory data is most probably corrupted
    160 		 */
    161 		xcode = -1;
    162 		printf("not enough cr found\n");
    163 	}
    164 	return xcode;
    165 }
    166 
    167 #define GET_STRING(name, which, len) \
    168 	if (ivm_findinventorystring(which, valbuf, len, buf) == 0) { \
    169 		ivm_set_value(name, (char *)valbuf); \
    170 	}
    171 
    172 static int ivm_check_crc(unsigned char *buf, int block)
    173 {
    174 	unsigned long	crc;
    175 	unsigned long	crceeprom;
    176 
    177 	crc = ivm_calc_crc(buf, CONFIG_SYS_IVM_EEPROM_PAGE_LEN - 2);
    178 	crceeprom = (buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN - 1] + \
    179 			buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN - 2] * 256);
    180 	if (crc != crceeprom) {
    181 		if (block == 0)
    182 			printf("Error CRC Block: %d EEprom: calculated: \
    183 			%lx EEprom: %lx\n", block, crc, crceeprom);
    184 		return -1;
    185 	}
    186 	return 0;
    187 }
    188 
    189 /* take care of the possible MAC address offset and the IVM content offset */
    190 static int process_mac(unsigned char *valbuf, unsigned char *buf,
    191 				int offset, bool unique)
    192 {
    193 	unsigned char mac[6];
    194 	unsigned long val = (buf[4] << 16) + (buf[5] << 8) + buf[6];
    195 
    196 	/* use an intermediate buffer, to not change IVM content
    197 	 * MAC address is at offset 1
    198 	 */
    199 	memcpy(mac, buf+1, 6);
    200 
    201 	/* MAC adress can be set to locally administred, this is only allowed
    202 	 * for interfaces which have now connection to the outside. For these
    203 	 * addresses we need to set the second bit in the first byte.
    204 	 */
    205 	if (!unique)
    206 		mac[0] |= 0x2;
    207 
    208 	if (offset) {
    209 		val += offset;
    210 		mac[3] = (val >> 16) & 0xff;
    211 		mac[4] = (val >> 8) & 0xff;
    212 		mac[5] = val & 0xff;
    213 	}
    214 
    215 	sprintf((char *)valbuf, "%pM", mac);
    216 	return 0;
    217 }
    218 
    219 static int ivm_analyze_block2(unsigned char *buf, int len)
    220 {
    221 	unsigned char	valbuf[MAC_STR_SZ];
    222 	unsigned long	count;
    223 
    224 	/* IVM_MAC Adress begins at offset 1 */
    225 	sprintf((char *)valbuf, "%pM", buf + 1);
    226 	ivm_set_value("IVM_MacAddress", (char *)valbuf);
    227 	/* IVM_MacCount */
    228 	count = (buf[10] << 24) +
    229 		   (buf[11] << 16) +
    230 		   (buf[12] << 8)  +
    231 		    buf[13];
    232 	if (count == 0xffffffff)
    233 		count = 1;
    234 	sprintf((char *)valbuf, "%lx", count);
    235 	ivm_set_value("IVM_MacCount", (char *)valbuf);
    236 	return 0;
    237 }
    238 
    239 int ivm_analyze_eeprom(unsigned char *buf, int len)
    240 {
    241 	unsigned short	val;
    242 	unsigned char	valbuf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN];
    243 	unsigned char	*tmp;
    244 
    245 	if (ivm_check_crc(buf, 0) != 0)
    246 		return -1;
    247 
    248 	ivm_get_value(buf, CONFIG_SYS_IVM_EEPROM_PAGE_LEN,
    249 			"IVM_BoardId", 0, 1);
    250 	val = ivm_get_value(buf, CONFIG_SYS_IVM_EEPROM_PAGE_LEN,
    251 			"IVM_HWKey", 6, 1);
    252 	if (val != 0xffff) {
    253 		sprintf((char *)valbuf, "%x", ((val / 100) % 10));
    254 		ivm_set_value("IVM_HWVariant", (char *)valbuf);
    255 		sprintf((char *)valbuf, "%x", (val % 100));
    256 		ivm_set_value("IVM_HWVersion", (char *)valbuf);
    257 	}
    258 	ivm_get_value(buf, CONFIG_SYS_IVM_EEPROM_PAGE_LEN,
    259 		"IVM_Functions", 12, 0);
    260 
    261 	GET_STRING("IVM_Symbol", IVM_POS_SYMBOL_ONLY, 8)
    262 	GET_STRING("IVM_DeviceName", IVM_POS_SHORT_TEXT, 64)
    263 	tmp = (unsigned char *)env_get("IVM_DeviceName");
    264 	if (tmp) {
    265 		int	len = strlen((char *)tmp);
    266 		int	i = 0;
    267 
    268 		while (i < len) {
    269 			if (tmp[i] == ';') {
    270 				ivm_set_value("IVM_ShortText",
    271 					(char *)&tmp[i + 1]);
    272 				break;
    273 			}
    274 			i++;
    275 		}
    276 		if (i >= len)
    277 			ivm_set_value("IVM_ShortText", NULL);
    278 	} else {
    279 		ivm_set_value("IVM_ShortText", NULL);
    280 	}
    281 	GET_STRING("IVM_ManufacturerID", IVM_POS_MANU_ID, 32)
    282 	GET_STRING("IVM_ManufacturerSerialNumber", IVM_POS_MANU_SERIAL, 20)
    283 	GET_STRING("IVM_ManufacturerPartNumber", IVM_POS_PART_NUMBER, 32)
    284 	GET_STRING("IVM_ManufacturerBuildState", IVM_POS_BUILD_STATE, 32)
    285 	GET_STRING("IVM_SupplierPartNumber", IVM_POS_SUPPLIER_PART_NUMBER, 32)
    286 	GET_STRING("IVM_DelieveryDate", IVM_POS_DELIVERY_DATE, 32)
    287 	GET_STRING("IVM_SupplierBuildState", IVM_POS_SUPPLIER_BUILD_STATE, 32)
    288 	GET_STRING("IVM_CustomerID", IVM_POS_CUSTOMER_ID, 32)
    289 	GET_STRING("IVM_CustomerProductID", IVM_POS_CUSTOMER_PROD_ID, 32)
    290 
    291 	if (ivm_check_crc(&buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN * 2], 2) != 0)
    292 		return 0;
    293 	ivm_analyze_block2(&buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN * 2],
    294 		CONFIG_SYS_IVM_EEPROM_PAGE_LEN);
    295 
    296 	return 0;
    297 }
    298 
    299 static int ivm_populate_env(unsigned char *buf, int len)
    300 {
    301 	unsigned char	*page2;
    302 	unsigned char	valbuf[MAC_STR_SZ];
    303 
    304 	/* do we have the page 2 filled ? if not return */
    305 	if (ivm_check_crc(buf, 2))
    306 		return 0;
    307 	page2 = &buf[CONFIG_SYS_IVM_EEPROM_PAGE_LEN*2];
    308 
    309 #ifndef CONFIG_KMTEGR1
    310 	/* if an offset is defined, add it */
    311 	process_mac(valbuf, page2, CONFIG_PIGGY_MAC_ADRESS_OFFSET, true);
    312 	env_set((char *)"ethaddr", (char *)valbuf);
    313 #ifdef CONFIG_KMVECT1
    314 /* KMVECT1 has two ethernet interfaces */
    315 	process_mac(valbuf, page2, 1, true);
    316 	env_set((char *)"eth1addr", (char *)valbuf);
    317 #endif
    318 #else
    319 /* KMTEGR1 has a special setup. eth0 has no connection to the outside and
    320  * gets an locally administred MAC address, eth1 is the debug interface and
    321  * gets the official MAC address from the IVM
    322  */
    323 	process_mac(valbuf, page2, CONFIG_PIGGY_MAC_ADRESS_OFFSET, false);
    324 	env_set((char *)"ethaddr", (char *)valbuf);
    325 	process_mac(valbuf, page2, CONFIG_PIGGY_MAC_ADRESS_OFFSET, true);
    326 	env_set((char *)"eth1addr", (char *)valbuf);
    327 #endif
    328 
    329 	return 0;
    330 }
    331 
    332 int ivm_read_eeprom(unsigned char *buf, int len)
    333 {
    334 	int ret;
    335 
    336 	i2c_set_bus_num(CONFIG_KM_IVM_BUS);
    337 	/* add deblocking here */
    338 	i2c_make_abort();
    339 
    340 	ret = i2c_read(CONFIG_SYS_IVM_EEPROM_ADR, 0, 1, buf, len);
    341 	if (ret != 0) {
    342 		printf("Error reading EEprom\n");
    343 		return -2;
    344 	}
    345 
    346 	return ivm_populate_env(buf, len);
    347 }
    348