text: #!/usr/bin/env python3
  #
  # Copyright (c) 2018 Intel Corporation
  #
  # SPDX-License-Identifier: Apache-2.0
  import os
  import fcntl
  import struct
  import mmap
  import time
  import logging
  
  from ctypes import cast, POINTER, c_uint8, c_uint32, c_uint16, c_uint64,\
                     addressof, byref, sizeof
  import utilities.registers as regs_def
  
  # diag_driver file
  DIAG_DRV_PATH = "/dev/hda"
  
  # Diag Driver Definition - sof-diagnostic-driver/ioctl.h
  
  # IOCTL definition
  CMD_OPEN_DEVICE  = 0x47
  CMD_ALLOC_MEMORY = 0x3A
  CMD_FREE_MEMORY  = 0x3B
  
  CMD_OPEN_DEVICE_LEN = 40
  
  
  class HdaBar:
      """ Data structure for HDA BAR information """
  
      def __init__(self, raw):
          self.base_p = 0
          self.base_v = 0
          self.size = 0
          (self.base_p, self.base_v, self.size) = struct.unpack('=QQL', raw)
  
      def __str__(self):
          return "   Base Physical Address: 0x%08X\n" \
                 "   Base Virtual Address:  0x%08X\n" \
                 "   Base Size:             0x%08X" \
                     % (self.base_p, self.base_v, self.size)
  
  
  class HdaMemory:
      """ Data structure for HDA memory allocation """
  
      def __init__(self, size=0):
          self.dma_addr_p = 0
          self.dma_addr_v = 0
          self.size = size
          self.memmap = None
  
      def set_value(self, raw):
          (self.dma_addr_p,
           self.dma_addr_v,
           self.size) = struct.unpack('=QQL', raw)
  
      def get_value(self):
          data = bytearray(struct.pack('=QQL', self.dma_addr_p,
                                               self.dma_addr_v,
                                               self.size))
          return data
  
      def __str__(self):
          return "   DMA Physical Address: 0x%08X\n" \
                 "   DMA Virtual  Address: 0x%08X\n" \
                 "   DMA Size:             0x%08X"   \
                 % (self.dma_addr_p, self.dma_addr_v, self.size)
  
  
  class HdaHandle:
      """ Data structure for HDA handles """
  
      def __init__(self, raw):
  
          data = struct.unpack('20s20s', raw)
          self.hda_bar = HdaBar(data[0])
          self.dsp_bar = HdaBar(data[1])
  
      def __str__(self):
          output = (
              "HDA BAR:\n"
              "{hda}\n"
              "DSP BAR:\n"
              "{dsp}"
          ).format(
              hda = self.hda_bar, dsp = self.dsp_bar
          )
          return output
  
  
  class DiagDriver:
      """ Interface for diag_driver """
  
      def __init__(self):
          self._mem_map_list = []
          self._buff_list = []
  
      def open_device(self):
          """
          Send CMD_OPEN_DEVICE and get HDA BAR and DSP BAR
  
          Returns:
              (handle)(obj:HdaHandle): HDA and DSP Bars objs
          """
  
          logging.debug(">>> DiagDriver.open_device()")
  
          # Allocate bytearry for HDABusTest_OpenDevice
          buf = bytearray(CMD_OPEN_DEVICE_LEN)
  
          logging.info("Open HDA device: %s" % DIAG_DRV_PATH)
          # Send CMD_OPEN_DEVICE
          with open(DIAG_DRV_PATH) as fd:
              fcntl.ioctl(fd, CMD_OPEN_DEVICE, buf)
  
          handle = HdaHandle(buf)
  
          logging.debug("<<< DiagDriver.open_device()")
  
          return handle
  
      def alloc_mem(self, size):
          """
          Send CMD_ALLOC_MEMORY to allocate DMA buffer
  
          Returns:
              hda_mem (obj:HDAMemory): Allocated DMA buffer information
          """
  
          logging.debug(">>> Diag_Driver.alloc_mem(size=0x%08X)" % size)
  
          hda_buf = HdaMemory(size)
  
          # Allocate bytearray for HDABusTestMem
          buf = hda_buf.get_value()
  
          # Send CMD_ALLOC_MEMORY
          with open(DIAG_DRV_PATH) as fd:
              fcntl.ioctl(fd, CMD_ALLOC_MEMORY, buf)
  
          hda_buf.set_value(buf)
  
          mem = self.mmap(hda_buf.dma_addr_p, hda_buf.size)
          hda_buf.memmap = mem
          hda_buf.dma_addr_v = addressof(mem)
  
          logging.debug("DMA Memory:\n%s" % hda_buf)
  
          # Append to buffer list for later clean up.
          self._buff_list.append(hda_buf)
  
          logging.debug("<<< Diag_Driver.alloc_mem()")
  
          return hda_buf
  
      def free_mem(self, hda_buf):
          """
          Send CMD_FREE_MEMORY to free the DMA buffer
  
          Params:
              had_mem (obj:HDAMemory): DMA buffer information to be freed
  
          Returns:
              0 for success, otherwise fail.
          """
          logging.debug(">>> Diag_Driver.free_mem()")
  
          if hda_buf not in self._buff_list:
              logging.error("Cannot find buffer from the list")
              raise ValueError("Cannot find buffer to free")
  
          logging.debug("Buffer to Free:\n%s" % hda_buf)
  
          buf = hda_buf.get_value()
  
          # Send CMD_FREE_MEMORY
          with open(DIAG_DRV_PATH) as fd:
              ret = fcntl.ioctl(fd, CMD_FREE_MEMORY, buf)
  
          self._buff_list.remove(hda_buf)
  
          logging.debug("<<< Diag_Driver.free_mem()")
          return ret
  
      def mmap(self, addr, length):
          """
          Setup mmap for HDA and DSP from /dev/mem
  
          Returns:
              (mem map,..)(uint32_t..): Array of uint32_t in mapped memory
          """
  
          logging.debug(">>> Diag_Driver.mmap(addr=0x%08X, length=0x%08X)"
                         % (addr, length))
  
          try:
              fd = os.open(DIAG_DRV_PATH, os.O_RDWR)
              mem_map = mmap.mmap(fd, length, offset=addr,
                                  prot=mmap.PROT_READ | mmap.PROT_WRITE,
                                  flags=mmap.MAP_SHARED)
  
              self._mem_map_list.append(mem_map)
  
              # Array of uint8
              mem = (c_uint8 * length).from_buffer(mem_map)
          finally:
              os.close(fd)
  
          logging.debug("<<< Diag_Driver.mmap()")
  
          return mem
  
  
  class Register:
      def __init__(self, base_addr, offset, type=c_uint32):
          self._type = type
          self._obj = cast(byref(base_addr, offset), POINTER(type))
  
      def __str__(self):
          if self._type == c_uint8:
              return "0x%02X" % self.value
          elif self._type == c_uint16:
              return "0x%04X" % self.value
          elif self._type == c_uint32:
              return "0x%08X" % self.value
          elif self._type == c_uint64:
              return "0x%08X %08X" % (
                  self.value >> 32,
                  self.value & 0xFFFFFFFF
              )
          else:
              return "0x%08X (unknown type)" % self.value
  
      @property
      def value(self):
          return self._obj.contents.value
      @value.setter
      def value(self, value):
          self._obj[0] = value
  