示例#1
0
 def detect_dsp(debug=False):
     SpiHandler.write(0xf890, [0], debug)
     time.sleep(1)
     SpiHandler.write(0xf890, [1], debug)
     time.sleep(1)
     reg1 = int.from_bytes(SpiHandler.read(
         0xf000, 2), byteorder='big')  # PLL feedback divider must be != 0
     reg2 = int.from_bytes(SpiHandler.read(
         0xf890, 2), byteorder='big')  # Soft reset is expected to be 1
     logging.debug("register read returned %s %s", reg1, reg2)
     if (reg1 != 0) and (reg2 == 1):
         return True
     else:
         return False
示例#2
0
class SigmaTCPHandler(BaseRequestHandler):

    checksum = None
    spi = SpiHandler()
    dsp = adau145x.Adau145x
    dspprogramfile = dspprogramfile()
    parameterfile = parameterfile()
    alsasync = None
    lgsoundsync = None
    updating = False
    xml = None
    checksum_error = False

    def __init__(self, request, client_address, server):
        logging.debug("__init__")
        BaseRequestHandler.__init__(self, request, client_address, server)

    def setup(self):
        logging.debug('setup')

    def finish(self):
        logging.debug('finish')

    def handle(self):
        logging.debug('handle')
        finished = False
        data = None
        read_more = False

        while not (finished):
            # Read dara
            try:
                buffer = None
                result = None

                if data is None:
                    data = self.request.recv(65536)
                    if len(data) == 0:
                        finished = True
                        continue

                if read_more:
                    logging.debug("waiting for more data")
                    d2 = self.request.recv(65536)
                    if (len(d2) == 0):
                        time.sleep(0.1)
                    data = data + d2
                    read_more = False

                # Not an expected header?
                if len(data) > 0 and len(data) < 14:
                    read_more = True
                    continue

                logging.debug("received request type %s", data[0])

                if data[0] == COMMAND_READ:
                    command_length = int.from_bytes(data[1:5], byteorder='big')
                    if (command_length > 0) and (len(data) < command_length):
                        read_more = True
                        logging.debug(
                            "Expect %s bytes from header information (read), but have only %s",
                            command_length, len(data))
                        continue

                    result = self.handle_read(data)

                elif data[0] == COMMAND_WRITE:
                    command_length = int.from_bytes(data[3:7], byteorder='big')

                    logging.debug("Len (data, header info): %s %s", len(data),
                                  command_length)

                    if command_length < len(data):
                        buffer = data[command_length:]
                        data = data[0:command_length]

                    if (command_length > 0) and (len(data) < command_length):
                        read_more = True
                        logging.debug(
                            "Expect %s bytes from header information (write), but have only %s",
                            command_length, len(data))
                        continue

                    self.handle_write(data)
                    result = None

                elif data[0] == COMMAND_EEPROM_FILE:
                    filename_length = data[1]
                    filename = "".join(map(chr, data[14:14 + filename_length]))
                    result = self.write_eeprom_file(filename)

                elif data[0] == COMMAND_STORE_DATA:
                    self.save_data_memory()

                elif data[0] == COMMAND_RESTORE_DATA:
                    self.restore_data_memory()

                elif data[0] == COMMAND_CHECKSUM:
                    result = self._response_packet(
                        COMMAND_CHECKSUM_RESPONSE, 0, 16) + \
                        self.program_checksum(cached=False)

                elif data[0] == COMMAND_XML:
                    try:
                        data = self.get_and_check_xml()

                    except IOError as e:
                        logging.debug("IOerror when reading XML file: %s", e)
                        data = None
                    except Exception as e:
                        logging.debug(
                            "Unexpected error when reading XML file: %s", e)
                        logging.exception(e)
                        data = None

                    if data is not None:
                        xml_bytes = data.encode()
                        result = self._response_packet(COMMAND_XML_RESPONSE, 0,
                                                       len(data)) + xml_bytes
                    else:
                        result = self._response_packet(COMMAND_XML_RESPONSE, 0,
                                                       0)

                elif data[0] == COMMAND_PROGMEM:
                    try:
                        data = self.get_program_memory()
                    except IOError:
                        data = []  # empty response

                    # format program memory dump
                    dump = ""
                    for i in range(0, len(data), 4):
                        dump += "{:02X}{:02X}{:02X}{:02X}\n".format(
                            data[i], data[i + 1], data[i + 2], data[i + 3])

                    result = self._response_packet(
                        COMMAND_PROGMEM_RESPONSE, 0, len(dump)) + \
                        dump.encode('ascii')

                elif data[0] == COMMAND_GPIO:
                    logging.error("GPIO command not yet implemented")

                elif data[0] == COMMAND_DATAMEM:
                    try:
                        data = self.get_data_memory()
                    except IOError:
                        data = []  # empty response

                    # format program memory dump
                    dump = ""
                    for i in range(0, len(data), 4):
                        dump += "{:02X}{:02X}{:02X}{:02X}\n".format(
                            data[i], data[i + 1], data[i + 2], data[i + 3])

                    result = self._response_packet(
                        COMMAND_DATAMEM_RESPONSE, 0, len(dump)) + \
                        dump.encode('ascii')

                elif data[0] == COMMAND_GET_META:
                    length = int.from_bytes(data[1:5], byteorder='big')

                    if length < len(data):
                        buffer = data[command_length:]
                        data = data[0:command_length]

                    attribute = data[14:length].decode("utf-8")
                    value = self.get_meta(attribute)
                    logging.debug("metadata request for %s = %s", attribute,
                                  value)

                    if value is None:
                        value = ""

                    value = value.encode('utf-8')

                    result = self._response_packet(COMMAND_META_RESPONSE, 0,
                                                   len(value))
                    result += value

                elif data[0] == COMMAND_WRITE_EEPROM_CONTENT:
                    command_length = int.from_bytes(data[3:7], byteorder='big')

                    logging.debug("Len (data, header info): %s %s", len(data),
                                  command_length)

                    if command_length < len(data):
                        buffer = data[command_length:]
                        data = data[0:command_length]

                    if (command_length > 0) and (len(data) < command_length):
                        read_more = True
                        logging.debug(
                            "Expect %s bytes from header information (write), but have only %s",
                            command_length, len(data))
                        continue

                    result = self.write_eeprom_content(data[14:command_length])

                if (result is not None) and (len(result) > 0):
                    logging.debug("Sending %s bytes answer to client",
                                  len(result))
                    self.request.send(result)

                # Still got data that hasn't been processed?
                if buffer is not None:
                    data = buffer
                else:
                    data = None

            except ConnectionResetError:
                finished = True
            except BrokenPipeError:
                finished = True

    @staticmethod
    def read_xml_profile():
        SigmaTCPHandler.xml = XmlProfile(SigmaTCPHandler.dspprogramfile)
        cs = SigmaTCPHandler.xml.get_meta("checksum")
        logging.debug("checksum from XML: %s", cs)
        SigmaTCPHandler.checksum_xml = None
        if cs is not None:
            SigmaTCPHandler.checksum_xml = bytearray()
            for i in range(0, len(cs), 2):
                octet = int(cs[i:i + 2], 16)
                SigmaTCPHandler.checksum_xml.append(octet)

        checksum_mem = SigmaTCPHandler.program_checksum()
        checksum_xml = SigmaTCPHandler.checksum_xml
        logging.info("checksum memory: %s, xmlfile: %s", checksum_mem,
                     checksum_xml)

        if (checksum_xml is not None) and (checksum_xml != 0):
            if (checksum_xml != checksum_mem):
                logging.error("checksums do not match, aborting")
                SigmaTCPHandler.checksum_error = True
                return
        else:
            logging.info("DSP profile doesn't have a checksum, "
                         "might be different from the program running now")

        SigmaTCPHandler.checksum_error = False

    @staticmethod
    def get_checked_xml():
        if not (SigmaTCPHandler.checksum_error):
            if SigmaTCPHandler.xml is None:
                SigmaTCPHandler.read_xml_profile()

            return SigmaTCPHandler.xml
        else:
            logging.debug("XML checksum error, ignoring XML file")
            return None

    @staticmethod
    def get_and_check_xml():
        return str(SigmaTCPHandler.get_checked_xml())

    @staticmethod
    def get_meta(attribute):
        if attribute == "detected_dsp":
            return this.dsp

        try:
            xml = SigmaTCPHandler.get_checked_xml()
        except:
            return None

        if xml is None:
            return None
        else:
            try:
                return xml.get_meta(attribute)
            except:
                logging.error("can't get attribute %s from XML", attribute)
                return None

    @staticmethod
    def handle_read(data):
        addr = int.from_bytes(data[10:12], byteorder='big')
        length = int.from_bytes(data[6:10], byteorder='big')

        logging.debug("Handle read %s/%s", addr, length)

        spi_response = SigmaTCPHandler.spi.read(addr, length)
        logging.debug("read {} bytes from {}".format(length, addr))

        res = SigmaTCPHandler._response_packet(
            COMMAND_READRESPONSE, addr, len(spi_response)) + spi_response
        return res

    @staticmethod
    def handle_write(data):

        if len(data) < 14:
            logging.error("Got incorrect write request, length < 14 bytes")
            return None

        addr = int.from_bytes(data[12:14], byteorder='big')
        length = int.from_bytes(data[8:12], byteorder='big')
        if (length == 0):
            # Client might not implement length correctly and leave
            # it empty
            length = len(data) - 14

        _safeload = data[1]  # TODO: use this

        if addr == SigmaTCPHandler.dsp.KILLCORE_REGISTER and not (
                SigmaTCPHandler.updating):
            logging.debug(
                "write to KILLCORE seen, guessing something is updating the DSP"
            )
            SigmaTCPHandler.prepare_update()

        logging.debug("writing {} bytes to {}".format(length, addr))
        memdata = data[14:]
        res = SigmaTCPHandler.spi.write(addr, memdata)

        if addr == SigmaTCPHandler.dsp.HIBERNATE_REGISTER and \
                SigmaTCPHandler.updating and memdata == b'\00\00':
            logging.debug("set HIBERNATE to 0 seen, guessing update is done")
            SigmaTCPHandler.finish_update()

        return res

    @staticmethod
    def write_eeprom_content(xmldata):

        logging.info("writing XML file: %s", xmldata)

        try:
            doc = xmltodict.parse(xmldata)

            SigmaTCPHandler.prepare_update()
            for action in doc["ROM"]["page"]["action"]:
                instr = action["@instr"]

                if instr == "writeXbytes":
                    addr = int(action["@addr"])
                    paramname = action["@ParamName"]
                    data = []
                    for d in action["#text"].split(" "):
                        value = int(d, 16)
                        data.append(value)

                    logging.debug("writeXbytes %s %s", addr, len(data))
                    SigmaTCPHandler.spi.write(addr, data)

                    # Sleep after erase operations
                    if ("g_Erase" in paramname):
                        logging.debug(
                            "found erase command, waiting 10 seconds to finish"
                        )
                        time.sleep(10)

                    # Delay after a page write
                    if ("Page_" in paramname):
                        logging.debug(
                            "found page write command, waiting 0.5 seconds to finish"
                        )
                        time.sleep(0.5)

                if instr == "delay":
                    logging.debug("delay")
                    time.sleep(1)

            SigmaTCPHandler.finish_update()

            # Write current DSP profile
            with open(SigmaTCPHandler.dspprogramfile, "w+b") as dspprogram:
                if (isinstance(xmldata, str)):
                    xmldata = xmldata.encode("utf-8")
                dspprogram.write(xmldata)

        except Exception as e:
            logging.error("exception during EEPROM write: %s", e)
            logging.exception(e)
            return b'\00'

        return b'\01'

    @staticmethod
    def write_eeprom_file(filename):
        try:
            with open(filename) as fd:
                data = fd.read()
                return SigmaTCPHandler.write_eeprom_content(data)
        except IOError as e:
            logging.debug("IOError: %s", e)
            return b'\00'

    @staticmethod
    def save_data_memory():
        logging.info("store: getting checksum")
        checksum = SigmaTCPHandler.program_checksum()
        memory = SigmaTCPHandler.get_memory_block(
            SigmaTCPHandler.dsp.DATA_ADDR, SigmaTCPHandler.dsp.DATA_LENGTH)
        logging.info("store: writing memory dump to file")
        SigmaTCPHandler.store_parameters(checksum, memory)

    @staticmethod
    def restore_data_memory():

        logging.info("restore: checking checksum")
        checksum = SigmaTCPHandler.program_checksum(cached=False)
        memory = SigmaTCPHandler.restore_parameters(checksum)

        if memory is None:
            return

        logging.info("restore: writing to memory")

        dsp = SigmaTCPHandler.dsp

        if (len(memory) > dsp.DATA_LENGTH * dsp.WORD_LENGTH):
            logging.error("Got %s bytes to restore, but memory is only %s",
                          len(memory), dsp.DATA_LENGTH * dsp.WORD_LENGTH)

        # Make sure DSP isn't running for this operation
        SigmaTCPHandler._kill_dsp()
        SigmaTCPHandler.spi.write(dsp.DATA_ADDR, memory)
        # Restart the core
        SigmaTCPHandler._start_dsp()

    @staticmethod
    def get_memory_block(addr, length):
        block_size = 2048

        dsp = SigmaTCPHandler.dsp

        logging.debug("reading %s bytes from memory", length * dsp.WORD_LENGTH)

        # Must kill the core to read program memory, but it doesn't
        # hurt doing it also for other memory types :(
        SigmaTCPHandler._kill_dsp()

        memory = bytearray()

        while len(memory) < length * dsp.WORD_LENGTH:
            logging.debug("reading memory code block from addr %s", addr)
            data = SigmaTCPHandler.spi.read(addr, block_size)
            # logging.debug("%s", data)
            memory += data
            addr = addr + int(block_size / dsp.WORD_LENGTH)

        # Restart the core
        SigmaTCPHandler._start_dsp()

        return memory[0:length * dsp.WORD_LENGTH]

    @staticmethod
    def get_program_memory():
        '''
        Calculate a checksum of the program memory of the DSP
        '''
        dsp = SigmaTCPHandler.dsp
        memory = SigmaTCPHandler.get_memory_block(dsp.PROGRAM_ADDR,
                                                  dsp.PROGRAM_LENGTH)

        end_index = memory.find(dsp.PROGRAM_END_SIGNATURE)

        if end_index < 0:
            memsum = 0
            for i in memory:
                memsum = memsum + i

            if (memsum > 0):
                logging.error("couldn't find program end signature," +
                              " using full program memory")
                end_index = dsp.PROGRAM_LENGTH - dsp.WORD_LENGTH
            else:
                logging.error("SPI returned only zeros - communication"
                              "error")
                return None
        else:
            end_index = end_index + len(dsp.PROGRAM_END_SIGNATURE)

        logging.debug("Program lengths = %s words",
                      end_index / dsp.WORD_LENGTH)

        # logging.debug("%s", memory[0:end_index])
        return memory[0:end_index]

    @staticmethod
    def get_data_memory():
        '''
        Calculate a checksum of the program memory of the DSP
        '''
        dsp = SigmaTCPHandler.dsp
        memory = SigmaTCPHandler.get_memory_block(dsp.DATA_ADDR,
                                                  dsp.DATA_LENGTH)
        logging.debug("Data lengths = %s words",
                      dsp.DATA_LENGTH / dsp.WORD_LENGTH)

        # logging.debug("%s", memory[0:end_index])
        return memory[0:dsp.DATA_LENGTH]

    @staticmethod
    def program_checksum(cached=True):
        if cached and SigmaTCPHandler.checksum is not None:
            logging.debug("using cached program checksum, "
                          "might not always be correct")
            return SigmaTCPHandler.checksum

        data = SigmaTCPHandler.get_program_memory()
        m = hashlib.md5()
        try:
            m.update(data)
        except:
            logging.error("Can't calculate checksum from %s", data)
            return None

        logging.debug("length: %s, digest: %s", len(data), m.digest())

        logging.info("caching program memory checksum")
        SigmaTCPHandler.checksum = m.digest()
        return SigmaTCPHandler.checksum

    @staticmethod
    def _list_str(int_list):
        formatted_list = [str(item) for item in int_list]
        return "[" + ','.join(formatted_list) + "]"

    @staticmethod
    def _response_packet(command, addr, data_length):
        packet = bytearray(HEADER_SIZE)
        packet[0] = command
        packet[4] = 14  # header length
        packet[5] = 1  # chip address

        packet[9] = data_length & 0xff
        packet[8] = (data_length >> 8) & 0xff
        packet[7] = (data_length >> 16) & 0xff
        packet[6] = (data_length >> 24) & 0xff

        packet[11] = addr & 0xff
        packet[10] = (addr >> 8) & 0xff

        return packet

    @staticmethod
    def _kill_dsp():
        logging.debug("killing DSP core")
        dsp = SigmaTCPHandler.dsp
        spi = SigmaTCPHandler.spi

        spi.write(dsp.HIBERNATE_REGISTER, int_data(1,
                                                   dsp.REGISTER_WORD_LENGTH))
        time.sleep(0.0001)
        spi.write(dsp.KILLCORE_REGISTER, int_data(0, dsp.REGISTER_WORD_LENGTH))
        time.sleep(0.0001)
        spi.write(dsp.KILLCORE_REGISTER, int_data(1, dsp.REGISTER_WORD_LENGTH))

    @staticmethod
    def _start_dsp():
        logging.debug("starting DSP core")
        dsp = SigmaTCPHandler.dsp
        spi = SigmaTCPHandler.spi

        spi.write(dsp.KILLCORE_REGISTER, int_data(0, dsp.REGISTER_WORD_LENGTH))
        time.sleep(0.0001)
        spi.write(dsp.STARTCORE_REGISTER, int_data(0,
                                                   dsp.REGISTER_WORD_LENGTH))
        time.sleep(0.0001)
        spi.write(dsp.STARTCORE_REGISTER, int_data(1,
                                                   dsp.REGISTER_WORD_LENGTH))
        time.sleep(0.0001)
        spi.write(dsp.HIBERNATE_REGISTER, int_data(0,
                                                   dsp.REGISTER_WORD_LENGTH))

    @staticmethod
    def store_parameters(checksum, memory):
        with open(SigmaTCPHandler.parameterfile, "wb") as datafile:
            datafile.write(checksum)
            datafile.write(memory)

    @staticmethod
    def restore_parameters(checksum):
        with open(SigmaTCPHandler.parameterfile, "rb") as datafile:
            file_checksum = datafile.read(16)
            logging.debug("Checking checksum %s/%s", checksum, file_checksum)
            if checksum != file_checksum:
                logging.error("checksums do not match, aborting")
                return

    @staticmethod
    def prepare_update():
        '''
        Call this method if the DSP program might change soon
        '''
        logging.info("preparing for memory update")
        SigmaTCPHandler.checksum = None
        SigmaTCPHandler.update_alsasync(clear=True)
        SigmaTCPHandler.update_lgsoundsync(clear=True)
        SigmaTCPHandler.updating = True

    @staticmethod
    def finish_update():
        '''
        Call this method after the DSP program has been refreshed
        '''
        logging.info("finished memory update")
        SigmaTCPHandler.xml = None
        ProgramRefresher().start()

    @staticmethod
    def update_alsasync(clear=False):
        if SigmaTCPHandler.alsasync is None:
            return

        if clear:
            SigmaTCPHandler.alsasync.set_volume_register(None)
            return

        volreg = SigmaTCPHandler.get_meta(ATTRIBUTE_VOL_CTL)
        if volreg is None or len(volreg) == 0:
            SigmaTCPHandler.alsasync.set_volume_register(None)

        reg = datatools.parse_int(volreg)
        SigmaTCPHandler.alsasync.set_volume_register(reg)

    @staticmethod
    def update_lgsoundsync(clear=False):
        if SigmaTCPHandler.lgsoundsync is None:
            logging.debug("LG Sound Sync instance is None")
            return

        if clear:
            SigmaTCPHandler.lgsoundsync.set_registers(None, None)
            return

        logging.debug(
            "checking profile for SPDIF state and volume control support")
        volreg = SigmaTCPHandler.get_meta(ATTRIBUTE_VOL_CTL)
        spdifreg = SigmaTCPHandler.get_meta(ATTRIBUTE_SPDIF_ACTIVE)
        if volreg is None or len(volreg) == 0 or \
            spdifreg is None or len(spdifreg) == 0:
            SigmaTCPHandler.lgsoundsync.set_registers(None, None)
            logging.debug("disabled LG Sound Sync")

        logging.info("enabling LG Sound Sync")
        volr = datatools.parse_int(volreg)
        spdifr = datatools.parse_int(spdifreg)
        SigmaTCPHandler.lgsoundsync.set_registers(volr, spdifr)