| /* |
| * Copyright 2012 Google Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <stdint.h> |
| #ifdef __APPLE__ |
| #include <libkern/OSByteOrder.h> |
| #define bswap_16(x) OSSwapInt16(x) |
| #define bswap_32(x) OSSwapInt32(x) |
| #include <architecture/byte_order.h> |
| #if BYTE_ORDER == LITTLE_ENDIAN |
| #define le32toh(x) (x) |
| #define le16toh(x) (x) |
| #define htobe16(x) bswap_16(x) |
| #else |
| #define le32toh(x) bswap_32(x) |
| #define le16toh(x) bswap_16(x) |
| #define htobe16(x) (x) |
| #endif |
| #else |
| #include <endian.h> |
| #endif |
| |
| #include "em100.h" |
| |
| #define DEDIPROG_CFG_PRO_MAX_ENTRIES 212 |
| |
| #define DEDIPROG_CFG_PRO_SIZE 176 |
| #define DEDIPROG_CFG_PRO_SIZE_SFDP 256 |
| #define DEDIPROG_CFG_PRO_SIZE_SRST 144 |
| |
| #define DEDIPROG_CFG_MAGIC 0x67666344 /* 'Dcfg' */ |
| #define DEDIPROG_SFDP_MAGIC 0x50444653 /* 'SFDP' */ |
| #define DEDIPROG_SRST_MAGIC 0x54535253 /* 'SRST' */ |
| #define DEDIPROG_PROT_MAGIC 0x544f5250 /* 'PROT' */ |
| |
| #define INIT_SEQUENCE_REIGSTER_OFFSET_0 0x2300 |
| #define INIT_SEQUENCE_REIGSTER_OFFSET_1 0x1100 |
| |
| /* Init sequence and file format: |
| * |
| * All v1.1 dediprog configuration files are 176 bytes and all values are |
| * encoded as little endian format. |
| |
| * At offset init_offset the init sequence consists of init entries that are |
| * sent to endpoint 1. There are 2 sets of entries separated by a 32-bit |
| * terminator of 0xffffffff. Each entry consists of a value and register offset. |
| * The first set of entries have a base register address of 0x2300 while the |
| * the second set of entries have a base register address of 0x1100. Each entry |
| * is sent to the device as <register> <value>, however the data is sent in |
| * big endian format. Additionally, each entry is sent as a 16-byte transfer |
| * with the remaining bytes all 0's. |
| * |
| * Configuration files that are >= 436 bytes contain SFDP data, separated by |
| * the magic value 'SFDP' while those that are 584 bytes contain SRST data |
| * separated by the magic value 'SRST' and containing 0 or 3 entries followed |
| * by PROT data in the rest of the buffer. Unfortunately there does not seem |
| * to be any change in the version fields and these are still reported as 1.1. |
| */ |
| |
| |
| struct dediprog_cfg_hdr { |
| uint32_t magic; |
| uint16_t ver_min; |
| uint16_t ver_maj; |
| uint32_t init_offset; |
| uint32_t chip_size; |
| uint32_t vendor_name_offset; |
| uint32_t chip_name_offset; |
| uint32_t unknown_offset[2]; |
| } __attribute__((packed)); |
| |
| struct dediprog_cfg_pro { |
| struct dediprog_cfg_hdr hdr; |
| uint8_t payload[DEDIPROG_CFG_PRO_SIZE-sizeof(struct dediprog_cfg_hdr)]; |
| } __attribute__((packed)); |
| |
| |
| struct dediprog_cfg_init_entry { |
| uint16_t value; |
| uint16_t reg; |
| } __attribute__((packed)); |
| |
| unsigned char cfg_buffer[DEDIPROG_CFG_PRO_SIZE]; |
| |
| static int parse_and_output_config(struct dediprog_cfg_pro *cfg, chipdesc *chip) |
| { |
| struct dediprog_cfg_init_entry *entry, *end; |
| struct dediprog_cfg_hdr *hdr; |
| const char *vendor, *chip_name; |
| uint16_t reg_offset; |
| int entries = 0; |
| |
| hdr = &cfg->hdr; |
| |
| /* The magic number is actually string, but it can be converted to |
| * a host ordered 32-bit number. */ |
| hdr->magic= le32toh(hdr->magic); |
| |
| if (hdr->magic != DEDIPROG_CFG_MAGIC) { |
| fprintf(stderr, "Invalid magic number: 0x%x\n", hdr->magic); |
| return -1; |
| } |
| |
| /* Convert all header values from little endian to host byte order. */ |
| hdr->ver_min = le16toh(hdr->ver_min); |
| hdr->ver_maj = le16toh(hdr->ver_maj); |
| hdr->init_offset = le32toh(hdr->init_offset); |
| hdr->chip_size = le32toh(hdr->chip_size); |
| hdr->vendor_name_offset = le32toh(hdr->vendor_name_offset); |
| hdr->chip_name_offset = le32toh(hdr->chip_name_offset); |
| |
| if (hdr->ver_maj != 1 && hdr->ver_min != 1) { |
| fprintf(stderr, "Invalid version number: %d.%d\n", hdr->ver_maj, |
| hdr->ver_min); |
| return -1; |
| } |
| |
| /* Adjust the offsets to be into the payload of the config file. */ |
| hdr->init_offset -= sizeof(*hdr); |
| hdr->vendor_name_offset -= sizeof(*hdr); |
| hdr->chip_name_offset -= sizeof(*hdr); |
| |
| vendor = (void *)&cfg->payload[hdr->vendor_name_offset]; |
| chip_name = (void *)&cfg->payload[hdr->chip_name_offset]; |
| |
| /* Since configs.tar is resident, we don't have to malloc */ |
| chip->vendor = (const char *)vendor; |
| chip->name = (const char *)chip_name; |
| chip->size = hdr->chip_size; |
| |
| #ifdef DEBUG |
| printf("%s %s (%d kB)\n", |
| vendor, chip_name, hdr->chip_size/1024); |
| #endif |
| |
| entry = (void *)&cfg->payload[hdr->init_offset]; |
| end = (void *)&cfg[1]; /* 1 past the last entry */ |
| |
| reg_offset = INIT_SEQUENCE_REIGSTER_OFFSET_0; |
| |
| for (; entry != end; entry++) { |
| uint8_t *reg, *value; |
| |
| /* Convert from little endian to host format. */ |
| entry->value = le16toh(entry->value); |
| entry->reg = le16toh(entry->reg); |
| |
| if (entry->value == 0xffff && entry->reg == 0xffff) { |
| reg_offset = INIT_SEQUENCE_REIGSTER_OFFSET_1; |
| continue; |
| } |
| |
| entry->reg += reg_offset; |
| |
| /* Convert from host to big endian format. */ |
| entry->value = htobe16(entry->value); |
| entry->reg = htobe16(entry->reg); |
| |
| value = (void *)&entry->value; |
| reg = (void *)&entry->reg; |
| |
| chip->init[entries][0] = reg[0]; |
| chip->init[entries][1] = reg[1]; |
| chip->init[entries][2] = value[0]; |
| chip->init[entries][3] = value[1]; |
| ++entries; |
| } |
| |
| return entries; |
| } |
| |
| static int parse_and_output_sfdp(chipdesc *chip, void **ptr, size_t *length, int entries) |
| { |
| int i, len = 0; |
| unsigned char *sfdp_buffer = (unsigned char *)*ptr; |
| |
| if (*length < DEDIPROG_CFG_PRO_SIZE_SFDP) { |
| fprintf(stderr, "Error reading SFDP\n"); |
| return -1; |
| } |
| |
| chip->init[entries][0] = 0x23; |
| chip->init[entries][1] = 0xc9; |
| chip->init[entries][2] = 0x00; |
| chip->init[entries][3] = 0x01; |
| |
| len++; |
| |
| for (i = 0; i < DEDIPROG_CFG_PRO_SIZE_SFDP; i+=2) { |
| chip->init[entries+len][0] = 0x23; |
| chip->init[entries+len][1] = 0xc1; |
| chip->init[entries+len][2] = sfdp_buffer[i+1]; |
| chip->init[entries+len][3] = sfdp_buffer[i]; |
| len++; |
| } |
| |
| *ptr += DEDIPROG_CFG_PRO_SIZE_SFDP; |
| *length -= DEDIPROG_CFG_PRO_SIZE_SFDP; |
| |
| return len; |
| } |
| |
| static int parse_and_output_srst(chipdesc *chip, void **ptr, size_t *length, int entries) |
| { |
| int i, len = 0; |
| uint32_t magic; |
| unsigned char *srst_buffer = (unsigned char *)*ptr; |
| |
| if (*length < DEDIPROG_CFG_PRO_SIZE_SRST) { |
| fprintf(stderr, "Error reading SRST\n"); |
| return -1; |
| } |
| |
| /* SRST has 0 or 3 entries before PROT */ |
| memcpy(&magic, &srst_buffer[0], sizeof(magic)); |
| |
| if (magic != DEDIPROG_PROT_MAGIC) { |
| int j; |
| |
| for (j = 0; j < 3; j++) { |
| chip->init[entries+len][0] = 0x23; |
| chip->init[entries+len][1] = srst_buffer[j*4+2]; |
| chip->init[entries+len][2] = srst_buffer[j*4+1]; |
| chip->init[entries+len][3] = srst_buffer[j*4]; |
| len++; |
| } |
| |
| /* Start after SFDP data and PROT magic */ |
| i = 16; |
| } else { |
| /* Start after PROT magic */ |
| i = 4; |
| } |
| |
| chip->init[entries+len][0] = 0x23; |
| chip->init[entries+len][1] = 0xc4; |
| chip->init[entries+len][2] = 0x00; |
| chip->init[entries+len][3] = 0x01; |
| len++; |
| |
| for (; i < DEDIPROG_CFG_PRO_SIZE_SRST; i+=2) { |
| chip->init[entries+len][0] = 0x23; |
| chip->init[entries+len][1] = 0xc5; |
| chip->init[entries+len][2] = srst_buffer[i+1]; |
| chip->init[entries+len][3] = srst_buffer[i]; |
| len++; |
| } |
| |
| *ptr += DEDIPROG_CFG_PRO_SIZE_SRST; |
| *length -= DEDIPROG_CFG_PRO_SIZE_SRST; |
| |
| return len; |
| } |
| |
| int parse_dcfg(chipdesc *chip, TFILE *dcfg) |
| { |
| struct dediprog_cfg_pro *cfg; |
| int init_len = 0; |
| uint32_t magic; |
| |
| void *ptr = (void *)dcfg->address; |
| size_t length = dcfg->length; |
| |
| if (length < sizeof(cfg_buffer)) { |
| /* Not a config file */ |
| return 1; |
| } |
| cfg = (struct dediprog_cfg_pro *)ptr; |
| |
| init_len = parse_and_output_config(cfg, chip); |
| if (init_len < 0) { |
| fprintf(stderr, "Error parsing Dcfg\n"); |
| return 1; |
| } |
| |
| ptr += DEDIPROG_CFG_PRO_SIZE; |
| length -= DEDIPROG_CFG_PRO_SIZE; |
| |
| /* Handle any extra data */ |
| while (length) { |
| int ret = 0; |
| |
| magic = *(uint32_t *)ptr; |
| ptr+=sizeof(uint32_t); |
| length-=sizeof(uint32_t); |
| |
| switch (magic) { |
| case DEDIPROG_SFDP_MAGIC: |
| ret = parse_and_output_sfdp(chip, &ptr, &length, init_len); |
| break; |
| case DEDIPROG_SRST_MAGIC: |
| ret = parse_and_output_srst(chip, &ptr, &length, init_len); |
| break; |
| default: |
| fprintf(stderr, "Unknown magic: 0x%08x\n", magic); |
| break; |
| } |
| |
| if (ret < 0) { |
| fprintf(stderr, "FAILED.\n"); |
| return 1; |
| } |
| init_len += ret; |
| } |
| |
| chip->init_len = init_len; |
| |
| return 0; |
| } |