1#!/usr/bin/env python3 2# 3# Copyright (c) 2019 Intel Corporation 4# 5# SPDX-License-Identifier: Apache-2.0 6import os 7import fcntl 8import struct 9import mmap 10import logging 11 12from ctypes import cast, POINTER, c_uint8, c_uint32, c_uint16, c_uint64,\ 13 addressof, byref 14 15# diag_driver file 16DIAG_DRV_PATH = "/dev/hda" 17 18# Diag Driver Definition - sof-diagnostic-driver/ioctl.h 19 20# IOCTL definition 21CMD_OPEN_DEVICE = 0x47 22CMD_ALLOC_MEMORY = 0x3A 23CMD_FREE_MEMORY = 0x3B 24 25CMD_OPEN_DEVICE_LEN = 40 26 27 28class HdaBar: 29 """ Data structure for HDA BAR information """ 30 31 def __init__(self, raw): 32 self.base_p = 0 33 self.base_v = 0 34 self.size = 0 35 (self.base_p, self.base_v, self.size) = struct.unpack('=QQL', raw) 36 37 def __str__(self): 38 return " Base Physical Address: 0x%08X\n" \ 39 " Base Virtual Address: 0x%08X\n" \ 40 " Base Size: 0x%08X" \ 41 % (self.base_p, self.base_v, self.size) 42 43 44class HdaMemory: 45 """ Data structure for HDA memory allocation """ 46 47 def __init__(self, size=0): 48 self.dma_addr_p = 0 49 self.dma_addr_v = 0 50 self.size = size 51 self.memmap = None 52 53 def set_value(self, raw): 54 (self.dma_addr_p, 55 self.dma_addr_v, 56 self.size) = struct.unpack('=QQL', raw) 57 58 def get_value(self): 59 data = bytearray(struct.pack('=QQL', self.dma_addr_p, 60 self.dma_addr_v, 61 self.size)) 62 return data 63 64 def __str__(self): 65 return " DMA Physical Address: 0x%08X\n" \ 66 " DMA Virtual Address: 0x%08X\n" \ 67 " DMA Size: 0x%08X" \ 68 % (self.dma_addr_p, self.dma_addr_v, self.size) 69 70 71class HdaHandle: 72 """ Data structure for HDA handles """ 73 74 def __init__(self, raw): 75 76 data = struct.unpack('20s20s', raw) 77 self.hda_bar = HdaBar(data[0]) 78 self.dsp_bar = HdaBar(data[1]) 79 80 def __str__(self): 81 output = ( 82 "HDA BAR:\n" 83 "{hda}\n" 84 "DSP BAR:\n" 85 "{dsp}" 86 ).format( 87 hda = self.hda_bar, dsp = self.dsp_bar 88 ) 89 return output 90 91 92class DiagDriver: 93 """ Interface for diag_driver """ 94 95 def __init__(self): 96 self._handle = None 97 self._mem_map_list = [] 98 self._buff_list = [] 99 100 def open_device(self): 101 """ 102 Send CMD_OPEN_DEVICE and get HDA BAR and DSP BAR 103 104 Returns: 105 (handle)(obj:HdaHandle): HDA and DSP Bars objs 106 """ 107 108 logging.debug(">>> DiagDriver.open_device()") 109 110 # Allocate bytearry for HDABusTest_OpenDevice 111 buf = bytearray(CMD_OPEN_DEVICE_LEN) 112 113 logging.info("Open HDA device: %s" % DIAG_DRV_PATH) 114 # Send CMD_OPEN_DEVICE 115 with open(DIAG_DRV_PATH) as fd: 116 fcntl.ioctl(fd, CMD_OPEN_DEVICE, buf) 117 118 self._handle = HdaHandle(buf) 119 120 logging.debug("<<< DiagDriver.open_device()") 121 122 return self._handle 123 124 def alloc_mem(self, size): 125 """ 126 Send CMD_ALLOC_MEMORY to allocate DMA buffer 127 128 Returns: 129 hda_mem (obj:HDAMemory): Allocated DMA buffer information 130 """ 131 132 logging.debug(">>> Diag_Driver.alloc_mem(size=0x%08X)" % size) 133 134 hda_buf = HdaMemory(size) 135 136 # Allocate bytearray for HDABusTestMem 137 buf = hda_buf.get_value() 138 139 # Send CMD_ALLOC_MEMORY 140 with open(DIAG_DRV_PATH) as fd: 141 fcntl.ioctl(fd, CMD_ALLOC_MEMORY, buf) 142 143 hda_buf.set_value(buf) 144 145 mem = self.mmap(hda_buf.dma_addr_p, hda_buf.size) 146 hda_buf.memmap = mem 147 hda_buf.dma_addr_v = addressof(mem) 148 149 logging.debug("DMA Memory:\n%s" % hda_buf) 150 151 # Append to buffer list for later clean up. 152 self._buff_list.append(hda_buf) 153 154 logging.debug("<<< Diag_Driver.alloc_mem()") 155 156 return hda_buf 157 158 def free_mem(self, hda_buf): 159 """ 160 Send CMD_FREE_MEMORY to free the DMA buffer 161 162 Params: 163 had_mem (obj:HDAMemory): DMA buffer information to be freed 164 165 Returns: 166 0 for success, otherwise fail. 167 """ 168 logging.debug(">>> Diag_Driver.free_mem()") 169 170 if hda_buf not in self._buff_list: 171 logging.error("Cannot find buffer from the list") 172 raise ValueError("Cannot find buffer to free") 173 174 logging.debug("Buffer to Free:\n%s" % hda_buf) 175 176 buf = hda_buf.get_value() 177 178 # Send CMD_FREE_MEMORY 179 with open(DIAG_DRV_PATH) as fd: 180 ret = fcntl.ioctl(fd, CMD_FREE_MEMORY, buf) 181 182 self._buff_list.remove(hda_buf) 183 184 logging.debug("<<< Diag_Driver.free_mem()") 185 return ret 186 187 def mmap(self, addr, length): 188 """ 189 Setup mmap for HDA and DSP from /dev/mem 190 191 Returns: 192 (mem map,..)(uint32_t..): Array of uint32_t in mapped memory 193 """ 194 195 logging.debug(">>> Diag_Driver.mmap(addr=0x%08X, length=0x%08X)" 196 % (addr, length)) 197 198 try: 199 fd = os.open(DIAG_DRV_PATH, os.O_RDWR) 200 mem_map = mmap.mmap(fd, length, offset=addr, 201 prot=mmap.PROT_READ | mmap.PROT_WRITE, 202 flags=mmap.MAP_SHARED) 203 204 self._mem_map_list.append(mem_map) 205 206 # Array of uint8 207 mem = (c_uint8 * length).from_buffer(mem_map) 208 finally: 209 os.close(fd) 210 211 logging.debug("<<< Diag_Driver.mmap()") 212 213 return mem 214 215 216class Register: 217 def __init__(self, base_addr, offset, type=c_uint32): 218 self._type = type 219 self._obj = cast(byref(base_addr, offset), POINTER(type)) 220 221 def __str__(self): 222 if self._type == c_uint8: 223 return "0x%02X" % self.value 224 elif self._type == c_uint16: 225 return "0x%04X" % self.value 226 elif self._type == c_uint32: 227 return "0x%08X" % self.value 228 elif self._type == c_uint64: 229 return "0x%08X %08X" % ( 230 self.value >> 32, 231 self.value & 0xFFFFFFFF 232 ) 233 else: 234 return "0x%08X (unknown type)" % self.value 235 236 @property 237 def value(self): 238 return self._obj.contents.value 239 @value.setter 240 def value(self, value): 241 self._obj[0] = value 242