Stefan Reinauer | db126f4 | 2019-11-18 18:25:45 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2012 Google Inc. |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License as published by |
| 6 | * the Free Software Foundation; version 2 of the License. |
| 7 | * |
| 8 | * This program is distributed in the hope that it will be useful, |
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | * GNU General Public License for more details. |
Stefan Reinauer | db126f4 | 2019-11-18 18:25:45 -0800 | [diff] [blame] | 12 | */ |
| 13 | |
| 14 | #include <unistd.h> |
| 15 | #include <stdlib.h> |
| 16 | #include <stdio.h> |
| 17 | #include <string.h> |
| 18 | #include <sys/types.h> |
| 19 | #include <sys/stat.h> |
| 20 | #include <stdint.h> |
| 21 | #ifdef __APPLE__ |
| 22 | #include <libkern/OSByteOrder.h> |
| 23 | #define bswap_16(x) OSSwapInt16(x) |
| 24 | #define bswap_32(x) OSSwapInt32(x) |
| 25 | #include <architecture/byte_order.h> |
| 26 | #if BYTE_ORDER == LITTLE_ENDIAN |
| 27 | #define le32toh(x) (x) |
| 28 | #define le16toh(x) (x) |
| 29 | #define htobe16(x) bswap_16(x) |
| 30 | #else |
| 31 | #define le32toh(x) bswap_32(x) |
| 32 | #define le16toh(x) bswap_16(x) |
| 33 | #define htobe16(x) (x) |
| 34 | #endif |
| 35 | #else |
| 36 | #include <endian.h> |
| 37 | #endif |
| 38 | |
| 39 | #include "em100.h" |
| 40 | |
| 41 | #define DEDIPROG_CFG_PRO_MAX_ENTRIES 212 |
| 42 | |
| 43 | #define DEDIPROG_CFG_PRO_SIZE 176 |
| 44 | #define DEDIPROG_CFG_PRO_SIZE_SFDP 256 |
| 45 | #define DEDIPROG_CFG_PRO_SIZE_SRST 144 |
| 46 | |
| 47 | #define DEDIPROG_CFG_MAGIC 0x67666344 /* 'Dcfg' */ |
| 48 | #define DEDIPROG_SFDP_MAGIC 0x50444653 /* 'SFDP' */ |
| 49 | #define DEDIPROG_SRST_MAGIC 0x54535253 /* 'SRST' */ |
| 50 | #define DEDIPROG_PROT_MAGIC 0x544f5250 /* 'PROT' */ |
| 51 | |
| 52 | #define INIT_SEQUENCE_REIGSTER_OFFSET_0 0x2300 |
| 53 | #define INIT_SEQUENCE_REIGSTER_OFFSET_1 0x1100 |
| 54 | |
| 55 | /* Init sequence and file format: |
| 56 | * |
| 57 | * All v1.1 dediprog configuration files are 176 bytes and all values are |
| 58 | * encoded as little endian format. |
| 59 | |
| 60 | * At offset init_offset the init sequence consists of init entries that are |
| 61 | * sent to endpoint 1. There are 2 sets of entries separated by a 32-bit |
| 62 | * terminator of 0xffffffff. Each entry consists of a value and register offset. |
| 63 | * The first set of entries have a base register address of 0x2300 while the |
| 64 | * the second set of entries have a base register address of 0x1100. Each entry |
| 65 | * is sent to the device as <register> <value>, however the data is sent in |
| 66 | * big endian format. Additionally, each entry is sent as a 16-byte transfer |
| 67 | * with the remaining bytes all 0's. |
| 68 | * |
| 69 | * Configuration files that are >= 436 bytes contain SFDP data, separated by |
| 70 | * the magic value 'SFDP' while those that are 584 bytes contain SRST data |
| 71 | * separated by the magic value 'SRST' and containing 0 or 3 entries followed |
| 72 | * by PROT data in the rest of the buffer. Unfortunately there does not seem |
| 73 | * to be any change in the version fields and these are still reported as 1.1. |
| 74 | */ |
| 75 | |
| 76 | |
| 77 | struct dediprog_cfg_hdr { |
| 78 | uint32_t magic; |
| 79 | uint16_t ver_min; |
| 80 | uint16_t ver_maj; |
| 81 | uint32_t init_offset; |
| 82 | uint32_t chip_size; |
| 83 | uint32_t vendor_name_offset; |
| 84 | uint32_t chip_name_offset; |
| 85 | uint32_t unknown_offset[2]; |
| 86 | } __attribute__((packed)); |
| 87 | |
| 88 | struct dediprog_cfg_pro { |
| 89 | struct dediprog_cfg_hdr hdr; |
| 90 | uint8_t payload[DEDIPROG_CFG_PRO_SIZE-sizeof(struct dediprog_cfg_hdr)]; |
| 91 | } __attribute__((packed)); |
| 92 | |
| 93 | |
| 94 | struct dediprog_cfg_init_entry { |
| 95 | uint16_t value; |
| 96 | uint16_t reg; |
| 97 | } __attribute__((packed)); |
| 98 | |
| 99 | unsigned char cfg_buffer[DEDIPROG_CFG_PRO_SIZE]; |
| 100 | |
| 101 | static int parse_and_output_config(struct dediprog_cfg_pro *cfg, chipdesc *chip) |
| 102 | { |
| 103 | struct dediprog_cfg_init_entry *entry, *end; |
| 104 | struct dediprog_cfg_hdr *hdr; |
| 105 | const char *vendor, *chip_name; |
| 106 | uint16_t reg_offset; |
| 107 | int entries = 0; |
| 108 | |
| 109 | hdr = &cfg->hdr; |
| 110 | |
| 111 | /* The magic number is actually string, but it can be converted to |
| 112 | * a host ordered 32-bit number. */ |
| 113 | hdr->magic= le32toh(hdr->magic); |
| 114 | |
| 115 | if (hdr->magic != DEDIPROG_CFG_MAGIC) { |
| 116 | fprintf(stderr, "Invalid magic number: 0x%x\n", hdr->magic); |
| 117 | return -1; |
| 118 | } |
| 119 | |
| 120 | /* Convert all header values from little endian to host byte order. */ |
| 121 | hdr->ver_min = le16toh(hdr->ver_min); |
| 122 | hdr->ver_maj = le16toh(hdr->ver_maj); |
| 123 | hdr->init_offset = le32toh(hdr->init_offset); |
| 124 | hdr->chip_size = le32toh(hdr->chip_size); |
| 125 | hdr->vendor_name_offset = le32toh(hdr->vendor_name_offset); |
| 126 | hdr->chip_name_offset = le32toh(hdr->chip_name_offset); |
| 127 | |
| 128 | if (hdr->ver_maj != 1 && hdr->ver_min != 1) { |
| 129 | fprintf(stderr, "Invalid version number: %d.%d\n", hdr->ver_maj, |
| 130 | hdr->ver_min); |
| 131 | return -1; |
| 132 | } |
| 133 | |
| 134 | /* Adjust the offsets to be into the payload of the config file. */ |
| 135 | hdr->init_offset -= sizeof(*hdr); |
| 136 | hdr->vendor_name_offset -= sizeof(*hdr); |
| 137 | hdr->chip_name_offset -= sizeof(*hdr); |
| 138 | |
| 139 | vendor = (void *)&cfg->payload[hdr->vendor_name_offset]; |
| 140 | chip_name = (void *)&cfg->payload[hdr->chip_name_offset]; |
| 141 | |
| 142 | /* Since configs.tar is resident, we don't have to malloc */ |
| 143 | chip->vendor = (const char *)vendor; |
| 144 | chip->name = (const char *)chip_name; |
| 145 | chip->size = hdr->chip_size; |
| 146 | |
| 147 | #ifdef DEBUG |
| 148 | printf("%s %s (%d kB)\n", |
| 149 | vendor, chip_name, hdr->chip_size/1024); |
| 150 | #endif |
| 151 | |
| 152 | entry = (void *)&cfg->payload[hdr->init_offset]; |
| 153 | end = (void *)&cfg[1]; /* 1 past the last entry */ |
| 154 | |
| 155 | reg_offset = INIT_SEQUENCE_REIGSTER_OFFSET_0; |
| 156 | |
| 157 | for (; entry != end; entry++) { |
| 158 | uint8_t *reg, *value; |
| 159 | |
| 160 | /* Convert from little endian to host format. */ |
| 161 | entry->value = le16toh(entry->value); |
| 162 | entry->reg = le16toh(entry->reg); |
| 163 | |
| 164 | if (entry->value == 0xffff && entry->reg == 0xffff) { |
| 165 | reg_offset = INIT_SEQUENCE_REIGSTER_OFFSET_1; |
| 166 | continue; |
| 167 | } |
| 168 | |
| 169 | entry->reg += reg_offset; |
| 170 | |
| 171 | /* Convert from host to big endian format. */ |
| 172 | entry->value = htobe16(entry->value); |
| 173 | entry->reg = htobe16(entry->reg); |
| 174 | |
| 175 | value = (void *)&entry->value; |
| 176 | reg = (void *)&entry->reg; |
| 177 | |
| 178 | chip->init[entries][0] = reg[0]; |
| 179 | chip->init[entries][1] = reg[1]; |
| 180 | chip->init[entries][2] = value[0]; |
| 181 | chip->init[entries][3] = value[1]; |
| 182 | ++entries; |
| 183 | } |
| 184 | |
| 185 | return entries; |
| 186 | } |
| 187 | |
Stefan Reinauer | 7953388 | 2019-11-22 00:57:29 -0800 | [diff] [blame] | 188 | static int parse_and_output_sfdp(chipdesc *chip, void **ptr, size_t *length, int entries) |
Stefan Reinauer | db126f4 | 2019-11-18 18:25:45 -0800 | [diff] [blame] | 189 | { |
| 190 | int i, len = 0; |
| 191 | unsigned char *sfdp_buffer = (unsigned char *)*ptr; |
| 192 | |
| 193 | if (*length < DEDIPROG_CFG_PRO_SIZE_SFDP) { |
| 194 | fprintf(stderr, "Error reading SFDP\n"); |
| 195 | return -1; |
| 196 | } |
| 197 | |
| 198 | chip->init[entries][0] = 0x23; |
| 199 | chip->init[entries][1] = 0xc9; |
| 200 | chip->init[entries][2] = 0x00; |
| 201 | chip->init[entries][3] = 0x01; |
| 202 | |
| 203 | len++; |
| 204 | |
| 205 | for (i = 0; i < DEDIPROG_CFG_PRO_SIZE_SFDP; i+=2) { |
| 206 | chip->init[entries+len][0] = 0x23; |
| 207 | chip->init[entries+len][1] = 0xc1; |
| 208 | chip->init[entries+len][2] = sfdp_buffer[i+1]; |
| 209 | chip->init[entries+len][3] = sfdp_buffer[i]; |
| 210 | len++; |
| 211 | } |
| 212 | |
| 213 | *ptr += DEDIPROG_CFG_PRO_SIZE_SFDP; |
| 214 | *length -= DEDIPROG_CFG_PRO_SIZE_SFDP; |
| 215 | |
| 216 | return len; |
| 217 | } |
| 218 | |
Stefan Reinauer | 7953388 | 2019-11-22 00:57:29 -0800 | [diff] [blame] | 219 | static int parse_and_output_srst(chipdesc *chip, void **ptr, size_t *length, int entries) |
Stefan Reinauer | db126f4 | 2019-11-18 18:25:45 -0800 | [diff] [blame] | 220 | { |
| 221 | int i, len = 0; |
| 222 | uint32_t magic; |
| 223 | unsigned char *srst_buffer = (unsigned char *)*ptr; |
| 224 | |
| 225 | if (*length < DEDIPROG_CFG_PRO_SIZE_SRST) { |
| 226 | fprintf(stderr, "Error reading SRST\n"); |
| 227 | return -1; |
| 228 | } |
| 229 | |
| 230 | /* SRST has 0 or 3 entries before PROT */ |
| 231 | memcpy(&magic, &srst_buffer[0], sizeof(magic)); |
| 232 | |
| 233 | if (magic != DEDIPROG_PROT_MAGIC) { |
| 234 | int j; |
| 235 | |
| 236 | for (j = 0; j < 3; j++) { |
| 237 | chip->init[entries+len][0] = 0x23; |
| 238 | chip->init[entries+len][1] = srst_buffer[j*4+2]; |
| 239 | chip->init[entries+len][2] = srst_buffer[j*4+1]; |
| 240 | chip->init[entries+len][3] = srst_buffer[j*4]; |
| 241 | len++; |
| 242 | } |
| 243 | |
| 244 | /* Start after SFDP data and PROT magic */ |
| 245 | i = 16; |
| 246 | } else { |
| 247 | /* Start after PROT magic */ |
| 248 | i = 4; |
| 249 | } |
| 250 | |
| 251 | chip->init[entries+len][0] = 0x23; |
| 252 | chip->init[entries+len][1] = 0xc4; |
| 253 | chip->init[entries+len][2] = 0x00; |
| 254 | chip->init[entries+len][3] = 0x01; |
| 255 | len++; |
| 256 | |
| 257 | for (; i < DEDIPROG_CFG_PRO_SIZE_SRST; i+=2) { |
| 258 | chip->init[entries+len][0] = 0x23; |
| 259 | chip->init[entries+len][1] = 0xc5; |
| 260 | chip->init[entries+len][2] = srst_buffer[i+1]; |
| 261 | chip->init[entries+len][3] = srst_buffer[i]; |
| 262 | len++; |
| 263 | } |
| 264 | |
| 265 | *ptr += DEDIPROG_CFG_PRO_SIZE_SRST; |
| 266 | *length -= DEDIPROG_CFG_PRO_SIZE_SRST; |
| 267 | |
| 268 | return len; |
| 269 | } |
| 270 | |
| 271 | int parse_dcfg(chipdesc *chip, TFILE *dcfg) |
| 272 | { |
| 273 | struct dediprog_cfg_pro *cfg; |
| 274 | int init_len = 0; |
| 275 | uint32_t magic; |
| 276 | |
| 277 | void *ptr = (void *)dcfg->address; |
Stefan Reinauer | 7953388 | 2019-11-22 00:57:29 -0800 | [diff] [blame] | 278 | size_t length = dcfg->length; |
Stefan Reinauer | db126f4 | 2019-11-18 18:25:45 -0800 | [diff] [blame] | 279 | |
| 280 | if (length < sizeof(cfg_buffer)) { |
| 281 | /* Not a config file */ |
| 282 | return 1; |
| 283 | } |
| 284 | cfg = (struct dediprog_cfg_pro *)ptr; |
| 285 | |
| 286 | init_len = parse_and_output_config(cfg, chip); |
| 287 | if (init_len < 0) { |
| 288 | fprintf(stderr, "Error parsing Dcfg\n"); |
| 289 | return 1; |
| 290 | } |
| 291 | |
| 292 | ptr += DEDIPROG_CFG_PRO_SIZE; |
| 293 | length -= DEDIPROG_CFG_PRO_SIZE; |
| 294 | |
| 295 | /* Handle any extra data */ |
| 296 | while (length) { |
| 297 | int ret = 0; |
| 298 | |
| 299 | magic = *(uint32_t *)ptr; |
| 300 | ptr+=sizeof(uint32_t); |
| 301 | length-=sizeof(uint32_t); |
| 302 | |
| 303 | switch (magic) { |
| 304 | case DEDIPROG_SFDP_MAGIC: |
| 305 | ret = parse_and_output_sfdp(chip, &ptr, &length, init_len); |
| 306 | break; |
| 307 | case DEDIPROG_SRST_MAGIC: |
| 308 | ret = parse_and_output_srst(chip, &ptr, &length, init_len); |
| 309 | break; |
| 310 | default: |
| 311 | fprintf(stderr, "Unknown magic: 0x%08x\n", magic); |
| 312 | break; |
| 313 | } |
| 314 | |
| 315 | if (ret < 0) { |
| 316 | fprintf(stderr, "FAILED.\n"); |
| 317 | return 1; |
| 318 | } |
| 319 | init_len += ret; |
| 320 | } |
| 321 | |
| 322 | chip->init_len = init_len; |
| 323 | |
| 324 | return 0; |
| 325 | } |