nexmon – Rev 1

Subversion Repositories:
Rev:
#!/usr/bin/env python
"""
#   Copyright (C) 2010  Michael Buesch <m@bues.ch>
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License version 2
#   as published by the Free Software Foundation.
#
#   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.
"""

import sys
import getopt

def indexToName(index):
        D11UCODE_NAMETAG_START          = 0
        D11LCN0BSINITVALS24             = 1
        D11LCN0INITVALS24               = 2
        D11LCN1BSINITVALS24             = 3
        D11LCN1INITVALS24               = 4
        D11LCN2BSINITVALS24             = 5
        D11LCN2INITVALS24               = 6
        D11N0ABSINITVALS16              = 7
        D11N0BSINITVALS16               = 8
        D11N0INITVALS16                 = 9
        D11UCODE_OVERSIGHT16_MIMO       = 10
        D11UCODE_OVERSIGHT16_MIMOSZ     = 11
        D11UCODE_OVERSIGHT24_LCN        = 12
        D11UCODE_OVERSIGHT24_LCNSZ      = 13
        D11UCODE_OVERSIGHT_BOMMAJOR     = 14
        D11UCODE_OVERSIGHT_BOMMINOR     = 15

        namemap = {
                D11UCODE_NAMETAG_START          : "start",
                D11LCN0BSINITVALS24             : "LCN0 bs initvals 24",
                D11LCN0INITVALS24               : "LCN0 initvals 24",
                D11LCN1BSINITVALS24             : "LCN1 bs initvals 24",
                D11LCN1INITVALS24               : "LCN1 initvals 24",
                D11LCN2BSINITVALS24             : "LCN2 bs initvals 24",
                D11LCN2INITVALS24               : "LCN2 initvals 24",
                D11N0ABSINITVALS16              : "N0A bs initvals 16",
                D11N0BSINITVALS16               : "N0 bs initvals 16",
                D11N0INITVALS16                 : "N0 initvals 16",
                D11UCODE_OVERSIGHT16_MIMO       : "microcode 16 MIMO",
                D11UCODE_OVERSIGHT16_MIMOSZ     : "microcode 16 MIMO size",
                D11UCODE_OVERSIGHT24_LCN        : "microcode 24 LCN",
                D11UCODE_OVERSIGHT24_LCNSZ      : "microcode 24 LCN size",
                D11UCODE_OVERSIGHT_BOMMAJOR     : "bom major",
                D11UCODE_OVERSIGHT_BOMMINOR     : "bom minor",
        }
        try:
                return namemap[index]
        except KeyError:
                return "Unknown"

def parseHeader(hdr_data, sortByOffset):
        sections = []
        for i in range(0, len(hdr_data), 3 * 4):
                offset = ord(hdr_data[i + 0]) | (ord(hdr_data[i + 1]) << 8) |\
                        (ord(hdr_data[i + 2]) << 16) | (ord(hdr_data[i + 3]) << 24)
                length = ord(hdr_data[i + 4]) | (ord(hdr_data[i + 5]) << 8) |\
                        (ord(hdr_data[i + 6]) << 16) | (ord(hdr_data[i + 7]) << 24)
                index = ord(hdr_data[i + 8]) | (ord(hdr_data[i + 9]) << 8) |\
                        (ord(hdr_data[i + 10]) << 16) | (ord(hdr_data[i + 11]) << 24)

                sections.append( (offset, length, index) )
        if sortByOffset:
                sections.sort(key = lambda x: x[0]) # Sort by offset
        else:
                sections.sort(key = lambda x: x[2]) # Sort by index
        return sections

def generateHeaderData(sections):
        data = []
        for section in sections:
                (offset, length, index) = section
                data.append(chr(offset & 0xFF))
                data.append(chr((offset >> 8) & 0xFF))
                data.append(chr((offset >> 16) & 0xFF))
                data.append(chr((offset >> 24) & 0xFF))
                data.append(chr(length & 0xFF))
                data.append(chr((length >> 8) & 0xFF))
                data.append(chr((length >> 16) & 0xFF))
                data.append(chr((length >> 24) & 0xFF))
                data.append(chr(index & 0xFF))
                data.append(chr((index >> 8) & 0xFF))
                data.append(chr((index >> 16) & 0xFF))
                data.append(chr((index >> 24) & 0xFF))
        return "".join(data)

def getSectionByIndex(sections, searchIndex):
        for section in sections:
                (offset, length, index) = section
                if searchIndex == index:
                        return section
        return None

def parseHeaderFile(hdr_filepath, sortByOffset=False):
        try:
                hdr_data = file(hdr_filepath, "rb").read()
        except (IOError), e:
                print "Failed to read header file: %s" % e.strerror
                return None
        if len(hdr_data) % (3 * 4) != 0:
                print "Invalid header file format"
                return None
        return parseHeader(hdr_data, sortByOffset)

def dumpInfo(hdr_filepath):
        sections = parseHeaderFile(hdr_filepath)
        if not sections:
                return 1
        for section in sections:
                (offset, length, index) = section
                print "Index %2d   %24s    ==>  offset:0x%08X  length:0x%08X" %\
                        (index, indexToName(index), offset, length)
        return 0

def extractSection(hdr_filepath, bin_filepath, extractIndex, outfilePath):
        sections = parseHeaderFile(hdr_filepath)
        if not sections:
                return 1
        section = getSectionByIndex(sections, extractIndex)
        if not section:
                print "Did not find a section with index %d" % extractIndex
                return 1
        (offset, length, index) = section
        try:
                bin_data = file(bin_filepath, "rb").read()
        except (IOError), e:
                print "Failed to read bin file: %s" % e.strerror
                return 1
        try:
                outfile = file(outfilePath, "wb")
                outfile.write(bin_data[offset : offset + length])
        except IndexError:
                print "Binfile index error."
                return 1
        except (IOError), e:
                print "Failed to write output file: %s" % e.strerror
                return 1
        return 0

def mergeSection(hdr_filepath, bin_filepath, mergeIndex, mergefilePath):
        sections = parseHeaderFile(hdr_filepath, sortByOffset=True)
        if not sections:
                return 1
        try:
                bin_data = file(bin_filepath, "rb").read()
        except (IOError), e:
                print "Failed to read bin file: %s" % e.strerror
                return 1
        try:
                merge_data = file(mergefilePath, "rb").read()
        except (IOError), e:
                print "Failed to open merge output file: %s" % e.strerror
                return 1
        newBin = []
        newSections = []
        newOffset = 0
        foundIt = False
        for section in sections:
                (offset, length, index) = section
                if index == mergeIndex:
                        if foundIt:
                                print "Confused. Multiple sections with index %d?" % index
                                return 1
                        foundIt = True
                        # We overwrite this section
                        newBin.append(merge_data)
                        newSections.append( (newOffset, len(merge_data), index) )
                        newOffset += len(merge_data)
                else:
                        try:
                                newBin.append(bin_data[offset : offset + length])
                        except IndexError:
                                print "Failed to read input data"
                                return 1
                        newSections.append( (newOffset, length, index) )
                        newOffset += length
        if not foundIt:
                print "Did not find section with index %d" % mergeIndex
                return 1
        newBin = "".join(newBin)
        newHdr = generateHeaderData(newSections)
        try:
                file(bin_filepath, "wb").write(newBin)
                file(hdr_filepath, "wb").write(newHdr)
        except (IOError), e:
                print "Failed to write bin or header file: %s" % e.strerror
                return 1
        return 0

def usage():
        print "BRCM80211 firmware converter tool"
        print ""
        print "Usage: %s [OPTIONS]" % sys.argv[0]
        print ""
        print "  -H|--header FILE         Use FILE as header file"
        print "  -B|--bin FILE            Use FILE as bin file"
        print ""
        print "Actions:"
        print "  -d|--dump                Dump general information"
        print "  -x|--extract INDEX:FILE  Extract the section with index INDEX to FILE"
        print "  -m|--merge INDEX:FILE    Merges FILE into the bin file stream at INDEX"
        print "                           A Merge modifies the files specified in -H and -B"
        print ""
        print "  -h|--help                Print this help text"

def main():
        opt_header = None
        opt_bin = None
        opt_action = None
        opt_index = None
        opt_outfile = None
        opt_mergefile = None

        try:
                (opts, args) = getopt.getopt(sys.argv[1:],
                        "hH:B:dx:m:",
                        [ "help", "header=", "bin=", "dump", "extract=", "merge=", ])
        except getopt.GetoptError:
                usage()
                return 1
        for (o, v) in opts:
                if o in ("-h", "--help"):
                        usage()
                        return 0;
                if o in ("-H", "--header"):
                        opt_header = v
                if o in ("-B", "--bin"):
                        opt_bin = v
                if o in ("-d", "--dump"):
                        opt_action = "dump"
                if o in ("-x", "--extract"):
                        opt_action = "extract"
                        try:
                                v = v.split(':')
                                opt_index = int(v[0])
                                opt_outfile = v[1]
                        except IndexError, ValueError:
                                print "Invalid -x|--extract index number\n"
                                usage()
                                return 1
                if o in ("-m", "--merge"):
                        opt_action = "merge"
                        try:
                                v = v.split(':')
                                opt_index = int(v[0])
                                opt_mergefile = v[1]
                        except IndexError, ValueError:
                                print "Invalid -m|--merge index and/or merge file name\n"
                                usage()
                                return 1
        if not opt_action:
                print "No action specified\n"
                usage()
                return 1
        if opt_action == "dump":
                if not opt_header:
                        print "No header file specified\n"
                        usage()
                        return 1
                return dumpInfo(opt_header)
        elif opt_action == "extract":
                if not opt_header or not opt_bin:
                        print "No header or bin file specified\n"
                        usage()
                        return 1
                return extractSection(opt_header, opt_bin, opt_index, opt_outfile)
        elif opt_action == "merge":
                if not opt_header or not opt_bin:
                        print "No header or bin file specified\n"
                        usage()
                        return 1
                return mergeSection(opt_header, opt_bin, opt_index, opt_mergefile)
        return 1

if __name__ == "__main__":
        sys.exit(main())